Compare commits

..

254 Commits

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

https://developer.mozilla.org/en-US/docs/Web/CSS/font-smooth
2015-11-17 16:15:33 -05:00
Samy Pessé bd24b03548 Add @buren to AUTHORS 2015-11-16 22:04:41 +01:00
Samy Pessé 5cb55a0f37 Merge pull request #1019 from buren/sv-locale
Add Swedish translation
2015-11-16 22:02:49 +01:00
Jacob Burenstam 163242f80f Add Swedish translation 2015-11-16 22:00:16 +01:00
Átila Camurça Alves 266bfcb8bf Update README.md
There is an extra space in the command line example that enable debug mode.
2015-11-16 16:45:33 -03:00
Samy Pessé d82fa02669 Update plugin-search@1.1.0 2015-11-16 13:09:05 +01:00
Samy Pessé 3a5207a0d2 Merge pull request #1016 from GitbookIO/fix/999
Fix #999: detection of structure should be case incensitive
2015-11-16 10:39:29 +01:00
Samy Pessé e38f3bb884 Improve book.findFile to be case incensitive 2015-11-16 10:31:24 +01:00
Samy Pessé 751cdd9b3b Add test for #999 2015-11-16 10:30:10 +01:00
Samy Pessé 8df6bdca89 Update node version for travis and appveyor tests 2015-11-16 10:18:52 +01:00
Samy Pessé f9df7545ad Merge pull request #1014 from GitbookIO/fix/1013
Fix #1013: prioritize structure defined in book.json
2015-11-16 10:16:52 +01:00
Samy Pessé 9b7a90afef Test first extension of structure itself in book.findFile 2015-11-16 10:06:31 +01:00
Samy Pessé 2ae83b3247 Add failing test for #1013 2015-11-16 09:55:45 +01:00
Samy Pessé 5d907f234e Merge pull request #1005 from mjanda/master
Czech translation
2015-11-07 21:56:28 +01:00
Marek Janda 3a005c4f13 Czech translation 2015-11-07 21:51:27 +01:00
Marek Janda 6c2f50470a Czech translation 2015-11-07 21:00:45 +01:00
Samy Pessé d233783942 Fix bottom margin of blockquote 2015-11-05 11:33:21 +01:00
Samy Pessé 3d9e4c80a2 Bump version to 2.5.2 2015-11-03 09:32:35 +01:00
Samy Pessé a7f49015c1 Fix custom styles for ebook 2015-11-03 09:25:51 +01:00
Samy Pessé 68ed2631dc Correctly show space between toolbar icon and text in buttons 2015-11-02 17:16:43 +01:00
Samy Pessé 2814fea67e Trigger "start" before "page.change" 2015-11-02 16:57:08 +01:00
Samy Pessé 3388be9db6 Update app.js 2015-10-27 23:11:55 +01:00
Samy Pessé 9b89319820 Bump version to 2.5.1 2015-10-27 23:11:31 +01:00
Samy Pessé ad34089df5 Merge changelog for 2.5.0 2015-10-27 23:10:29 +01:00
Samy Pessé 7a4c6bc326 Fix calcul of root in gitbook.state when serving index.html as / 2015-10-27 18:44:04 +01:00
Samy Pessé 6ab386ae35 Bump version to 2.5.0 2015-10-27 13:25:52 +01:00
Samy Pessé 0abbfde912 Add chapterTitle to app build 2015-10-27 12:19:54 +01:00
Samy Pessé 0521defb63 Add 'chapterTitle' to gitbook.state api 2015-10-27 12:18:04 +01:00
Samy Pessé ea4d96610f Compatibility for require of plugins 2015-10-24 21:36:17 +02:00
Samy Pessé 4bc56564b4 Comment gitbook.state api 2015-10-22 16:12:21 +02:00
Samy Pessé 013f7f09da Add gitbook.state.filepath
Fix gitbook.state.root
2015-10-22 16:11:05 +02:00
Samy Pessé f082de1f92 Bump version to 2.5.0-beta.7 2015-10-22 13:52:19 +02:00
Samy Pessé ad39dc8c0c Update app.js 2015-10-22 13:50:05 +02:00
Samy Pessé 481a76319f Provide gitbook.state.root to plugins 2015-10-22 13:49:28 +02:00
Samy Pessé 27a43d5aea Merge pull request #986 from GitbookIO/fix/heading_ids
Normalize ID for headings
2015-10-21 18:05:40 +02:00
Samy Pessé df1d5a0680 Correctly generate id if none found 2015-10-21 17:58:06 +02:00
Samy Pessé 722109faf0 Update gitbook-parsers@0.8.7 2015-10-21 16:22:47 +02:00
Samy Pessé d4dca7736a Update gitbook-parsers@0.8.6 2015-10-21 16:15:56 +02:00
Samy Pessé 4e90becd52 Use Arial as default font 2015-10-21 15:54:45 +02:00
Samy Pessé 26890e94eb Bump version to 2.5.0-beta.6 2015-10-20 18:20:44 +02:00
Samy Pessé 55c83f9a59 Use helvetica by default for pdf 2015-10-20 18:09:51 +02:00
Samy Pessé b837d82878 Fix default style for pdf footer and header 2015-10-20 18:02:23 +02:00
Samy Pessé cbdcdb1e0f Fix #974: correctly handle query in image urls 2015-10-20 17:22:01 +02:00
Samy Pessé 4fea85289e Merge pull request #983 from GitbookIO/fix/982
Fix #982: correctly handle templating in imported content
2015-10-20 17:09:15 +02:00
Samy Pessé ba5f9132c9 Fix #982: fix shortcuts for included templates 2015-10-20 17:08:44 +02:00
Samy Pessé 991681daa2 Add failing test for #982 2015-10-20 16:38:48 +02:00
Samy Pessé 2faf53e16b Bump version to 2.5.0-beta.5 2015-10-16 13:48:27 +02:00
Samy Pessé 5430ed99de Update gitbook-parsers@0.8.5 2015-10-16 13:47:09 +02:00
Samy Pessé fef4dfd05e Fix position of sidebar toggle button 2015-10-14 13:35:03 +02:00
Samy Pessé 05943db3b5 Bump version to 2.5.0-beta.4 2015-10-14 13:16:49 +02:00
Samy Pessé 682fabd1ef Fix position of glossary icon relative to sidebar toggler 2015-10-14 13:15:36 +02:00
Samy Pessé ee8ccc6d7e Fix legacy properties book.options.input, ... 2015-10-14 12:31:37 +02:00
Samy Pessé 8ee0bcac2a Bump version to 2.5.0-beta.3 2015-10-14 11:54:58 +02:00
Samy Pessé 0c4ad74788 Fix resolve of plugins for prereleases 2015-10-14 11:54:12 +02:00
Samy Pessé 7d944e4f1f Fix instruction for local developement 2015-10-14 11:48:27 +02:00
Samy Pessé 67b2394c16 Bump version to 2.5.0-beta.2 2015-10-14 11:39:23 +02:00
Samy Pessé 269d08a35f Correctly handle pre-version for plugins checks 2015-10-13 15:50:54 +02:00
Samy Pessé f626e83e48 Bump version to 2.5.0-beta.1 2015-10-13 15:21:20 +02:00
Samy Pessé 8ba18cd587 Merge pull request #979 from GitbookIO/feature/plugins_config
Improvements for plugin configurations
2015-10-13 15:14:26 +02:00
Samy Pessé 54c12fea89 Set default config for all plugins 2015-10-13 15:05:46 +02:00
Samy Pessé afb5ce71ce Update gitbook-plugin-sharing@1.0.1 2015-10-13 14:56:59 +02:00
Samy Pessé 6fef6acf10 Correctly apply defaults config to pluginsConfig 2015-10-13 14:55:39 +02:00
Samy Pessé a27411401b Update gitbook-plugin-fontsettings@1.0.2 2015-10-13 14:48:09 +02:00
Samy Pessé 3718fc319b Fix error in accessing plugins config 2015-10-13 14:47:33 +02:00
Samy Pessé c6c9463f8c Update gitbook-plugin-search@1.0.2 2015-10-13 14:38:54 +02:00
Samy Pessé b410685318 Update gitbook-plugin-search@1.0.1 2015-10-13 14:34:29 +02:00
Samy Pessé b742b29259 Add tests for plugins config validation 2015-10-13 14:31:11 +02:00
Samy Pessé df5d27ab61 Use jsonschema to valid and default plugins config 2015-10-13 13:56:46 +02:00
Samy Pessé 2870155c0b Fix js error 2015-10-12 10:45:13 +02:00
Samy Pessé 9ba1075f48 Add hook "config" to transform configuration before generation 2015-10-12 10:43:14 +02:00
Samy Pessé 4ae5b2e050 Merge pull request #959 from GitbookIO/improvements/ebooks
Improvements for ebooks
2015-10-08 20:58:57 +02:00
Samy Pessé 4369bd8f5e Fix copy of assets and js path in gulpfile 2015-10-06 14:23:10 +02:00
Samy Pessé 321b24a7b9 Don't copy assets for ebook to website bundle 2015-10-06 14:21:02 +02:00
Samy Pessé 22e0c1fb54 Fix css being used for pdf footer and header 2015-10-06 13:35:04 +02:00
Samy Pessé ba65e3c020 Adapt tests 2015-10-06 13:26:32 +02:00
Samy Pessé c19ab952e3 Only add page count on pdf/epub 2015-10-06 13:20:20 +02:00
Samy Pessé ecc29087b5 Fix summary page counter for epub low resolution 2015-10-06 13:10:51 +02:00
Samy Pessé 87e056de64 Change markdown -> markup in less mixins 2015-10-06 13:05:32 +02:00
Samy Pessé 761200529f Adapt glossary and summary for pdf/epub to new markup style 2015-10-06 13:03:52 +02:00
Samy Pessé 770351916e Cleaner markup css 2015-10-06 12:45:25 +02:00
Samy Pessé 4b7d957b47 Use black links in mobi files 2015-10-06 10:38:09 +02:00
Samy Pessé a1c03c18a5 Improve support of glossary page in pdf 2015-10-06 10:00:49 +02:00
Samy Pesse 5d376b054f Handle correctly rtl for toc in ebooks 2015-10-06 09:47:41 +02:00
Samy Pesse 277d13c2bb Center title for table of content 2015-10-06 09:47:25 +02:00
Samy Pesse 4a9389d80a Improve display of table of contents for ebooks 2015-10-06 09:47:15 +02:00
Samy Pesse 33593c13ae Inline css for footer/header templates 2015-10-06 09:47:04 +02:00
Samy Pesse eef82c18c2 Remove bottom border on h1 and h2 2015-10-06 09:45:31 +02:00
Samy Pessé 7363580095 Fix glossary page using a "section" instead of a "div" 2015-10-06 09:45:22 +02:00
Samy Pessé 23a46b0597 Remove margin on first header of a page (ebook)
Avoid breaking in code blocks
2015-10-06 09:45:06 +02:00
Samy Pessé 51c79f294c Add css to footer/header for pdf 2015-10-06 09:44:54 +02:00
Samy Pessé ce1e0e1873 Merge pull request #964 from GitbookIO/features/plugin_api
Better APIs for Plugins
2015-10-06 09:42:49 +02:00
Samy Pessé c2775b3b63 Export jQuery for plugins compatibility 2015-10-06 09:37:01 +02:00
Samy Pessé bcb224d1cb Add more node.js versions to travis-ci 2015-10-05 23:06:46 +02:00
Samy Pessé 8bc5f22866 Add search plugin to dependencies 2015-10-05 17:22:47 +02:00
Samy Pessé c3f2ef02c4 Adapt tests for new default plugins 2015-10-05 17:16:37 +02:00
Samy Pessé 08e2c90d9d Don't fail when can't write to localstorage 2015-10-05 17:11:55 +02:00
Samy Pessé d2bb2ba454 Remove style for search 2015-10-05 17:07:25 +02:00
Samy Pessé ba912318cd Tweak some details for browserify compatibility 2015-10-05 16:49:37 +02:00
Samy Pessé 3d1f057373 Switch to gulp/browserify 2015-10-05 16:47:19 +02:00
Samy Pessé 0679807517 Define specific action for toggling summary 2015-10-05 16:02:03 +02:00
Samy Pessé 7f0e4803e2 Start removing search and lunr from core 2015-10-05 15:55:39 +02:00
Samy Pessé fa76029a17 Adapt tests for new default plugins 2015-10-05 14:24:55 +02:00
Samy Pessé 376c68957f Remove sharing 2015-10-05 13:53:19 +02:00
Samy Pessé 222ec8d4b9 Correctly reset buttons when page changed 2015-10-05 12:13:54 +02:00
Samy Pessé 90715c1944 Remove fontsettings from core and add plugin as default 2015-10-05 12:07:19 +02:00
Samy Pessé 801e1b6059 Add api to extend toolbar 2015-10-05 12:07:02 +02:00
Samy Pessé 14bb659b75 Update lodash for client side code 2015-10-05 10:43:21 +02:00
Samy Pessé eab3c50288 Add sharing plugin as default 2015-10-05 10:32:43 +02:00
Samy Pessé 74396e7240 Add method book.formatString 2015-10-05 10:07:04 +02:00
Samy Pessé 6c2539e886 Move Book.init in another file 2015-10-05 09:54:40 +02:00
Samy Pessé aba25d8521 Add eslint instead of jshint 2015-10-05 09:54:28 +02:00
Samy Pessé f0c97756de Bump version to 2.4.3 2015-10-01 14:07:32 +02:00
Samy Pessé f14c57d4c2 Merge pull request #955 from mstade/patch-1
Change nunjucks dependency to v2.1.0
2015-10-01 12:03:21 +02:00
Marcus Stade b6f60cc8ad Change nunjucks dependency to v2.1.0
Looks like mozilla/nunjucks#504 was merged and released in v2.1.0, so this changes `package.json` to no longer depend on a specific commit. Incidentally, this also solves a problem where corporate firewalls blocks npm from pulling in straight from github repos.
2015-10-01 12:01:52 +02:00
Samy Pessé 170516d343 Add ukranian translation 2015-09-28 21:01:28 +02:00
Samy Pessé c931e91bef Fix #952: add configuration for index maximum size 2015-09-28 20:54:45 +02:00
Samy Pessé db9151ae36 Update gitbook-parsers@0.8.3 2015-09-28 20:40:02 +02:00
Samy Pessé f2d86b6d69 Add slack button in readme 2015-09-23 12:19:04 +02:00
Samy Pessé 534cdd091b Add link to slack community channel 2015-09-23 12:16:22 +02:00
Samy Pessé fdf4a48638 Bump version to 2.4.2 2015-09-22 23:30:42 +02:00
Samy Pessé 58a3c5634e Improve installation of plugins to not install default ones 2015-09-22 23:25:15 +02:00
Samy Pessé f0ddb6e89f Merge branch 'fix/inline_code_html' 2015-09-22 22:46:19 +02:00
Samy Pessé 2b408db7cb Update gitbook-plugin-highlight@1.0.3 2015-09-22 22:45:55 +02:00
Samy Pessé 4e4bd23e2b Handle html param from code block output 2015-09-22 22:41:13 +02:00
Samy Pessé 62d0eb606e Add test for inline code block with html tags 2015-09-22 22:36:35 +02:00
Samy Pessé 0e9beeae14 Fix #941: limit size of lunr index 2015-09-22 12:36:18 +02:00
Samy Pessé 5e4dbdee24 Disable auto code highlighting (language should be specified) 2015-09-21 16:19:42 +02:00
Samy Pessé 9435dae6cd Fix #939: don't check gitbook version for sub-books (checked for parent) 2015-09-21 09:41:29 +02:00
Samy Pessé 813210bf47 Throw correct error in configuration 2015-09-20 23:27:39 +02:00
Samy Pessé c372dfc544 Bump version to 2.4.1 2015-09-19 00:32:43 +02:00
Samy Pessé ac77200683 Fix disabling a default plugin 2015-09-19 00:31:32 +02:00
Samy Pessé f5afbc90a4 Add more tests for blocks args/kwargs 2015-09-17 11:21:55 +02:00
Samy Pessé b24760c819 Bump version to 2.4.0 2015-09-15 14:32:11 +02:00
Samy Pessé 6732957e42 Merge pull request #930 from GitbookIO/jshint
Add config for jshint and fix lint errors
2015-09-15 14:21:27 +02:00
Samy Pessé 1fd0006859 Lint all tests 2015-09-15 14:20:32 +02:00
Samy Pessé 5a205346e3 Jshint all files 2015-09-15 13:57:21 +02:00
Samy Pessé e193004659 Fix passing context to block functions
Add test for it
2015-09-15 13:36:02 +02:00
Samy Pessé fe3a9c33d0 Improve code style for book.js and index.js 2015-09-15 13:16:26 +02:00
Samy Pessé d83116b88f Add jshint configuration 2015-09-15 13:16:06 +02:00
Samy Pessé 5f56098ae7 Fix stupid error that passed tests because "context" was global 2015-09-15 12:38:13 +02:00
Samy Pessé 0f935f4372 Merge pull request #806 from aschempp/aria-roles
Aria roles for accessibility
2015-09-15 12:29:12 +02:00
Samy Pessé df5c5a8c01 Merge pull request #919 from gaearon/fix-cmd-click
Don't react when user wants to open a link in a new tab
2015-09-15 12:28:32 +02:00
Samy Pessé 57a0c3bad8 Merge pull request #929 from GitbookIO/feature/improve_resolve
Improve paths resolving for conrefs
2015-09-15 12:27:07 +02:00
Samy Pessé 2a52326a45 Improve conrefs to handle all absolute file paths correctly
Add test for it
2015-09-15 12:22:54 +02:00
Samy Pessé 87e4ee1eeb Handle absolute file correctly for conrefs
Add test for absolute file for conrefs
2015-09-15 11:48:18 +02:00
Samy Pessé 45833eea9a Add test for navigation
Update mocha and should
2015-09-15 11:31:50 +02:00
Samy Pessé baf10e9b15 Improve book.resolve to ensure file is in the book
Adapt tests to plugin-highlight
2015-09-15 11:09:08 +02:00
Samy Pessé 463a947df1 Change text for editor 2015-09-15 09:47:34 +02:00
Samy Pessé cceebb4fd9 Merge branch 'improve/template-blocks' 2015-09-15 09:37:43 +02:00
Samy Pessé afd5465a61 Merge pull request #928 from GitbookIO/feature/highlight_block
Code highlighting extended by plugins
2015-09-14 18:26:26 +02:00
Samy Pesse fe604733de Change name of highlight test plugin 2015-09-14 12:05:51 +02:00
Samy Pesse da22f9f39f Update css without highlight.js
Add highlight as default plugin to be loaded
2015-09-14 11:57:54 +02:00
Samy Pesse 284ebbf75d Add highlight as default plugin 2015-09-14 11:47:56 +02:00
Samy Pesse 2c08ccad87 Pass language as kwargs to code block
Improve tests for extending code highlighting
2015-09-14 11:41:01 +02:00
Samy Pesse fa4e234bab Add base test for extending code highlighting 2015-09-14 11:28:14 +02:00
Samy Pesse 35c4179ca1 Use block code for highlight pre/code
Cleanup correctly predefined blocks
2015-09-14 11:21:16 +02:00
Dan Abramov c14ba7ce34 Don't react when user wants to open a link in a new tab
Fixes #846
2015-09-12 21:31:40 +03:00
Samy Pesse 601ce3add3 Add default blocks (html and code) 2015-09-12 19:25:01 +02:00
Samy Pesse 08ff39fd0b Remove highlight.js dependency 2015-09-12 19:23:07 +02:00
Samy Pesse 82a6ee3875 Add test for replacing highlighting 2015-09-12 18:21:35 +02:00
Samy Pessé 3bf592f870 Bump version to 2.3.3 2015-09-10 11:26:15 +02:00
Samy Pessé 241e205717 Merge pull request #912 from GitbookIO/fix/538
Fix 538: duplicate entries without paths are ignored
2015-09-10 09:59:45 +02:00
Samy Pessé fb2720ea8d Update gitbook-parsers@0.8.2 2015-09-10 09:57:04 +02:00
Samy Pessé 14853befda Add test for init with deep summary 2015-09-10 09:56:18 +02:00
Samy Pessé e7f2803d11 Bump version to 2.3.2 2015-09-09 09:30:53 +02:00
Samy Pessé 320015dd8f Fix #910: fix stupid error with semver api 2015-09-09 09:27:08 +02:00
Samy Pessé 471c124a98 Merge pull request #908 from GitbookIO/fix/asciidoc_blocks
Fix blocks in asciidoc
2015-09-09 00:01:43 +02:00
Samy Pesse aa44a109c6 Use position indicator that doesn't conflict with asciidoc 2015-09-08 23:57:40 +02:00
Samy Pesse 41e5ca8422 Add test for blocks without parsing 2015-09-08 23:55:30 +02:00
Samy Pessé 53af8b2bb6 Bump version to 2.3.1 2015-09-08 16:33:22 +02:00
Samy Pessé bd885b135f Merge pull request #904 from kuaipao/fix-mobi-font-color
use black font on mobi
2015-09-08 09:16:43 +02:00
kuaipao 62a6dde81a use black font on mobi 2015-09-08 04:02:48 +08:00
Samy Pesse 14b72b344f Log modified file in watcher 2015-09-06 21:13:18 +02:00
Samy Pesse ac03668063 Update chokidar@1.0.5 2015-09-06 21:11:49 +02:00
Samy Pesse 2512e176c9 Fix #893: korean iso code is "ko" not "kr" 2015-09-06 21:01:39 +02:00
Samy Pesse b613393866 Update highlight.js@8.8.0 2015-09-06 19:04:09 +02:00
Samy Pessé 607a6e95de Update gitbook-parsers@0.8.1 2015-09-05 19:00:42 +02:00
Samy Pessé 871f574ff7 Fix #761: fix syntax highlighting for asciidoc 2015-09-05 17:28:01 +02:00
Samy Pessé 377e470b15 Add warning for file outside spine 2015-09-04 19:12:27 +02:00
Samy Pessé e0a3f0cea5 Add warning for too strict gitbook version 2015-09-04 18:58:16 +02:00
Samy Pessé 66727b2736 Update gitbook-parsers@0.8.0 2015-09-04 18:33:45 +02:00
Samy Pessé 8215ad977f Merge pull request #898 from deminoth/master
Korean translation fixed
2015-09-04 08:36:19 +02:00
SangYeob Bono Yu a611dffbdf Add my name 2015-09-04 10:29:59 +09:00
SangYeob Bono Yu 39f378d8cf Korean translation fixed 2015-09-04 10:22:24 +09:00
Samy Pessé f00da58153 Update appveyor button 2015-09-01 17:52:27 +02:00
Samy Pessé 09b038b16d Bump version to 2.3.0 2015-09-01 17:44:53 +02:00
Samy Pessé cf601d97ad Improve error handling for failed image download 2015-09-01 17:38:33 +02:00
Samy Pessé 80edad53d4 Update nunjucks to fix raw and loader 2015-09-01 17:25:43 +02:00
Samy Pessé a972b32769 Update nunjucks@2.0.0 2015-08-28 08:45:28 +02:00
Samy Pessé efcaae3e60 Add author for hebrew 2015-08-27 19:07:31 +02:00
Samy Pessé e0ee591638 Merge pull request #877 from a-moses/hebrew
hebrew support theme
2015-08-27 19:06:26 +02:00
Samy Pessé 310a6d909f Add method 'get' to config object (helper for plugins) 2015-08-27 15:26:12 +02:00
Samy Pessé cefd8fc50f Don't fail if hook returns nothing for "page" and "page:before" 2015-08-27 15:04:28 +02:00
Samy Pessé a4422593f2 Add back page:before and page as non-deprecated 2015-08-26 11:16:02 +02:00
Samy Pessé e2c5d22756 Merge pull request #844 from xcv58/master
Fix #843 ignore _book in watch.js
2015-08-26 11:05:43 +02:00
Samy Pessé 84385d9dc3 Merge pull request #881 from mortonfox/patch-1
Fix Plugins link.
2015-08-26 11:01:28 +02:00
Morton Fox 6cfa4c8ab7 Fix Plugins link. 2015-08-25 23:35:15 -04:00
a-moses c76ccc53e1 Update he.json 2015-08-23 09:59:14 +03:00
a-moses d48eafb1a2 hebrew support theme
full Hebrew support for GITBOOK (with rtl )

* Sans and Serif may still in original name fonts
2015-08-23 09:53:25 +03:00
Aaron O'Mullan 302d9b4b91 Merge pull request #849 from GitbookIO/improve/remove-webfonts
Use system fonts instead of webfonts
2015-08-13 19:19:35 +02:00
Samy Pessé a091a7f68b Update gitbook-parsers@0.7.7 2015-08-09 15:45:04 +02:00
Aaron O'Mullan 53621f6de3 Use system fonts instead of webfonts 2015-08-08 17:35:05 +02:00
Samy Pessé 288b965db3 Merge pull request #847 from GitbookIO/fix/flicker
Improve header merge on page change
2015-08-07 16:55:42 +02:00
Aaron O'Mullan 578c9dcfc6 Improve header merge on page change
Prevents flickering (style reset)

Fixes #782, #838, #840
2015-08-07 16:46:23 +02:00
xcv58 64de17b248 ignore _book in watch.js 2015-08-04 00:50:31 -04:00
Aaron O'Mullan d699dff872 Use Book.resolve when possible
For added consistency
2015-07-31 15:08:56 +02:00
Samy Pesse 501843d624 Bump version to 2.2.0 2015-07-28 15:28:38 -07:00
Samy Pesse 508ce4283e Fix #819: codes must always be ltr 2015-07-28 15:25:12 -07:00
Samy Pessé 4aa8e66549 Merge pull request #813 from MrMaksimize/pdf-options
Allowing for custom chapter-mark and page-break in PDF.
2015-07-01 18:00:31 -04:00
Maksim Pecherskiy 917a6f70d8 Allowing for custom chapter-mark and page-break in PDF. 2015-06-30 19:48:51 -07:00
Samy Pesse 2f5e53da55 Update highlight.js@8.6.0 2015-06-29 10:02:35 -04:00
Samy Pesse c3cdefc90a Simpler template for summary 2015-06-29 09:58:23 -04:00
Samy Pesse 9c580e4989 Fix #802: make book.json accessible as "config." in templating 2015-06-19 16:28:46 +02:00
Andreas Schempp dbc75f7352 Add role and html5 markup for the main navigation 2015-06-18 20:29:11 +02:00
Andreas Schempp db841a8578 Added basic ARIA roles for screenreaders 2015-06-18 20:23:12 +02:00
Samy Pessé 50c902c4e4 Merge pull request #805 from GitbookIO/fix/804
Fix #804
2015-06-18 11:16:32 +02:00
Samy Pesse 84b6e0bde6 Add test for kwargs 2015-06-18 11:15:44 +02:00
Samy Pesse abc29c3fba Fix #804: correctly extract kwargs from blocks 2015-06-18 11:11:47 +02:00
Samy Pesse 8356c534f5 Add failing tests for block's arguments 2015-06-18 11:09:02 +02:00
Samy Pessé 78ff2687b4 Merge pull request #797 from nghong/vi-locale
Adding Vietnamese locale support
2015-06-13 11:15:00 +02:00
Hong Nguyen 9b6586fb36 Add translator name to AUTHORS.md 2015-06-13 16:13:34 +07:00
Hong Nguyen ea133219d0 Minor fix 2015-06-13 16:08:21 +07:00
Hong Nguyen c9b5d81a3c Add Vietnamese locale support 2015-06-13 15:42:49 +07:00
184 changed files with 28496 additions and 3792 deletions
+1
View File
@@ -0,0 +1 @@
theme/**/*
+19
View File
@@ -0,0 +1,19 @@
{
"rules": {
"indent": [ 2, 4 ],
"quotes": [ 2, "single" ],
"linebreak-style": [ 2, "unix" ],
"semi": [ 2, "always" ],
"no-unused-vars": [ 2, {
"vars": "all",
"args": "none"
} ],
"spaced-comment": [ 2, "always" ]
},
"env": {
"node": true,
"mocha": true,
"browser": true
},
"extends": "eslint:recommended"
}
-3
View File
@@ -24,8 +24,5 @@ build/Release
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
node_modules
theme/javascript/vendors
theme/vendors
# vim swapfile
*.swp
+4 -3
View File
@@ -1,7 +1,8 @@
sudo: false
language: node_js
node_js:
- "0.11"
- "0.10"
- "stable"
- "4.1"
- "0.12"
before_install:
- npm install svgexport -g
- npm install svgexport -g
+25 -14
View File
@@ -21,32 +21,43 @@ Translators
------------
- German
- Winnie (@winniehell)
- Winnie (@winniehell)
- Italian
- Giulio Bonanome (@gbonanome)
- Giulio Bonanome (@gbonanome)
- Russian
- Andrey Akinshin (@AndreyAkinshin)
- Andrey Akinshin (@AndreyAkinshin)
- Norwegian
- Knut Melvær (@kmelve)
- Knut Melvær (@kmelve)
- Persian (Farsi)
- Mohammad Hossein Mojtahedi (@mhm5000)
- Mohammad Hossein Mojtahedi (@mhm5000)
- Polish
- Łukasz Szeremeta (@lszeremeta)
- Łukasz Szeremeta (@lszeremeta)
- Portuguese
- Ryan O'Mullan (@RyanOM)
- Ryan O'Mullan (@RyanOM)
- Spanish
- Ryan O'Mullan (@RyanOM)
- Ryan O'Mullan (@RyanOM)
- Simplifiled Chinese
- Hu Hao (@howiehu)
- Hu Hao (@howiehu)
- Traditional Chinese
- Hu Hao (@howiehu)
- Hu Hao (@howiehu)
- French
- Samy Pessé (@SamyPesse)
- Samy Pessé (@SamyPesse)
- Romanian
- Iancu Aurel (@awrelll)
- Finnish
- Tommi Savikko (@savikko)
- Tommi Savikko (@savikko)
- Japanese
- Iancu Aurel (@awrelll)
- Iancu Aurel (@awrelll)
- Korean
- Iancu Aurel (@awrelll)
- Iancu Aurel (@awrelll)
- SangYeob Yu (@deminoth)
- Vietnamese
- Hong Nguyen (@nghong)
- Hebrew
- @a-moses
- Ukrainian
- @Karnaukhov
- Czech
- @mjanda
- Swedish
- Jacob Burenstam (@buren)
+111
View File
@@ -2,6 +2,117 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 2.6.6
- Fix custom CSS that are generated by plugins for PDF output
## 2.6.5
- Fix support for plugins generating custom stylesheets (`styles-less` and `styles-sass`)
- Fix glossary terms being replaced in script (ex: math)
## 2.6.4
- Fix regression introduced by `2.6.3` of single HTML tags in markdown
## 2.6.3
- Fix parsing bug with inline HTML in Markdown
## 2.6.2
- Fix `gitbook.state.bookRoot`, `gitbook.state.root` now has a trailing slash
- Update nunjucks to 2.2.0
## 2.6.1
- Use CamelCase for `gitbook.state.innerLanguage`
## 2.6.0
- Close sidebar after clicking a link on mobile
- Add root for multilingual books: `gitbook.state.bookRoot`
- Fix color of h6 headings
- Fix bottom margin of lists and blockquotes
- Add Swedish translation (`sv`)
- Add Czech translation (`cs`)
- Fix locale names for `zh-hans` and `jp`
- Update plugin `search` to fix crashes
- Aceept case-incensitive structure files (README, GLOSSARY, etc)
## 2.5.2
- Fix custom stylesheets for ebook generation
- Trigger event `start` before `page.change`
## 2.5.1
- Fix calcul of `gitbook.state.root` when serving HTML index as `/`
## 2.5.0
- Font settings, sharing and search are externalized as default plugins
- Plugins can define a configuration schema in the manifest, this schema will be used to validate configuration during build
- New Node.js API for plugin: `book.formatString(type, content)`
- New client side API for website plugins: `gitbook.toolbar.createButton(opts)`
- Better header/footer for PDF, CSS wil be inlined to easily style the header/footer
- Cleaner table of contents for ebooks
- Support for RTL in ebook's table of contents
- Better colors for mobi (links and code blocks)
- Fix installation of plugins when using a pre-release
- Fix support for pre-releases
- Update asciidoc parser to remove git dependency
- Fix templating in imported content
- Fix querystring in image urls
- Normalize heading IDs like GitHub
- Use Arial as default font for PDFs
- Add `root`, `chapterTitle` and `filepath` to `gitbook.state` JS API
## 2.4.3
- Add ukrainian translation (`uk`)
- Add `book.json` configuration for maximum size of search index
- Improve reliability of summary parser
## 2.4.2
- Default plugins should not be installed by `gitbook install`
- Limit search index size to avoid crash during generation
- Fix code highlighting for html without language specified
- Fix warning message for gitbook version when building a multilingual book
## 2.4.1
- Fix disabling of default plugins, ex: `-highlight`
## 2.4.0
- Fix page being updated when user wants to open a link in a new tab
- Plugins can now replaced default code highlighter
- Add semantic information for screen readers (web version)
- Content references accept absolute paths, resolved to book folder
- Improve overall reliability
## 2.3.3
- Fix bug in SUMMARY parsing preventing multiple entries without filenames
## 2.3.2
- Fix blocks (like maths) in Asciidoc
- Fix error when checking gitbook version
## 2.3.1
- Fix black font color for ebooks (mobi, pdf and epub)
- Fix ISO code for korean language
- Fix korean translation
- Fix syntax highlighting for asciidoc
- Fix inline html escaping in markdown
- Add warning for file outside SUMMARY
- Force SUMMARY entries to be unique by filename
## 2.3.0
- Fix nunjucks issue with multiple `{% raw %}` blocks
- Fix crash when git conref failed
- Fix crash when failed to download remote image (better error message)
- Fix flicking effect when changing page (big UX improvement)
- Add Hebrew translation (`he`)
- Add utility method `book.config.get` for plugins
- Hooks `page:before` and `page` are no longer deprecated
- Remove webfonts to make website lighter
- Make glossary's order case insensitive
## 2.2.0
- Fix direction in code blocks (always LTR)
- Add options `chapterMark` and `pageBreaksBefore` for PDF
- Update code highlighting library
- `book.json` accessible as `config` in templating syntax
- Add Vietnamese translation (`vi`)
## 2.1.0
- Fix error in calcul of `levels` in table of contents, error introduced a few versions ago
- Add optional `styles/print.css` to replace `print.css` used in ebook
-96
View File
@@ -1,96 +0,0 @@
module.exports = function (grunt) {
var path = require("path");
// Load NPM tasks
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-requirejs');
grunt.loadNpmTasks("grunt-bower-install-simple");
// Init GRUNT configuraton
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
'bower-install-simple': {
options: {
color: true,
production: false,
directory: "theme/vendors"
}
},
less: {
development: {
options: {
compress: true,
yuicompress: true,
optimization: 2
},
files: {
"theme/assets/style.css": "theme/stylesheets/website.less",
"theme/assets/print.css": "theme/stylesheets/ebook.less"
}
}
},
requirejs: {
compile: {
options: {
name: "gitbook",
baseUrl: "theme/javascript/",
out: "theme/assets/app.js",
preserveLicenseComments: false,
optimize: "uglify",
include: ["requireLib"],
paths: {
"jQuery": '../vendors/jquery/dist/jquery',
"lodash": '../vendors/lodash/dist/lodash',
"requireLib": '../vendors/requirejs/require',
"Mousetrap": '../vendors/mousetrap/mousetrap',
"lunr": '../vendors/lunr.js/lunr',
"URIjs": '../vendors/URIjs/src/',
"ace": '../vendors/ace-builds/src-noconflict/'
},
shim: {
'jQuery': {
exports: '$'
},
'lodash': {
exports: '_'
},
'Mousetrap': {
exports: 'Mousetrap'
},
'lunr': {
exports: 'lunr'
}
}
}
}
},
copy: {
vendors: {
files: [
{
expand: true,
cwd: 'theme/vendors/fontawesome/fonts/',
src: ['**'],
dest: 'theme/assets/fonts/fontawesome/',
filter: 'isFile'
}
]
}
}
});
grunt.registerTask("bower-install", [ "bower-install-simple" ]);
// Build
grunt.registerTask('build', [
'bower-install',
'less',
'requirejs',
'copy:vendors'
]);
grunt.registerTask('default', [
'build'
]);
};
+10 -9
View File
@@ -2,14 +2,15 @@ GitBook
=======
[![NPM version](https://badge.fury.io/js/gitbook.svg)](http://badge.fury.io/js/gitbook)
[![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/github/GitbookIO/gitbook?branch=master&svg=true)](https://ci.appveyor.com/project/SamyPesse/gitbook)
[![Linux Build Status](https://travis-ci.org/GitbookIO/gitbook.png?branch=master)](https://travis-ci.org/GitbookIO/gitbook)
[![Windows Build status](https://ci.appveyor.com/api/projects/status/63nlflxcwmb2pue6?svg=true)](https://ci.appveyor.com/project/GitBook/gitbook)
[![Slack Status](https://slack.gitbook.com/badge.svg)](https://slack.gitbook.com)
GitBook is a command line tool (and Node.js library) for building beautiful books using GitHub/Git and Markdown (or AsciiDoc). Here is an example: [Learn Javascript](https://www.gitbook.com/book/GitBookIO/javascript).
You can publish and host book easily online using [gitbook.com](https://www.gitbook.com), a web-editor is [also available](https://www.gitbook.com/editor).
You can publish and host book easily online using [gitbook.com](https://www.gitbook.com), a desktop editor is [also available](https://www.gitbook.com/editor).
Stay updated by following [@GitBookIO](https://twitter.com/GitBookIO) on Twitter or [GitBook](https://www.facebook.com/gitbookcom) on Facebook.
Check out the [GitBook Community Slack Channel](https://slack.gitbook.com), Stay updated by following [@GitBookIO](https://twitter.com/GitBookIO) on Twitter or [GitBook](https://www.facebook.com/gitbookcom) on Facebook.
Complete documentation is available at [help.gitbook.com](http://help.gitbook.com/).
@@ -50,7 +51,7 @@ $ gitbook build
* [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/plugins/index.html)
* [Plugins](http://help.gitbook.com/format/plugins.html)
* Interractive reader website:
* Search
* Font Settings (Serif, Sans Serif)
@@ -212,7 +213,7 @@ Plugins needed to build a book can be installed using: `gitbook install ./`. You
You can use the options `--log=debug` and `--debug` to get better error messages (with stack trace). For example:
```
$ gitbook build ./ -- log=debug --debug
$ gitbook build ./ --log=debug --debug
```
#### How to use the latest commit from GitBook in gitbook-cli
@@ -221,9 +222,9 @@ To use the latest commit from `GitBook/gitbook` with `gitbook-cli`:
```
$ git clone https://github.com/GitbookIO/gitbook.git ./gitbook
$ gitbook versions:link 2.0.0 ./gitbook
$ gitbook versions:link ./gitbook
```
Now the version tag `2.0.0` wil be associated with the `./gitbook` folder.
Now `gitbook-cli` will be using the `./gitbook` folder.
You can uninstall it using: `gitbook versions:uninstall 2.0.0`.
You can uninstall it using: `gitbook versions:uninstall latest`.
+2 -2
View File
@@ -5,8 +5,8 @@ init:
# Test against these versions of Node.js.
environment:
matrix:
- nodejs_version: "0.10"
- nodejs_version: "0.11"
- nodejs_version: "0.12"
- nodejs_version: "4.1"
# Install scripts. (runs after repo cloning)
install:
+3 -3
View File
@@ -2,6 +2,6 @@
var color = require('bash-color');
console.log(color.red("You need to install 'gitbook-cli' to have access to the gitbook command anywhere on your system."));
console.log(color.red("If you've installed this package globally, you need to uninstall it."));
console.log(color.red(">> Run 'npm uninstall -g gitbook' then 'npm install -g gitbook-cli'"));
console.log(color.red('You need to install "gitbook-cli" to have access to the gitbook command anywhere on your system.'));
console.log(color.red('If you\'ve installed this package globally, you need to uninstall it.'));
console.log(color.red('>> Run "npm uninstall -g gitbook" then "npm install -g gitbook-cli"'));
-13
View File
@@ -1,13 +0,0 @@
{
"name": "GitBook",
"version": "1.0.0",
"dependencies": {
"jquery": "2.1.1",
"lodash": "2.4.1",
"requirejs": "2.1.11",
"URIjs": "1.13.1",
"mousetrap": "1.4.6",
"lunr.js": "0.5.2",
"fontawesome": "4.1.0"
}
}
+47
View File
@@ -0,0 +1,47 @@
var _ = require('lodash');
var gulp = require('gulp');
var gutil = require('gulp-util');
var less = require('gulp-less');
var rename = require('gulp-rename');
var minifyCSS = require('gulp-minify-css');
var browserify = require('browserify');
var mergeStream = require('merge-stream');
var source = require('vinyl-source-stream');
gulp.task('css', function() {
var merged = mergeStream();
_.each({
'ebook.less': 'ebook/ebook.css',
'pdf.less': 'ebook/pdf.css',
'mobi.less': 'ebook/mobi.css',
'epub.less': 'ebook/epub.css',
'website.less': 'website/style.css'
}, function(out, input) {
gutil.log('compiling', input, 'into', out);
merged.add(gulp.src('theme/stylesheets/'+input)
.pipe(less())
.pipe(minifyCSS())
.pipe(rename(out))
.pipe(gulp.dest('theme/assets/')));
});
return merged;
});
gulp.task('js', function() {
return browserify('./theme/javascript/index.js')
.bundle()
.pipe(source('app.js'))
.pipe(gulp.dest('./theme/assets/website'));
});
gulp.task('assets', function() {
return gulp.src('./node_modules/font-awesome/fonts/*')
.pipe(gulp.dest('theme/assets/website/fonts/fontawesome/'));
});
gulp.task('default', ['css', 'js', 'assets'], function() {
});
+11
View File
@@ -0,0 +1,11 @@
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 extent by plugins
code: _.identity
};
+171 -213
View File
@@ -1,30 +1,24 @@
var Q = require("q");
var _ = require("lodash");
var path = require("path");
var util = require("util");
var lunr = require("lunr");
var parsers = require("gitbook-parsers");
var color = require("bash-color");
var Q = require('q');
var _ = require('lodash');
var path = require('path');
var parsers = require('gitbook-parsers');
var fs = require("./utils/fs");
var parseNavigation = require("./utils/navigation");
var parseProgress = require("./utils/progress");
var pageUtil = require("./utils/page");
var batch = require("./utils/batch");
var links = require("./utils/links");
var i18n = require("./utils/i18n");
var logger = require("./utils/logger");
var fs = require('./utils/fs');
var parseNavigation = require('./utils/navigation');
var parseProgress = require('./utils/progress');
var pageUtil = require('./utils/page');
var pathUtil = require('./utils/path');
var links = require('./utils/links');
var i18n = require('./utils/i18n');
var logger = require('./utils/logger');
var Configuration = require("./configuration");
var TemplateEngine = require("./template");
var Plugin = require("./plugin");
var PluginsList = require("./pluginslist");
var Configuration = require('./configuration');
var TemplateEngine = require('./template');
var PluginsList = require('./pluginslist');
var generators = require("./generators");
var generators = require('./generators');
var Book = function(root, context, parent) {
var that = this;
this.context = _.defaults(context || {}, {
// Extend book configuration
config: {},
@@ -35,7 +29,7 @@ var Book = function(root, context, parent) {
},
// Log level
logLevel: "info"
logLevel: 'info'
});
// Log
@@ -49,7 +43,7 @@ var Book = function(root, context, parent) {
// Configuration
this.config = new Configuration(this, this.context.config);
Object.defineProperty(this, "options", {
Object.defineProperty(this, 'options', {
get: function () {
return this.config.options;
}
@@ -83,21 +77,13 @@ var Book = function(root, context, parent) {
this.readmeFile = null;
this.langsFile = null;
// Search Index
this.searchIndex = lunr(function () {
this.ref('url');
this.field('title', { boost: 10 });
this.field('body');
});
// Bind methods
_.bindAll(this);
};
// Return string representation
Book.prototype.toString = function() {
return "[Book "+this.root+"]";
return '[Book '+this.root+']';
};
// Initialize and parse the book: config, summary, glossary
@@ -115,11 +101,11 @@ Book.prototype.parse = function() {
return that.parseLangs()
.then(function() {
multilingual = that.langs.length > 0;
if (multilingual) that.log.info.ln("Parsing multilingual book, with", that.langs.length, "languages");
if (multilingual) that.log.info.ln('Parsing multilingual book, with', that.langs.length, 'languages');
// Sub-books that inherit from the current book configuration
that.books = _.map(that.langs, function(lang) {
that.log.info.ln("Preparing language book", lang.lang);
that.log.info.ln('Preparing language book', lang.lang);
return new Book(
path.join(that.root, lang.path),
_.merge({}, that.context, {
@@ -129,7 +115,7 @@ Book.prototype.parse = function() {
})
}),
that
)
);
});
});
})
@@ -168,15 +154,15 @@ Book.prototype.generate = function(generator) {
var that = this;
that.options.generator = generator || that.options.generator;
that.log.info.ln("start generation with", that.options.generator, "generator");
that.log.info.ln('start generation with', that.options.generator, 'generator');
return Q()
// Clean output folder
.then(function() {
that.log.info("clean", that.options.generator, "generator");
that.log.info('clean', that.options.generator, 'generator');
return fs.clean(that.options.output)
.progress(function(p) {
that.log.debug.ln("remove", p.file, "("+p.i+"/"+p.count+")");
that.log.debug.ln('remove', p.file, '('+p.i+'/'+p.count+')');
})
.then(function() {
that.log.info.ok();
@@ -186,12 +172,20 @@ Book.prototype.generate = function(generator) {
// Create generator
.then(function() {
var Generator = generators[generator];
if (!Generator) throw "Generator '"+that.options.generator+"' doesn't exist";
if (!Generator) throw 'Generator \''+that.options.generator+'\' doesn\'t exist';
generator = new Generator(that);
return generator.prepare();
})
// Transform configuration
.then(function() {
return that.callHook('config', that.config.dump())
.then(function(newConfig) {
that.config.replace(newConfig);
});
})
// Generate content
.then(function() {
if (that.isMultilingual()) {
@@ -199,12 +193,12 @@ Book.prototype.generate = function(generator) {
} else {
// Separate list of files into the different operations needed
var ops = _.groupBy(that.files, function(file) {
if (file[file.length -1] == "/") {
return "directories";
if (file[file.length -1] == '/') {
return 'directories';
} else if (_.contains(parsers.extensions, path.extname(file)) && that.navigation[file]) {
return "content";
return 'content';
} else {
return "files";
return 'files';
}
});
@@ -213,9 +207,9 @@ Book.prototype.generate = function(generator) {
// First, let's create folder
.then(function() {
return _.reduce(ops["directories"] || [], function(prev, folder) {
return _.reduce(ops.directories || [], function(prev, folder) {
return prev.then(function() {
that.log.debug.ln("transferring folder", folder);
that.log.debug.ln('transferring folder', folder);
return Q(generator.transferFolder(folder));
});
}, Q());
@@ -223,19 +217,19 @@ Book.prototype.generate = function(generator) {
// Then, let's copy other files
.then(function() {
return Q.all(_.map(ops["files"] || [], function(file) {
that.log.debug.ln("transferring file", file);
return Q.all(_.map(ops.files || [], function(file) {
that.log.debug.ln('transferring file', file);
return Q(generator.transferFile(file));
}));
})
// Finally let's generate content
.then(function() {
var nFiles = (ops["content"] || []).length;
return _.reduce(ops["content"] || [], function(prev, file, i) {
var nFiles = (ops.content || []).length;
return _.reduce(ops.content || [], function(prev, file, i) {
return prev.then(function() {
var p = ((i*100)/nFiles).toFixed(0)+"%";
that.log.debug.ln("processing", file, p);
var p = ((i*100)/nFiles).toFixed(0)+'%';
that.log.debug.ln('processing', file, p);
return Q(generator.convertFile(file))
.fail(function(err) {
@@ -252,21 +246,21 @@ Book.prototype.generate = function(generator) {
// Finish generation
.then(function() {
return generator.callHook("finish:before");
return that.callHook('finish:before');
})
.then(function() {
return generator.finish();
})
.then(function() {
return generator.callHook("finish");
return that.callHook('finish');
})
.then(function() {
that.log.info.ln("generation is finished");
that.log.info.ln('generation is finished');
});
};
// Generate the output for a multilingual book
Book.prototype.generateMultiLingual = function(generator) {
Book.prototype.generateMultiLingual = function() {
var that = this;
return Q()
@@ -287,26 +281,26 @@ Book.prototype.generateFile = function(output, options) {
options = _.defaults(options || {}, {
ebookFormat: path.extname(output).slice(1)
});
output = output || path.resolve(book.root, "book."+options.ebookFormat);
output = output || path.resolve(book.root, 'book.'+options.ebookFormat);
return fs.tmp.dir()
.then(function(tmpDir) {
book.setOutput(tmpDir);
return book.generate(options.ebookFormat)
.then(function(_options) {
.then(function() {
var copyFile = function(lang) {
var _outputFile = output;
var _tmpDir = tmpDir;
if (lang) {
_outputFile = _outputFile.slice(0, -path.extname(_outputFile).length)+"_"+lang+path.extname(_outputFile);
_outputFile = _outputFile.slice(0, -path.extname(_outputFile).length)+'_'+lang+path.extname(_outputFile);
_tmpDir = path.join(_tmpDir, lang);
}
book.log.debug.ln("copy ebook to", _outputFile);
book.log.debug.ln('copy ebook to', _outputFile);
return fs.copy(
path.join(_tmpDir, "index."+options.ebookFormat),
path.join(_tmpDir, 'index.'+options.ebookFormat),
_outputFile
);
};
@@ -326,7 +320,7 @@ Book.prototype.generateFile = function(output, options) {
}
})
.then(function(n) {
book.log.info.ok(n+" file(s) generated");
book.log.info.ok(n+' file(s) generated');
return fs.remove(tmpDir);
});
@@ -338,7 +332,7 @@ Book.prototype.generateFile = function(output, options) {
Book.prototype.parseConfig = function() {
var that = this;
that.log.info("loading book configuration....")
that.log.info('loading book configuration....');
return that.config.load()
.then(function() {
that.log.info.ok();
@@ -348,39 +342,38 @@ Book.prototype.parseConfig = function() {
// Parse list of plugins
Book.prototype.parsePlugins = function() {
var that = this;
var failed = [];
// Load plugins
return that.plugins.load(that.options.plugins)
.then(function() {
if (_.size(that.plugins.failed) > 0) return Q.reject(new Error("Error loading plugins: "+that.plugins.failed.join(",")+". Run 'gitbook install' to install plugins from NPM."));
if (_.size(that.plugins.failed) > 0) return Q.reject(new Error('Error loading plugins: '+that.plugins.failed.join(',')+'. Run \'gitbook install\' to install plugins from NPM.'));
that.log.info.ok(that.plugins.count()+" plugins loaded");
that.log.debug.ln("normalize plugins list");
that.log.info.ok(that.plugins.count()+' plugins loaded');
that.log.debug.ln('normalize plugins list');
});
};
// Parse readme to extract defaults title and description
Book.prototype.parseReadme = function() {
var that = this;
var structure = that.config.getStructure("readme");
that.log.debug.ln("start parsing readme:", structure);
var filename = that.config.getStructure('readme', true);
that.log.debug.ln('start parsing readme:', filename);
return that.findFile(structure)
return that.findFile(filename)
.then(function(readme) {
if (!readme) throw "No README file";
if (!_.contains(that.files, readme.path)) throw "README file is ignored";
if (!readme) throw 'No README file';
if (!_.contains(that.files, readme.path)) throw 'README file is ignored';
that.readmeFile = readme.path;
that._defaultsStructure(that.readmeFile);
that.log.debug.ln("readme located at", that.readmeFile);
that.log.debug.ln('readme located at', that.readmeFile);
return that.template.renderFile(that.readmeFile)
.then(function(content) {
return readme.parser.readme(content)
.fail(function(err) {
throw that.normError(err, {
name: err.name || "Readme Parse Error",
name: err.name || 'Readme Parse Error',
fileName: that.readmeFile
});
});
@@ -397,23 +390,23 @@ Book.prototype.parseReadme = function() {
Book.prototype.parseLangs = function() {
var that = this;
var structure = that.config.getStructure("langs");
that.log.debug.ln("start parsing languages index:", structure);
var filename = that.config.getStructure('langs', true);
that.log.debug.ln('start parsing languages index:', filename);
return that.findFile(structure)
return that.findFile(filename)
.then(function(langs) {
if (!langs) return [];
that.langsFile = langs.path;
that._defaultsStructure(that.langsFile);
that.log.debug.ln("languages index located at", that.langsFile);
that.log.debug.ln('languages index located at', that.langsFile);
return that.template.renderFile(that.langsFile)
.then(function(content) {
return langs.parser.langs(content)
.fail(function(err) {
throw that.normError(err, {
name: err.name || "Langs Parse Error",
name: err.name || 'Langs Parse Error',
fileName: that.langsFile
});
});
@@ -428,19 +421,19 @@ Book.prototype.parseLangs = function() {
Book.prototype.parseSummary = function() {
var that = this;
var structure = that.config.getStructure("summary");
that.log.debug.ln("start parsing summary:", structure);
var filename = that.config.getStructure('summary', true);
that.log.debug.ln('start parsing summary:', filename);
return that.findFile(structure)
return that.findFile(filename)
.then(function(summary) {
if (!summary) throw "No SUMMARY file";
if (!summary) throw 'No SUMMARY file';
// Remove the summary from the list of files to parse
that.summaryFile = summary.path;
that._defaultsStructure(that.summaryFile);
that.files = _.without(that.files, that.summaryFile);
that.log.debug.ln("summary located at", that.summaryFile);
that.log.debug.ln('summary located at', that.summaryFile);
return that.template.renderFile(that.summaryFile)
.then(function(content) {
return summary.parser.summary(content, {
@@ -450,7 +443,7 @@ Book.prototype.parseSummary = function() {
})
.fail(function(err) {
throw that.normError(err, {
name: err.name || "Summary Parse Error",
name: err.name || 'Summary Parse Error',
fileName: that.summaryFile
});
});
@@ -466,10 +459,10 @@ Book.prototype.parseSummary = function() {
Book.prototype.parseGlossary = function() {
var that = this;
var structure = that.config.getStructure("glossary");
that.log.debug.ln("start parsing glossary: ", structure);
var filename = that.config.getStructure('glossary', true);
that.log.debug.ln('start parsing glossary: ', filename);
return that.findFile(structure)
return that.findFile(filename)
.then(function(glossary) {
if (!glossary) return [];
@@ -478,13 +471,13 @@ Book.prototype.parseGlossary = function() {
that._defaultsStructure(that.glossaryFile);
that.files = _.without(that.files, that.glossaryFile);
that.log.debug.ln("glossary located at", that.glossaryFile);
that.log.debug.ln('glossary located at', that.glossaryFile);
return that.template.renderFile(that.glossaryFile)
.then(function(content) {
return glossary.parser.glossary(content)
.fail(function(err) {
throw that.normError(err, {
name: err.name || "Glossary Parse Error",
name: err.name || 'Glossary Parse Error',
fileName: that.glossaryFile
});
});
@@ -512,16 +505,16 @@ Book.prototype.parsePage = function(filename, options) {
var interpolate = function(fn) {
return Q(fn(page))
.then(function(_page) {
page = _page;
page = _page || page;
});
};
that.log.debug.ln("start parsing file", filename);
that.log.debug.ln('start parsing file', filename);
var extension = path.extname(filename);
var filetype = parsers.get(extension);
if (!filetype) return Q.reject(new Error("Can't parse file: "+filename));
if (!filetype) return Q.reject(new Error('Can\'t parse file: '+filename));
// Type of parser used
page.type = filetype.name;
@@ -535,7 +528,7 @@ Book.prototype.parsePage = function(filename, options) {
// Progress in the book
page.progress = parseProgress(that.navigation, filename);
that.log.debug.ln("render template", filename);
that.log.debug.ln('render template', filename);
// Read file content
return that.readFile(page.path)
@@ -562,7 +555,7 @@ Book.prototype.parsePage = function(filename, options) {
.then(function(content) {
page.content = content;
that.log.debug.ln("use file parser", filetype.name, "for", filename);
that.log.debug.ln('use file parser', filetype.name, 'for', filename);
return filetype.page(page.content);
})
@@ -570,11 +563,11 @@ Book.prototype.parsePage = function(filename, options) {
.then(function(_page) {
return _.reduce(_page.sections, function(prev, section) {
return prev.then(function(_sections) {
return that.template.postProcess(section.content || "")
return that.template.postProcess(section.content || '')
.then(function(content) {
section.content = content;
return _sections.concat([section]);
})
});
});
}, Q([]));
})
@@ -599,7 +592,6 @@ Book.prototype.parsePage = function(filename, options) {
})
.then(function() {
that.indexPage(page);
return page;
});
};
@@ -608,43 +600,74 @@ Book.prototype.parsePage = function(filename, options) {
Book.prototype.findFile = function(filename) {
var that = this;
return _.reduce(parsers.extensions, function(prev, ext) {
var ext = path.extname(filename);
var basename = path.basename(filename, ext);
// Ordered list of extensions to test
var exts = parsers.extensions;
if (ext) exts = _.uniq([ext].concat(exts));
return _.reduce(exts, function(prev, ext) {
return prev.then(function(output) {
// Stop if already find a parser
if (output) return output;
var filepath = filename+ext;
var filepath = basename+ext;
return fs.findFile(that.root, filepath)
.then(function(realFilepath) {
if (!realFilepath) return null;
return that.fileExists(filepath)
.then(function(exists) {
if (!exists) return null;
return {
parser: parsers.get(ext),
path: filepath
path: realFilepath
};
})
});
});
}, Q(null));
};
// Format a string using a specific markup language
Book.prototype.formatString = function(extension, content) {
return Q()
.then(function() {
var filetype = parsers.get(extension);
if (!filetype) throw new Error('Filetype doesn\'t exist: '+filetype);
return filetype.page(content);
})
// Merge sections
.then(function(page) {
return _.reduce(page.sections, function(content, section) {
return content + section.content;
}, '');
});
};
// Check if a file exists in the book
Book.prototype.fileExists = function(filename) {
return fs.exists(
path.join(this.root, filename)
this.resolve(filename)
);
};
// Check if a file path is inside the book
Book.prototype.fileIsInBook = function(filename) {
return pathUtil.isInRoot(this.root, filename);
};
// Read a file
Book.prototype.readFile = function(filename) {
return fs.readFile(
path.join(this.root, filename),
{ encoding: "utf8" }
this.resolve(filename),
{ encoding: 'utf8' }
);
};
// Return stat for a file
Book.prototype.statFile = function(filename) {
return fs.stat(path.join(this.root, filename));
return fs.stat(this.resolve(filename));
};
// List all files in the book
@@ -682,7 +705,7 @@ Book.prototype.listAllFiles = function() {
};
// Return true if the book is a multilingual book
Book.prototype.isMultilingual = function(filename) {
Book.prototype.isMultilingual = function() {
return this.books.length > 0;
};
@@ -702,40 +725,44 @@ Book.prototype.isEntryPoint = function(fp) {
return fp == this.readmeFile;
};
// Resolve a path in book
Book.prototype.resolve = function(p) {
return path.resolve(this.root, p);
// Alias to book.config.get
Book.prototype.getConfig = function(key, def) {
return this.config.get(key, def);
};
// Resolve a path in the book source
// Enforce that the output path in the root folder
Book.prototype.resolve = function() {
return pathUtil.resolveInRoot.apply(null, [this.root].concat(_.toArray(arguments)));
};
// Resolve a path in the book output
// Enforce that the output path in the output folder
Book.prototype.resolveOutput = function() {
return pathUtil.resolveInRoot.apply(null, [this.options.output].concat(_.toArray(arguments)));
};
// Convert an abslute path into a relative path to this
Book.prototype.relative = function(p) {
return path.relative(this.root, p);
};
// Normalize a path to .html and convert README -> index
Book.prototype.contentPath = function(link) {
if (
path.basename(link, path.extname(link)) == "README"
|| link == this.readmeFile
path.basename(link, path.extname(link)) == 'README' ||
link == this.readmeFile
) {
link = path.join(path.dirname(link), "index"+path.extname(link));
link = path.join(path.dirname(link), 'index'+path.extname(link));
}
link = links.changeExtension(link, ".html");
link = links.changeExtension(link, '.html');
return link;
}
};
// Normalize a link to .html and convert README -> index
Book.prototype.contentLink = function(link) {
return links.normalize(this.contentPath(link));
}
// Index a page into the search index
Book.prototype.indexPage = function(page) {
var nav = this.navigation[page.path];
if (!nav) return;
this.log.debug.ln("index page", page.path);
this.searchIndex.add({
url: this.contentLink(page.path),
title: nav.title,
body: pageUtil.extractText(page.sections),
});
};
// Default structure paths to an extension
@@ -743,11 +770,11 @@ Book.prototype._defaultsStructure = function(filename) {
var that = this;
var extension = path.extname(filename);
that.readmeFile = that.readmeFile || that.config.getStructure("readme")+extension;
that.summaryFile = that.summaryFile || that.config.getStructure("summary")+extension;
that.glossaryFile = that.glossaryFile || that.config.getStructure("glossary")+extension;
that.langsFile = that.langsFile || that.config.getStructure("langs")+extension;
}
that.readmeFile = that.readmeFile || that.config.getStructure('readme')+extension;
that.summaryFile = that.summaryFile || that.config.getStructure('summary')+extension;
that.glossaryFile = that.glossaryFile || that.config.getStructure('glossary')+extension;
that.langsFile = that.langsFile || that.config.getStructure('langs')+extension;
};
// Change output path
Book.prototype.setOutput = function(p) {
@@ -760,7 +787,7 @@ Book.prototype.setOutput = function(p) {
};
// Translate a strign according to the book language
Book.prototype.i18n = function(phrase) {
Book.prototype.i18n = function() {
var args = Array.prototype.slice.call(arguments);
return i18n.__.apply({}, [this.config.normalizeLanguage()].concat(args));
};
@@ -779,87 +806,18 @@ Book.prototype.normError = function(err, opts, defs) {
err.toString = function() {
var attributes = [];
if (this.fileName) attributes.push("In file '"+this.fileName+"'");
if (this.lineNumber) attributes.push("Line "+this.lineNumber);
if (this.columnNumber) attributes.push("Column "+this.columnNumber);
return (this.name || "Error")+": "+this.message+((attributes.length > 0)? " ("+attributes.join(", ")+")" : "")
if (this.fileName) attributes.push('In file \''+this.fileName+'\'');
if (this.lineNumber) attributes.push('Line '+this.lineNumber);
if (this.columnNumber) attributes.push('Column '+this.columnNumber);
return (this.name || 'Error')+': '+this.message+((attributes.length > 0)? ' ('+attributes.join(', ')+')' : '');
};
return err;
};
// Init and return a book
Book.init = function(root, opts) {
var book = new Book(root, opts);
var extensionToUse = ".md";
var chaptersPaths = function(chapters) {
return _.reduce(chapters || [], function(accu, chapter) {
var o = {
title: chapter.title
};
if (chapter.path) o.path = chapter.path;
return accu.concat(
[o].concat(chaptersPaths(chapter.articles))
);
}, []);
};
book.log.info.ln("init book at", root);
return fs.mkdirp(root)
.then(function() {
book.log.info.ln("detect structure from SUMMARY (if it exists)");
return book.parseSummary();
})
.fail(function() {
return Q();
})
.then(function() {
var summary = book.summaryFile || "SUMMARY.md";
var chapters = book.summary.chapters || [];
extensionToUse = path.extname(summary);
if (chapters.length == 0) {
chapters = [
{
title: "Summary",
path: "SUMMARY"+extensionToUse
},
{
title: "Introduction",
path: "README"+extensionToUse
}
];
}
return Q(chaptersPaths(chapters));
})
.then(function(chapters) {
// Create files that don't exist
return Q.all(_.map(chapters, function(chapter) {
if (!chapter.path) return Q();
var absolutePath = path.resolve(book.root, chapter.path);
return fs.exists(absolutePath)
.then(function(exists) {
if(exists) {
book.log.info.ln("found", chapter.path);
return;
} else {
book.log.info.ln("create", chapter.path);
}
return fs.mkdirp(path.dirname(absolutePath))
.then(function() {
return fs.writeFile(absolutePath, '# '+chapter.title+'\n');
});
});
}));
})
.then(function() {
book.log.info.ln("initialization is finished");
});
// Call a hook in plugins
Book.prototype.callHook = function(name, data) {
return this.plugins.hook(name, data);
};
module.exports= Book;
+109
View File
@@ -0,0 +1,109 @@
var path = require('path');
module.exports = {
// Options that can't be extend
'configFile': 'book',
'generator': 'website',
'extension': null,
// Book metadats (somes are extracted from the README by default)
'title': null,
'description': null,
'isbn': null,
'language': 'en',
'direction': null,
'author': null,
// version of gitbook to use
'gitbook': '*',
// Structure
'structure': {
'langs': 'LANGS.md',
'readme': 'README.md',
'glossary': 'GLOSSARY.md',
'summary': 'SUMMARY.md'
},
// CSS Styles
'styles': {
'website': 'styles/website.css',
'print': 'styles/print.css',
'ebook': 'styles/ebook.css',
'pdf': 'styles/pdf.css',
'mobi': 'styles/mobi.css',
'epub': 'styles/epub.css'
},
// Plugins list, can contain '-name' for removing default plugins
'plugins': [],
// Global configuration for plugins
'pluginsConfig': {},
// Variables for templating
'variables': {},
// Set another theme with your own layout
// It's recommended to use plugins or add more options for default theme, though
// See https://github.com/GitbookIO/gitbook/issues/209
'theme': path.resolve(__dirname, '../theme'),
// Links in template (null: default, false: remove, string: new value)
'links': {
// Custom links at top of sidebar
'sidebar': {
// 'Custom link name': 'https://customlink.com'
},
// Sharing links
'sharing': {
'google': null,
'facebook': null,
'twitter': null,
'weibo': null,
'all': null
}
},
// Options for PDF generation
'pdf': {
// Add toc at the end of the file
'toc': true,
// Add page numbers to the bottom of every page
'pageNumbers': false,
// Font for the file content
'fontSize': 12,
'fontFamily': 'Arial',
// Paper size for the pdf
// Choices are [ua0, ua1, ua2, ua3, ua4, ua5, ua6, ub0, ub1, ub2, ub3, ub4, ub5, ub6, ulegal, uletter]
'paperSize': 'a4',
// How to mark detected chapters.
// Choices are “pagebreak”, “rule”, 'both' or “none”.
'chapterMark' : 'pagebreak',
// An XPath expression. Page breaks are inserted before the specified elements.
// To disable use the expression: '/'
'pageBreaksBefore': '/',
// Margin (in pts)
// Note: 72 pts equals 1 inch
'margin': {
'right': 62,
'left': 62,
'top': 56,
'bottom': 56
},
// Header HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
'headerTemplate': null,
// Footer HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
'footerTemplate': null
}
};
+114 -170
View File
@@ -1,38 +1,57 @@
var _ = require("lodash");
var Q = require("q");
var path = require("path");
var _ = require('lodash');
var Q = require('q');
var path = require('path');
var semver = require('semver');
var pkg = require('../package.json');
var fs = require("./utils/fs");
var i18n = require("./utils/i18n");
var i18n = require('./utils/i18n');
var version = require('./version');
var DEFAULT_CONFIG = require('./config_default');
// Default plugins added to each books
var defaultsPlugins = [];
var DEFAULT_PLUGINS = ['highlight', 'search', 'sharing', 'fontsettings'];
// Check if a plugin is a default plugin
// Plugin should be in the list
// And version from book.json specified for this plugin should be satisfied
function isDefaultPlugin(name, version) {
if (!_.contains(DEFAULT_PLUGINS, name)) return false;
try {
var pluginPkg = require('gitbook-plugin-'+name+'/package.json');
return semver.satisfies(pluginPkg.version, version || '*');
} catch(e) {
return false;
}
}
// Normalize a list of plugins to use
function normalizePluginsList(plugins) {
function normalizePluginsList(plugins, addDefaults) {
// Normalize list to an array
plugins = _.isString(plugins) ? plugins.split(",") : (plugins || []);
plugins = _.isString(plugins) ? plugins.split(',') : (plugins || []);
// Remove empty parts
plugins = _.compact(plugins);
// Divide as {name, version} to handle format like "myplugin@1.0.0"
// Divide as {name, version} to handle format like 'myplugin@1.0.0'
plugins = _.map(plugins, function(plugin) {
if (plugin.name) return plugin;
var parts = plugin.split("@");
var parts = plugin.split('@');
var name = parts[0];
var version = parts[1];
return {
'name': parts[0],
'version': parts[1] // optional
}
'name': name,
'version': version, // optional
'isDefault': isDefaultPlugin(name, version)
};
});
// List plugins to remove
var toremove = _.chain(plugins)
.filter(function(plugin) {
return plugin.name.length > 0 && plugin.name[0] == "-";
return plugin.name.length > 0 && plugin.name[0] == '-';
})
.map(function(plugin) {
return plugin.name.slice(1);
@@ -40,54 +59,33 @@ function normalizePluginsList(plugins) {
.value();
// Merge with defaults
plugins = _.chain(plugins)
.concat(_.map(defaultsPlugins, function(plugin) {
return { 'name': plugin }
}))
.uniq()
.value();
if (addDefaults !== false) {
_.each(DEFAULT_PLUGINS, function(plugin) {
if (_.find(plugins, { name: plugin })) {
return;
}
// Build final list
plugins.push({
'name': plugin,
'isDefault': true
});
});
}
// Remove plugin that start with '-'
plugins = _.filter(plugins, function(plugin) {
return !_.contains(toremove, plugin.name) && !(plugin.name.length > 0 && plugin.name[0] == "-");
return !_.contains(toremove, plugin.name) && !(plugin.name.length > 0 && plugin.name[0] == '-');
});
// Remove duplicates
plugins = _.uniq(plugins, 'name');
return plugins;
}
// Normalize a list of plugin name to use
function normalizePluginsNames(plugins) {
return _.pluck(normalizePluginsList(plugins), "name");
};
var Configuration = function(book, options) {
var that = this;
this.book = book;
this.options = _.cloneDeep(Configuration.DEFAULT);
this.options = _.merge(this.options, options || {});
// options.input == book.root
Object.defineProperty(this.options, "input", {
get: function () {
return that.book.root;
}
});
// options.originalInput == book.parent.root
Object.defineProperty(this.options, "originalInput", {
get: function () {
return that.book.parent? that.book.parent.root : undefined;
}
});
// options.originalOutput == book.parent.options.output
Object.defineProperty(this.options, "originalOutput", {
get: function () {
return that.book.parent? that.book.parent.options.output : undefined;
}
});
this.replace(options);
};
// Read and parse the configuration
@@ -96,11 +94,11 @@ Configuration.prototype.load = function() {
return Q()
.then(function() {
var configPath, config;
var configPath, _config;
try {
configPath = require.resolve(
path.resolve(that.book.root, that.options.configFile)
that.book.resolve(that.options.configFile)
);
// Invalidate node.js cache for livreloading
@@ -118,18 +116,20 @@ Configuration.prototype.load = function() {
}
})
.then(function() {
if (!semver.satisfies(pkg.version, that.options.gitbook)) {
throw "GitBook version doesn't satisfy version required by the book: "+that.options.gitbook;
}
if (that.options.gitbook == "*") {
that.book.log.warn.ln("you should specify a gitbook version to use in your book.json, for example: "+(_.first(pkg.version.split("."))+".x.x"));
if (!that.book.isSubBook()) {
if (!version.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(pkg.version, 'patch'), that.options.gitbook)) {
that.book.log.warn.ln('gitbook version specified in your book.json might be too strict for future patches, \''+(_.first(pkg.version.split('.'))+'.x.x')+'\' is more adequate');
}
}
that.options.output = path.resolve(that.options.output || path.join(that.book.root, "_book"));
that.options.output = path.resolve(that.options.output || that.book.resolve('_book'));
that.options.plugins = normalizePluginsList(that.options.plugins);
that.options.defaultsPlugins = normalizePluginsList(that.options.defaultsPlugins || "");
that.options.defaultsPlugins = normalizePluginsList(that.options.defaultsPlugins || '', false);
that.options.plugins = _.union(that.options.plugins, that.options.defaultsPlugins);
that.options.plugins = _.uniq(that.options.plugins, "name");
that.options.plugins = _.uniq(that.options.plugins, 'name');
// Default value for text direction (from language)
if (!that.options.direction) {
@@ -146,9 +146,47 @@ Configuration.prototype.extend = function(options) {
_.extend(this.options, options);
};
// Replace the whole configuration
Configuration.prototype.replace = function(options) {
var that = this;
this.options = _.cloneDeep(DEFAULT_CONFIG);
this.options = _.merge(this.options, options || {});
// options.input == book.root
Object.defineProperty(this.options, 'input', {
get: function () {
return that.book.root;
}
});
// options.originalInput == book.parent.root
Object.defineProperty(this.options, 'originalInput', {
get: function () {
return that.book.parent? that.book.parent.root : undefined;
}
});
// options.originalOutput == book.parent.options.output
Object.defineProperty(this.options, 'originalOutput', {
get: function () {
return that.book.parent? that.book.parent.options.output : undefined;
}
});
};
// Dump configuration as json object
Configuration.prototype.dump = function() {
return _.cloneDeep(this.options);
};
// Get structure file
Configuration.prototype.getStructure = function(name) {
return this.options.structure[name].split(".").slice(0, -1).join(".");
Configuration.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 normalized language
@@ -156,111 +194,17 @@ Configuration.prototype.normalizeLanguage = function() {
return i18n.normalizeLanguage(this.options.language);
};
// Default configuration
Configuration.DEFAULT = {
// Options that can't be extend
"configFile": "book",
"generator": "website",
"extension": null,
// Book metadats (somes are extracted from the README by default)
"title": null,
"description": null,
"isbn": null,
"language": "en",
"direction": null,
"author": null,
// version of gitbook to use
"gitbook": "*",
// Structure
"structure": {
"langs": "LANGS.md",
"readme": "README.md",
"glossary": "GLOSSARY.md",
"summary": "SUMMARY.md"
},
// CSS Styles
"styles": {
"website": "styles/website.css",
"print": "styles/print.css",
"ebook": "styles/ebook.css",
"pdf": "styles/pdf.css",
"mobi": "styles/mobi.css",
"epub": "styles/epub.css"
},
// Plugins list, can contain "-name" for removing default plugins
"plugins": [],
// Global configuration for plugins
"pluginsConfig": {
"fontSettings": {
"theme": null, //"sepia", "night" or "white",
"family": "sans",// "serif" or "sans",
"size": 2 // 1 - 4
}
},
// Variables for templating
"variables": {},
// Set another theme with your own layout
// It's recommended to use plugins or add more options for default theme, though
// See https://github.com/GitbookIO/gitbook/issues/209
"theme": path.resolve(__dirname, '../theme'),
// Links in template (null: default, false: remove, string: new value)
"links": {
// Custom links at top of sidebar
"sidebar": {
//"Custom link name": "https://customlink.com"
},
// Sharing links
"sharing": {
"google": null,
"facebook": null,
"twitter": null,
"weibo": null,
"all": null
}
},
// Options for PDF generation
"pdf": {
// Add toc at the end of the file
"toc": true,
// Add page numbers to the bottom of every page
"pageNumbers": false,
// Font size for the file content
"fontSize": 12,
// Paper size for the pdf
// Choices are [ua0, ua1, ua2, ua3, ua4, ua5, ua6, ub0, ub1, ub2, ub3, ub4, ub5, ub6, ulegal, uletter]
"paperSize": "a4",
// Margin (in pts)
// Note: 72 pts equals 1 inch
"margin": {
"right": 62,
"left": 62,
"top": 56,
"bottom": 56
},
//Header HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
"headerTemplate": "",
//Footer HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
"footerTemplate": ""
}
// Return a configuration
Configuration.prototype.get = function(key, def) {
return _.get(this.options, key, def);
};
// Update a configuration
Configuration.prototype.set = function(key, value) {
return _.set(this.options, key, value);
};
// Default configuration
Configuration.DEFAULT = DEFAULT_CONFIG;
module.exports= Configuration;
+73
View File
@@ -0,0 +1,73 @@
var _ = require('lodash');
var path = require('path');
var nunjucks = require('nunjucks');
var git = require('./utils/git');
var fs = require('./utils/fs');
var pathUtil = require('./utils/path');
// The loader should handle relative and git url
var BookLoader = nunjucks.Loader.extend({
async: true,
init: function(book, opts) {
this.opts = _.defaults(opts || {}, {
interpolate: _.identity
});
this.book = book;
},
getSource: function(fileurl, callback) {
var that = this;
git.resolveFile(fileurl)
.then(function(filepath) {
// Is local file
if (!filepath) filepath = path.resolve(fileurl);
else that.book.log.debug.ln('resolve from git', fileurl, 'to', filepath);
// Read file from absolute path
return fs.readFile(filepath)
.then(function(source) {
return that.opts.interpolate(filepath, source.toString());
})
.then(function(source) {
return {
src: source,
path: filepath,
// We disable cache sincde content is modified (shortcuts, ...)
noCache: true
};
});
})
.nodeify(callback);
},
resolve: function(from, to) {
// If origin is in the book, we enforce result file to be in the book
if (this.book.fileIsInBook(from)) {
return this.book.resolve(
this.book.relative(path.dirname(from)),
to
);
}
// If origin is in a git repository, we resolve file in the git repository
var gitRoot = 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);
},
// Handle all files as relative, so that nunjucks pass responsability to 'resolve'
// Only git urls are considered as absolute
isRelative: function(filename) {
return !git.checkUrl(filename);
}
});
module.exports = BookLoader;
+15 -17
View File
@@ -1,14 +1,12 @@
var _ = require("lodash");
var path = require("path");
var Q = require("q");
var fs = require("./utils/fs");
var Plugin = require("./plugin");
var _ = require('lodash');
var path = require('path');
var Q = require('q');
var fs = require('./utils/fs');
var BaseGenerator = function(book) {
this.book = book;
Object.defineProperty(this, "options", {
Object.defineProperty(this, 'options', {
get: function () {
return this.book.options;
}
@@ -18,25 +16,25 @@ var BaseGenerator = function(book) {
};
BaseGenerator.prototype.callHook = function(name, data) {
return this.book.plugins.hook(name, data);
return this.book.callHook(name, data);
};
// Prepare the genertor
BaseGenerator.prototype.prepare = function() {
var that = this;
return that.callHook("init");
return that.callHook('init');
};
// Write a parsed file to the output
BaseGenerator.prototype.convertFile = function(input) {
return Q.reject(new Error("Could not convert "+input));
return Q.reject(new Error('Could not convert '+input));
};
// Copy file to the output (non parsable)
BaseGenerator.prototype.transferFile = function(input) {
return fs.copy(
path.join(this.book.root, input),
this.book.resolve(input),
path.join(this.options.output, input)
);
};
@@ -53,26 +51,26 @@ BaseGenerator.prototype.copyCover = function() {
var that = this;
return Q.all([
fs.copy(path.join(that.book.root, "cover.jpg"), path.join(that.options.output, "cover.jpg")),
fs.copy(path.join(that.book.root, "cover_small.jpg"), path.join(that.options.output, "cover_small.jpg"))
fs.copy(that.book.resolve('cover.jpg'), path.join(that.options.output, 'cover.jpg')),
fs.copy(that.book.resolve('cover_small.jpg'), path.join(that.options.output, 'cover_small.jpg'))
])
.fail(function() {
// If orignaly from multi-lang, try copy from parent
if (!that.book.isSubBook()) return;
return Q.all([
fs.copy(path.join(that.book.parentRoot(), "cover.jpg"), path.join(that.options.output, "cover.jpg")),
fs.copy(path.join(that.book.parentRoot(), "cover_small.jpg"), path.join(that.options.output, "cover_small.jpg"))
fs.copy(path.join(that.book.parentRoot(), 'cover.jpg'), path.join(that.options.output, 'cover.jpg')),
fs.copy(path.join(that.book.parentRoot(), 'cover_small.jpg'), path.join(that.options.output, 'cover_small.jpg'))
]);
})
.fail(function(err) {
.fail(function() {
return Q();
});
};
// At teh end of the generation
BaseGenerator.prototype.finish = function() {
return Q.reject(new Error("Could not finish generation"));
return Q.reject(new Error('Could not finish generation'));
};
module.exports = BaseGenerator;
+89 -56
View File
@@ -1,12 +1,13 @@
var util = require("util");
var path = require("path");
var Q = require("q");
var _ = require("lodash");
var util = require('util');
var path = require('path');
var Q = require('q');
var _ = require('lodash');
var juice = require('juice');
var exec = require('child_process').exec;
var fs = require("../utils/fs");
var stringUtils = require("../utils/string");
var BaseGenerator = require("./website");
var fs = require('../utils/fs');
var stringUtils = require('../utils/string');
var BaseGenerator = require('./website');
var Generator = function(book, format) {
BaseGenerator.apply(this, arguments);
@@ -15,10 +16,10 @@ var Generator = function(book, format) {
this.ebookFormat = format;
// Resources namespace
this.namespace = "ebook";
this.namespace = 'ebook';
// Styles to use
this.styles = _.compact(["print", "ebook", this.ebookFormat]);
this.styles = _.compact(['print', 'ebook', this.ebookFormat]);
// Convert images (svg -> png)
this.convertImages = true;
@@ -26,9 +27,9 @@ var Generator = function(book, format) {
util.inherits(Generator, BaseGenerator);
Generator.prototype.prepareTemplates = function() {
this.templates["page"] = this.book.plugins.template("ebook:page") || path.resolve(this.options.theme, 'templates/ebook/page.html');
this.templates["summary"] = this.book.plugins.template("ebook:summary") || path.resolve(this.options.theme, 'templates/ebook/summary.html');
this.templates["glossary"] = this.book.plugins.template("ebook:glossary") || path.resolve(this.options.theme, 'templates/ebook/glossary.html');
this.templates.page = this.book.plugins.template('ebook:page') || path.resolve(this.options.theme, 'templates/ebook/page.html');
this.templates.summary = this.book.plugins.template('ebook:summary') || path.resolve(this.options.theme, 'templates/ebook/summary.html');
this.templates.glossary = this.book.plugins.template('ebook:glossary') || path.resolve(this.options.theme, 'templates/ebook/glossary.html');
return Q();
};
@@ -37,8 +38,39 @@ Generator.prototype.prepareTemplates = function() {
Generator.prototype.writeSummary = function() {
var that = this;
that.book.log.info.ln("write SUMMARY.html");
return this._writeTemplate(this.templates["summary"], {}, path.join(this.options.output, "SUMMARY.html"));
that.book.log.info.ln('write SUMMARY.html');
return this._writeTemplate(this.templates.summary, {}, path.join(this.options.output, 'SUMMARY.html'));
};
// Return template for footer/header with inlined css
Generator.prototype.getPDFTemplate = function(id) {
var tpl = this.options.pdf[id+'Template'];
var defaultTpl = path.resolve(this.options.theme, 'templates/ebook/'+id+'.html');
var defaultCSS = path.resolve(this.options.theme, 'assets/ebook/pdf.css');
// Default template from theme
if (!tpl && fs.existsSync(defaultTpl)) {
tpl = fs.readFileSync(defaultTpl, { encoding: 'utf-8' });
}
// Inline CSS using juice
var stylesheets = [];
// From theme
if (fs.existsSync(defaultCSS)) {
stylesheets.push(fs.readFileSync(defaultCSS, { encoding: 'utf-8' }));
}
// Custom PDF style
if (this.styles.pdf) {
stylesheets.push(fs.readFileSync(this.book.resolveOutput(this.styles.pdf), { encoding: 'utf-8' }));
}
tpl = juice(tpl, {
extraCss: stylesheets.join('\n\n')
});
return tpl;
};
Generator.prototype.finish = function() {
@@ -52,70 +84,71 @@ Generator.prototype.finish = function() {
.then(function() {
if (!that.ebookFormat) return Q();
var d = Q.defer();
if (!that.options.cover && fs.existsSync(path.join(that.options.output, "cover.jpg"))) {
that.options.cover = path.join(that.options.output, "cover.jpg");
if (!that.options.cover && fs.existsSync(path.join(that.options.output, 'cover.jpg'))) {
that.options.cover = path.join(that.options.output, 'cover.jpg');
}
var d = Q.defer();
var _options = {
"--cover": that.options.cover,
"--title": that.options.title,
"--comments": that.options.description,
"--isbn": that.options.isbn,
"--authors": that.options.author,
"--language": that.options.language,
"--book-producer": "GitBook",
"--publisher": "GitBook",
"--chapter": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter ')]",
"--chapter-mark": "pagebreak",
"--page-breaks-before": "/",
"--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
'--cover': that.options.cover,
'--title': that.options.title,
'--comments': that.options.description,
'--isbn': that.options.isbn,
'--authors': that.options.author,
'--language': that.options.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.ebookFormat == "pdf") {
if (that.ebookFormat == 'pdf') {
var pdfOptions = that.options.pdf;
_.extend(_options, {
"--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": String(pdfOptions.headerTemplate) || "<p class='header'><span>"+that.options.title+"</span></p>",
"--pdf-footer-template": String(pdfOptions.footerTemplate) || "<p class='footer'><span>_SECTION_</span> <span style='float:right;'>_PAGENUM_</span></p>"
'--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)
});
} else if (that.ebookFormat == "epub") {
} else if (that.ebookFormat == 'epub') {
_.extend(_options, {
"--dont-split-on-page-breaks": true
'--dont-split-on-page-breaks': true
});
}
var command = [
"ebook-convert",
path.join(that.options.output, "SUMMARY.html"),
path.join(that.options.output, "index."+that.ebookFormat),
'ebook-convert',
path.join(that.options.output, 'SUMMARY.html'),
path.join(that.options.output, 'index.'+that.ebookFormat),
stringUtils.optionsToShellArgs(_options)
].join(" ");
].join(' ');
that.book.log.info("start conversion to", that.ebookFormat, "....");
that.book.log.info('start conversion to', that.ebookFormat, '....');
var child = exec(command, function (error, stdout, stderr) {
var child = exec(command, function (error, stdout) {
if (error) {
that.book.log.info.fail();
if (error.code == 127) {
error.message = "Need to install ebook-convert from Calibre";
error.message = 'Need to install ebook-convert from Calibre';
} else {
error.message = error.message + " "+stdout;
error.message = error.message + ' '+stdout;
}
return d.reject(error);
}
+12 -12
View File
@@ -1,11 +1,11 @@
var util = require("util");
var path = require("path");
var Q = require("q");
var _ = require("lodash");
var util = require('util');
var path = require('path');
var Q = require('q');
var _ = require('lodash');
var fs = require("../utils/fs");
var BaseGenerator = require("../generator");
var links = require("../utils/links");
var fs = require('../utils/fs');
var BaseGenerator = require('../generator');
var links = require('../utils/links');
var Generator = function() {
BaseGenerator.apply(this, arguments);
@@ -13,7 +13,7 @@ var Generator = function() {
util.inherits(Generator, BaseGenerator);
// Ignore some methods
Generator.prototype.transferFile = function(input) { };
Generator.prototype.transferFile = function() { };
// Convert an input file
Generator.prototype.convertFile = function(input) {
@@ -26,7 +26,7 @@ Generator.prototype.convertFile = function(input) {
sections: page.sections
};
var output = links.changeExtension(page.path, ".json");
var output = links.changeExtension(page.path, '.json');
output = path.join(that.options.output, output);
return fs.writeFile(
@@ -44,14 +44,14 @@ Generator.prototype.finish = function() {
// Write README.json
Generator.prototype.writeReadme = function() {
var that = this;
var mainlang, langs;
var mainLang, langs, readme;
return Q()
.then(function() {
langs = that.book.langs;
mainLang = langs.length > 0? _.first(langs).lang : null;
readme = links.changeExtension(that.book.readmeFile, ".json");
readme = links.changeExtension(that.book.readmeFile, '.json');
// Read readme from main language
return fs.readFile(
@@ -67,7 +67,7 @@ Generator.prototype.writeReadme = function() {
// Write it as README.json
return fs.writeFile(
path.join(that.options.output, "README.json"),
path.join(that.options.output, 'README.json'),
JSON.stringify(json, null, 4)
);
});
+46 -54
View File
@@ -1,19 +1,18 @@
var util = require("util");
var path = require("path");
var Q = require("q");
var _ = require("lodash");
var util = require('util');
var path = require('path');
var Q = require('q');
var _ = require('lodash');
var nunjucks = require("nunjucks");
var AutoEscapeExtension = require("nunjucks-autoescape")(nunjucks);
var FilterExtension = require("nunjucks-filter")(nunjucks);
var nunjucks = require('nunjucks');
var AutoEscapeExtension = require('nunjucks-autoescape')(nunjucks);
var FilterExtension = require('nunjucks-filter')(nunjucks);
var fs = require("../utils/fs");
var BaseGenerator = require("../generator");
var links = require("../utils/links");
var pageUtil = require("../utils/page");
var i18n = require("../utils/i18n");
var fs = require('../utils/fs');
var BaseGenerator = require('../generator');
var links = require('../utils/links');
var i18n = require('../utils/i18n');
var pkg = require("../../package.json");
var pkg = require('../../package.json');
var Generator = function() {
BaseGenerator.apply(this, arguments);
@@ -22,10 +21,10 @@ var Generator = function() {
this.revision = new Date();
// Resources namespace
this.namespace = "website";
this.namespace = 'website';
// Style to integrates in the output
this.styles = ["website"];
this.styles = ['website'];
// Convert images (svg -> png)
this.convertImages = false;
@@ -50,7 +49,12 @@ Generator.prototype.prepareStyles = function() {
this.styles = _.chain(this.styles)
.map(function(style) {
var stylePath = that.options.styles[style];
if (stylePath && fs.existsSync(path.resolve(that.book.root, stylePath))) {
var styleExists = (
fs.existsSync(that.book.resolveOutput(stylePath)) ||
fs.existsSync(that.book.resolve(stylePath))
);
if (stylePath && styleExists) {
return [style, stylePath];
}
return null;
@@ -64,9 +68,9 @@ Generator.prototype.prepareStyles = function() {
// Prepare templates
Generator.prototype.prepareTemplates = function() {
this.templates["page"] = this.book.plugins.template("site:page") || path.resolve(this.options.theme, 'templates/website/page.html');
this.templates["langs"] = this.book.plugins.template("site:langs") || path.resolve(this.options.theme, 'templates/website/langs.html');
this.templates["glossary"] = this.book.plugins.template("site:glossary") || path.resolve(this.options.theme, 'templates/website/glossary.html');
this.templates.page = this.book.plugins.template('site:page') || path.resolve(this.options.theme, 'templates/website/page.html');
this.templates.langs = this.book.plugins.template('site:langs') || path.resolve(this.options.theme, 'templates/website/langs.html');
this.templates.glossary = this.book.plugins.template('site:glossary') || path.resolve(this.options.theme, 'templates/website/glossary.html');
return Q();
};
@@ -80,7 +84,7 @@ Generator.prototype.prepareTemplateEngine = function() {
var language = that.book.config.normalizeLanguage();
if (!i18n.hasLocale(language)) {
that.book.log.warn.ln("Language '"+language+"' is not available as a layout locales (en, "+i18n.getLocales().join(", ")+")");
that.book.log.warn.ln('Language "'+language+'" is not available as a layout locales (en, '+i18n.getLocales().join(', ')+')');
}
var folders = _.chain(that.templates)
@@ -97,9 +101,9 @@ Generator.prototype.prepareTemplateEngine = function() {
);
// Add filter
that.env.addFilter("contentLink", that.book.contentLink.bind(that.book));
that.env.addFilter('contentLink', that.book.contentLink.bind(that.book));
that.env.addFilter('lvl', function(lvl) {
return lvl.split(".").length;
return lvl.split('.').length;
});
// Add extension
@@ -113,8 +117,7 @@ Generator.prototype.finish = function() {
return this.copyAssets()
.then(this.copyCover)
.then(this.writeGlossary)
.then(this.writeSearchIndex)
.then(this.writeLangsIndex)
.then(this.writeLangsIndex);
};
// Convert an input file
@@ -124,60 +127,48 @@ Generator.prototype.convertFile = function(input) {
return that.book.parsePage(input, {
convertImages: that.convertImages,
interpolateTemplate: function(page) {
return that.callHook("page:before", page);
return that.callHook('page:before', page);
},
interpolateContent: function(page) {
return that.callHook("page", page);
return that.callHook('page', page);
}
})
.then(function(page) {
var relativeOutput = that.book.contentPath(page.path);
var output = path.join(that.options.output, relativeOutput);
var basePath = path.relative(path.dirname(output), that.options.output) || ".";
var basePath = path.relative(path.dirname(output), that.options.output) || '.';
if (process.platform === 'win32') basePath = basePath.replace(/\\/g, '/');
that.book.log.debug.ln("write parsed file", page.path, "to", relativeOutput);
that.book.log.debug.ln('write parsed file', page.path, 'to', relativeOutput);
return that._writeTemplate(that.templates["page"], {
return that._writeTemplate(that.templates.page, {
progress: page.progress,
_input: page.path,
content: page.sections,
basePath: basePath,
staticBase: links.join(basePath, "gitbook")
staticBase: links.join(basePath, 'gitbook')
}, output);
});
};
// Write the index for langs
Generator.prototype.writeLangsIndex = function() {
var that = this;
if (!this.book.langs.length) return Q();
return this._writeTemplate(this.templates["langs"], {
return this._writeTemplate(this.templates.langs, {
langs: this.book.langs
}, path.join(this.options.output, "index.html"));
}, path.join(this.options.output, 'index.html'));
};
// Write glossary
Generator.prototype.writeGlossary = function() {
var that = this;
// No glossary
if (this.book.glossary.length == 0) return Q();
if (this.book.glossary.length === 0) return Q();
return this._writeTemplate(this.templates["glossary"], {}, path.join(this.options.output, "GLOSSARY.html"));
};
// Write the search index
Generator.prototype.writeSearchIndex = function() {
var that = this;
return fs.writeFile(
path.join(this.options.output, "search_index.json"),
JSON.stringify(this.book.searchIndex)
);
return this._writeTemplate(this.templates.glossary, {}, path.join(this.options.output, 'GLOSSARY.html'));
};
// Convert a page into a normalized data set
@@ -194,7 +185,7 @@ Generator.prototype.normalizePage = function(page) {
return Q()
.then(function() {
return _callHook("page");
return _callHook('page');
})
.then(function() {
return page;
@@ -207,7 +198,7 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
interpolate = interpolate || _.identity;
return Q()
.then(function(sections) {
.then(function() {
return that.env.render(
tpl,
_.extend({
@@ -222,6 +213,7 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
title: that.options.title,
description: that.options.description,
language: that.book.config.normalizeLanguage(),
innerlanguage: that.book.isSubBook()? that.book.config.get('language') : null,
glossary: that.book.glossary,
@@ -236,8 +228,8 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
options: that.options,
basePath: ".",
staticBase: path.join(".", "gitbook"),
basePath: '.',
staticBase: path.join('.', 'gitbook'),
'__': that.book.i18n.bind(that.book)
}, options)
@@ -258,15 +250,15 @@ Generator.prototype.copyAssets = function() {
// Copy gitbook assets
return fs.copy(
path.join(that.options.theme, "assets"),
path.join(that.options.output, "gitbook")
path.join(that.options.theme, 'assets/'+this.namespace),
path.join(that.options.output, 'gitbook')
)
// Copy plugins assets
.then(function() {
return Q.all(
_.map(that.book.plugins.list, function(plugin) {
var pluginAssets = path.join(that.options.output, "gitbook/plugins/", plugin.name);
var pluginAssets = path.join(that.options.output, 'gitbook/plugins/', plugin.name);
return plugin.copyAssets(pluginAssets, that.namespace);
})
);
+57 -52
View File
@@ -1,45 +1,50 @@
var Q = require("q");
var _ = require("lodash");
var path = require("path");
/* eslint no-console: 0 */
var Q = require('q');
var _ = require('lodash');
var path = require('path');
var tinylr = require('tiny-lr');
var color = require('bash-color');
var Book = require("./book");
var Plugin = require("./plugin");
var Server = require("./utils/server");
var stringUtils = require("./utils/string");
var watch = require("./utils/watch");
var logger = require("./utils/logger");
var Book = require('./book');
var initBook = require('./init');
var Server = require('./utils/server');
var stringUtils = require('./utils/string');
var watch = require('./utils/watch');
var logger = require('./utils/logger');
var LOG_OPTION = {
name: "log",
description: "Minimum log level to display",
name: 'log',
description: 'Minimum log level to display',
values: _.chain(logger.LEVELS).keys().map(stringUtils.toLowerCase).value(),
defaults: "info"
defaults: 'info'
};
var FORMAT_OPTION = {
name: "format",
description: "Format to build to",
values: ["website", "json", "ebook"],
defaults: "website"
name: 'format',
description: 'Format to build to',
values: ['website', 'json', 'ebook'],
defaults: 'website'
};
// Export init to gitbook library
Book.init = initBook;
module.exports = {
Book: Book,
LOG_LEVELS: logger.LEVELS,
commands: _.flatten([
{
name: "build [book] [output]",
description: "build a book",
name: 'build [book] [output]',
description: 'build a book',
options: [
FORMAT_OPTION,
LOG_OPTION
],
exec: function(args, kwargs) {
var input = args[0] || process.cwd();
var output = args[1] || path.join(input, "_book");
var output = args[1] || path.join(input, '_book');
var book = new Book(input, _.extend({}, {
'config': {
@@ -53,16 +58,16 @@ module.exports = {
return book.generate(kwargs.format);
})
.then(function(){
console.log("");
console.log(color.green("Done, without error"));
console.log('');
console.log(color.green('Done, without error'));
});
}
},
_.map(["pdf", "epub", "mobi"], function(ebookType) {
_.map(['pdf', 'epub', 'mobi'], function(ebookType) {
return {
name: ebookType+" [book] [output]",
description: "build a book to "+ebookType,
name: ebookType+' [book] [output]',
description: 'build a book to '+ebookType,
options: [
LOG_OPTION
],
@@ -81,30 +86,30 @@ module.exports = {
});
})
.then(function(){
console.log("");
console.log(color.green("Done, without error"));
console.log('');
console.log(color.green('Done, without error'));
});
}
};
}),
{
name: "serve [book]",
description: "Build then serve a gitbook from a directory",
name: 'serve [book]',
description: 'Build then serve a gitbook from a directory',
options: [
{
name: "port",
description: "Port for server to listen on",
name: 'port',
description: 'Port for server to listen on',
defaults: 4000
},
{
name: "lrport",
description: "Port for livereload server to listen on",
name: 'lrport',
description: 'Port for livereload server to listen on',
defaults: 35729
},
{
name: "watch",
description: "Enable/disable file watcher",
name: 'watch',
description: 'Enable/disable file watcher',
defaults: true
},
FORMAT_OPTION,
@@ -116,16 +121,16 @@ module.exports = {
// Init livereload server
var lrServer = tinylr({});
var lrPath = undefined;
var lrPath;
var generate = function() {
if (server.isRunning()) console.log("Stopping server");
if (server.isRunning()) console.log('Stopping server');
return server.stop()
.then(function() {
var book = new Book(input, _.extend({}, {
'config': {
'defaultsPlugins': ["livereload"]
'defaultsPlugins': ['livereload']
},
'logLevel': kwargs.log
}));
@@ -158,11 +163,11 @@ module.exports = {
.then(function(filepath) {
// set livereload path
lrPath = filepath;
console.log("Restart after change in files");
console.log('Restart after change in file', filepath);
console.log('');
return generate();
})
})
});
});
});
};
@@ -170,16 +175,16 @@ module.exports = {
.then(function() {
console.log('Live reload server started on port:', kwargs.lrport);
console.log('Press CTRL+C to quit ...');
console.log('')
console.log('');
return generate();
});
}
},
{
name: "install [book]",
description: "install plugins dependencies",
exec: function(args, kwargs) {
name: 'install [book]',
description: 'install plugins dependencies',
exec: function(args) {
var input = args[0] || process.cwd();
var book = new Book(input);
@@ -189,20 +194,20 @@ module.exports = {
return book.plugins.install();
})
.then(function(){
console.log("");
console.log(color.green("Done, without error"));
console.log('');
console.log(color.green('Done, without error'));
});
}
},
{
name: "init [directory]",
description: "create files and folders based on contents of SUMMARY.md",
exec: function(args, kwargs) {
return Book.init(args[0] || process.cwd())
name: 'init [directory]',
description: 'create files and folders based on contents of SUMMARY.md',
exec: function(args) {
return initBook(args[0] || process.cwd())
.then(function(){
console.log("");
console.log(color.green("Done, without error"));
console.log('');
console.log(color.green('Done, without error'));
});
}
}
+83
View File
@@ -0,0 +1,83 @@
var _ = require('lodash');
var Q = require('q');
var path = require('path');
var Book = require('./book');
var fs = require('./utils/fs');
// Initialize folder structure for a book
// Read SUMMARY to created the right chapter
function initBook(root, opts) {
var book = new Book(root, opts);
var extensionToUse = '.md';
var chaptersPaths = function(chapters) {
return _.reduce(chapters || [], function(accu, chapter) {
var o = {
title: chapter.title
};
if (chapter.path) o.path = chapter.path;
return accu.concat(
[o].concat(chaptersPaths(chapter.articles))
);
}, []);
};
book.log.info.ln('init book at', root);
return fs.mkdirp(root)
.then(function() {
book.log.info.ln('detect structure from SUMMARY (if it exists)');
return book.parseSummary();
})
.fail(function() {
return Q();
})
.then(function() {
var summary = book.summaryFile || 'SUMMARY.md';
var chapters = book.summary.chapters || [];
extensionToUse = path.extname(summary);
if (chapters.length === 0) {
chapters = [
{
title: 'Summary',
path: 'SUMMARY'+extensionToUse
},
{
title: 'Introduction',
path: 'README'+extensionToUse
}
];
}
return Q(chaptersPaths(chapters));
})
.then(function(chapters) {
// Create files that don't exist
return Q.all(_.map(chapters, function(chapter) {
if (!chapter.path) return Q();
var absolutePath = path.resolve(book.root, chapter.path);
return fs.exists(absolutePath)
.then(function(exists) {
if(exists) {
book.log.info.ln('found', chapter.path);
return;
} else {
book.log.info.ln('create', chapter.path);
}
return fs.mkdirp(path.dirname(absolutePath))
.then(function() {
return fs.writeFile(absolutePath, '# '+chapter.title+'\n');
});
});
}));
})
.then(function() {
book.log.info.ln('initialization is finished');
});
}
module.exports = initBook;
+82 -24
View File
@@ -1,16 +1,27 @@
var _ = require("lodash");
var Q = require("q");
var semver = require("semver");
var path = require("path");
var url = require("url");
var fs = require("./utils/fs");
var _ = require('lodash');
var Q = require('q');
var path = require('path');
var url = require('url');
var fs = require('./utils/fs');
var resolve = require('resolve');
var mergeDefaults = require('merge-defaults');
var jsonschema = require('jsonschema');
var jsonSchemaDefaults = require('json-schema-defaults');
var version = require('./version');
var PLUGIN_PREFIX = 'gitbook-plugin-';
// Return an absolute name for the plugin (the one on NPM)
function absoluteName(name) {
if (name.indexOf(PLUGIN_PREFIX) === 0) return name;
return [PLUGIN_PREFIX, name].join('');
}
var pkg = require("../package.json");
var Plugin = function(book, name) {
this.book = book;
this.name = name;
this.name = absoluteName(name);
this.packageInfos = {};
this.infos = {};
@@ -18,9 +29,8 @@ var Plugin = function(book, name) {
_.bindAll(this);
_.each([
"gitbook-plugin-"+name,
"gitbook-"+name,
name,
absoluteName(name),
name
], function(_name) {
// Load from the book
if (this.load(_name, book.root)) return false;
@@ -31,20 +41,27 @@ var Plugin = function(book, name) {
};
// Type of plugins resources
Plugin.RESOURCES = ["js", "css"];
Plugin.RESOURCES = ['js', 'css'];
Plugin.HOOKS = [
"init", "finish", "finish:before"
]
'init', 'finish', 'finish:before', 'config', 'page', 'page:before'
];
// Return the reduce name for the plugin
// "gitbook-plugin-test" -> "test"
// Return a relative name for the plugin (the one on GitBook)
Plugin.prototype.reducedName = function() {
return this.name.replace(PLUGIN_PREFIX, '');
};
// Load from a name
Plugin.prototype.load = function(name, baseDir) {
try {
var res = resolve.sync(name+"/package.json", { basedir: baseDir });
var res = resolve.sync(name+'/package.json', { basedir: baseDir });
this.baseDir = path.dirname(res);
this.packageInfos = require(res);
this.infos = require(resolve.sync(name, { basedir: baseDir }));
this.name = name;
this.name = this.packageInfos.name;
return true;
} catch (e) {
@@ -62,13 +79,13 @@ Plugin.prototype.normalizeResource = function(resource) {
// so we will simply link to using it's URL
if (parsed.protocol) {
return {
"url": resource
'url': resource
};
}
// This will be copied over from disk
// and shipped with the book's build
return { "path": this.name+"/"+resource };
return { 'path': this.name+'/'+resource };
};
// Return resources
@@ -77,7 +94,7 @@ Plugin.prototype._getResources = function(base) {
var book = this.infos[base];
// Compatibility with version 1.x.x
if (base == "website") book = book || this.infos["book"];
if (base == 'website') book = book || this.infos.book;
// Nothing specified, fallback to default
if (!book) {
@@ -85,7 +102,7 @@ Plugin.prototype._getResources = function(base) {
}
// Dynamic function
if(typeof book === "function") {
if(typeof book === 'function') {
// Call giving it the context of our book
return Q().then(book.bind(this.book));
}
@@ -127,18 +144,49 @@ Plugin.prototype.isValid = function() {
this.packageInfos.name &&
this.packageInfos.engines &&
this.packageInfos.engines.gitbook &&
semver.satisfies(pkg.version, this.packageInfos.engines.gitbook)
version.satisfies(this.packageInfos.engines.gitbook)
);
// Valid hooks
_.each(this.infos.hooks, function(hook, hookName) {
if (_.contains(Plugin.HOOKS, hookName)) return;
that.book.log.warn.ln("Hook '"+hookName+" 'used by plugin '"+that.packageInfos.name+"' has been removed or is deprecated");
that.book.log.warn.ln('Hook "'+hookName+'"" used by plugin "'+that.packageInfos.name+'" has been removed or is deprecated');
});
return isValid;
};
// Normalize, validate configuration for this plugin using its schema
// Throw an error when shcema is not respected
Plugin.prototype.validateConfig = function(config) {
var that = this;
return Q()
.then(function() {
var schema = that.packageInfos.gitbook || {};
if (!schema) return config;
// Normalize schema
schema.id = '/pluginsConfig.'+that.reducedName();
schema.type = 'object';
// Validate and throw if invalid
var v = new jsonschema.Validator();
var result = v.validate(config, schema, {
propertyName: 'pluginsConfig.'+that.reducedName()
});
// Throw error
if (result.errors.length > 0) {
throw new Error('Configuration Error: '+result.errors[0].stack);
}
// Insert default values
var defaults = jsonSchemaDefaults(schema);
return mergeDefaults(config, defaults);
});
};
// Resolve file path
Plugin.prototype.resolveFile = function(filename) {
return path.resolve(this.baseDir, filename);
@@ -154,8 +202,8 @@ Plugin.prototype.callHook = function(name, data) {
if (!hookFunc) return Q(data);
this.book.log.debug.ln("call hook", name);
if (!_.contains(Plugin.HOOKS, name)) this.book.log.warn.ln("hook '"+name+"' used by plugin '"+this.name+"' is deprecated, and will be removed in the coming versions");
this.book.log.debug.ln('call hook', name);
if (!_.contains(Plugin.HOOKS, name)) this.book.log.warn.ln('hook "'+name+'" used by plugin "'+this.name+'" is deprecated, and will be removed in the coming versions');
return Q()
.then(function() {
@@ -180,4 +228,14 @@ Plugin.prototype.copyAssets = function(out, base) {
}, _.constant(false));
};
// Get config from book
Plugin.prototype.getConfig = function() {
return this.book.config.get('pluginsConfig.'+this.reducedName(), {});
};
// Set configuration for this plugin
Plugin.prototype.setConfig = function(values) {
return this.book.config.set('pluginsConfig.'+this.reducedName(), values);
};
module.exports = Plugin;
+57 -52
View File
@@ -1,11 +1,11 @@
var _ = require("lodash");
var Q = require("q");
var _ = require('lodash');
var Q = require('q');
var npmi = require('npmi');
var npm = require('npm');
var semver = require('semver');
var Plugin = require("./plugin");
var pkg = require("../package.json");
var Plugin = require('./plugin');
var version = require('./version');
var initNPM = _.memoize(function() {
return Q.nfcall(npm.load, { silent: true, loglevel: 'silent' });
@@ -23,7 +23,7 @@ var PluginsList = function(book, plugins) {
this.failed = [];
// Namespaces
this.namespaces = _.chain(["website", "ebook"])
this.namespaces = _.chain(['website', 'ebook'])
.map(function(namespace) {
return [
namespace,
@@ -53,7 +53,7 @@ PluginsList.prototype.count = function() {
};
// Add and load a plugin
PluginsList.prototype.load = function(plugin, options) {
PluginsList.prototype.load = function(plugin) {
var that = this;
if (_.isArray(plugin)) {
@@ -66,7 +66,7 @@ PluginsList.prototype.load = function(plugin, options) {
if (_.isObject(plugin) && !(plugin instanceof Plugin)) plugin = plugin.name;
if (_.isString(plugin)) plugin = new Plugin(this.book, plugin);
that.log.info("load plugin", plugin.name, "....");
that.log.info('load plugin', plugin.name, '....');
if (!plugin.isValid()) {
that.log.info.fail();
that.failed.push(plugin.name);
@@ -78,38 +78,46 @@ PluginsList.prototype.load = function(plugin, options) {
that.list.push(plugin);
}
// Extract filters
_.each(plugin.getFilters(), function(filterFunc, filterName) {
that.book.template.addFilter(filterName, filterFunc);
});
return Q()
// Extract blocks
_.each(plugin.getBlocks(), function(block, blockName) {
that.book.template.addBlock(blockName, block);
});
// Validate and normalize configuration
.then(function() {
var config = plugin.getConfig();
return plugin.validateConfig(config);
})
.then(function(config) {
// Update configuration
plugin.setConfig(config);
return _.reduce(_.keys(that.namespaces), function(prev, namespaceName) {
return prev.then(function() {
return plugin.getResources(namespaceName)
.then(function(plResources) {
var namespace = that.namespaces[namespaceName];
// Extract filters
that.book.template.addFilters(plugin.getFilters());
// Extract js and css
_.each(Plugin.RESOURCES, function(resourceType) {
namespace.resources[resourceType] = (namespace.resources[resourceType] || []).concat(plResources[resourceType] || []);
// Extract blocks
that.book.template.addBlocks(plugin.getBlocks());
return _.reduce(_.keys(that.namespaces), function(prev, namespaceName) {
return prev.then(function() {
return plugin.getResources(namespaceName)
.then(function(plResources) {
var namespace = that.namespaces[namespaceName];
// Extract js and css
_.each(Plugin.RESOURCES, function(resourceType) {
namespace.resources[resourceType] = (namespace.resources[resourceType] || []).concat(plResources[resourceType] || []);
});
// Map of html resources by name added by each plugin
_.each(plResources.html || {}, function(value, tag) {
// Turn into function if not one already
if (!_.isFunction(value)) value = _.constant(value);
namespace.html[tag] = namespace.html[tag] || [];
namespace.html[tag].push(value);
});
});
// Map of html resources by name added by each plugin
_.each(plResources.html || {}, function(value, tag) {
// Turn into function if not one already
if (!_.isFunction(value)) value = _.constant(value);
namespace.html[tag] = namespace.html[tag] || [];
namespace.html[tag].push(value);
});
})
});
}, Q());
});
}, Q());
});
};
// Call a hook
@@ -139,7 +147,7 @@ PluginsList.prototype.html = function(namespace, tag, context, options) {
var htmlSnippets = this.namespaces[namespace].html[tag];
return _.map(htmlSnippets || [], function(code) {
return code.call(context, options);
}).join("\n");
}).join('\n');
};
// Return a resources map for a namespace
@@ -150,18 +158,17 @@ PluginsList.prototype.resources = function(namespace) {
// Install plugins from a book
PluginsList.prototype.install = function() {
var that = this;
var defaultsPlugins = _.pluck(that.book.options.defaultsPlugins)
// Remove defaults (no need to install)
var plugins = _.filter(that.book.options.plugins, function(plugin) {
return !_.contains(defaultsPlugins, plugin.name);
var plugins = _.reject(that.book.options.plugins, {
isDefault: true
});
// Install plugins one by one
that.book.log.info.ln(plugins.length+" plugins to install");
that.book.log.info.ln(plugins.length+' plugins to install');
return _.reduce(plugins, function(prev, plugin) {
return prev.then(function() {
var fullname = "gitbook-plugin-"+plugin.name;
var fullname = 'gitbook-plugin-'+plugin.name;
return Q()
@@ -169,10 +176,10 @@ PluginsList.prototype.install = function() {
.then(function() {
if (plugin.version) return plugin.version;
that.book.log.info.ln("No version specified, resolve plugin", plugin.name);
that.book.log.info.ln('No version specified, resolve plugin', plugin.name);
return initNPM()
.then(function() {
return Q.nfcall(npm.commands.view, [fullname+"@*", "engines"], true);
return Q.nfcall(npm.commands.view, [fullname+'@*', 'engines'], true);
})
.then(function(versions) {
return _.chain(versions)
@@ -180,16 +187,16 @@ PluginsList.prototype.install = function() {
.map(function(v) {
return {
version: v[0],
gitbook: (v[1].engines || {})["gitbook"]
}
gitbook: (v[1].engines || {}).gitbook
};
})
.filter(function(v) {
return v.gitbook && semver.satisfies(pkg.version, v.gitbook);
return v.gitbook && version.satisfies(v.gitbook);
})
.sort(function(v1, v2) {
return semver.lt(v1.version, v2.version)? 1 : -1;
})
.pluck("version")
.pluck('version')
.first()
.value();
});
@@ -198,10 +205,10 @@ PluginsList.prototype.install = function() {
// Install the plugin with the resolved version
.then(function(version) {
if (!version) {
throw "Found no satisfactory version for plugin "+plugin.name;
throw 'Found no satisfactory version for plugin '+plugin.name;
}
that.book.log.info.ln("install plugin", plugin.name, "from npm ("+fullname+") with version", version);
that.book.log.info.ln('install plugin', plugin.name, 'from npm ('+fullname+') with version', version);
return Q.nfcall(npmi, {
'name': fullname,
'version': version,
@@ -214,12 +221,10 @@ PluginsList.prototype.install = function() {
});
})
.then(function() {
that.book.log.info.ok("plugin", plugin.name, "installed with success");
that.book.log.info.ok('plugin', plugin.name, 'installed with success');
});
});
}, Q());
};
module.exports = PluginsList;
+160 -94
View File
@@ -1,57 +1,42 @@
var _ = require("lodash");
var Q = require("q");
var path = require("path");
var nunjucks = require("nunjucks");
var escapeStringRegexp = require("escape-string-regexp");
var _ = require('lodash');
var Q = require('q');
var path = require('path');
var nunjucks = require('nunjucks');
var parsers = require('gitbook-parsers');
var escapeStringRegexp = require('escape-string-regexp');
var git = require("./utils/git");
var fs = require("./utils/fs");
var batch = require("./utils/batch");
var pkg = require("../package.json");
var batch = require('./utils/batch');
var pkg = require('../package.json');
var defaultBlocks = require('./blocks');
var BookLoader = require('./conrefs_loader');
// The loader should handle relative and git url
var BookLoader = nunjucks.Loader.extend({
async: true,
init: function(book) {
this.book = book;
},
getSource: function(fileurl, callback) {
var that = this;
git.resolveFile(fileurl)
.then(function(filepath) {
// Is local file
if (!filepath) filepath = path.resolve(that.book.root, fileurl);
else that.book.log.debug.ln("resolve from git", fileurl, "to", filepath)
// Read file from absolute path
return fs.readFile(filepath)
.then(function(source) {
return {
src: source.toString(),
path: filepath
}
});
})
.nodeify(callback);
},
resolve: function(from, to) {
return path.resolve(path.dirname(from), to);
}
});
// Normalize result from a block
function normBlockResult(blk) {
if (_.isString(blk)) blk = { body: blk };
return blk;
}
var TemplateEngine = function(book) {
var that = this;
this.book = book;
this.log = this.book.log;
// Template loader
this.loader = new BookLoader(book, {
// Replace shortcuts in imported files
interpolate: function(filepath, source) {
var parser = parsers.get(path.extname(filepath));
var type = parser? parser.name : null;
return that.applyShortcuts(type, source);
}
});
// Nunjucks env
this.env = new nunjucks.Environment(
new BookLoader(book),
this.loader,
{
// Escaping is done after by the markdown parser
autoescape: false,
@@ -71,49 +56,48 @@ var TemplateEngine = function(book) {
// List of tags shortcuts
this.shortcuts = [];
// Map of blocks
// Map of blocks bodies (that requires post-processing)
this.blockBodies = {};
// Map of added blocks
this.blocks = {};
// Bind methods
_.bindAll(this);
// Default block "html" that return html not parsed
this.addBlock("html", {
process: _.identity
});
// Add default blocks
this.addBlocks(defaultBlocks);
};
// Process a block in a context
// Process the result of block in a context
TemplateEngine.prototype.processBlock = function(blk) {
if (_.isString(blk)) blk = { body: blk };
blk = _.defaults(blk, {
parse: false,
post: undefined
});
blk.id = _.uniqueId("blk");
blk.id = _.uniqueId('blk');
var toAdd = (!blk.parse) || (blk.post != undefined);
var toAdd = (!blk.parse) || (blk.post !== undefined);
// Add to global map
if (toAdd) this.blocks[blk.id] = blk;
if (toAdd) this.blockBodies[blk.id] = blk;
//Parsable block, just return it
// Parsable block, just return it
if (blk.parse) {
return blk.body;
}
// Return it as a macro
return "%+%"+blk.id+"%+%";
// Return it as a position marker
return '@%@'+blk.id+'@%@';
};
// Replace blocks by body after processing
// This is done to avoid that markdown processer parse the block content
// 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.blocks[key];
return content.replace(/\@\%\@([\s\S]+?)\@\%\@/g, function(match, key) {
var blk = that.blockBodies[key];
if (!blk) return match;
var body = blk.body;
@@ -141,11 +125,13 @@ TemplateEngine.prototype.bindContext = function(func) {
TemplateEngine.prototype.addFilter = function(filterName, func) {
try {
this.env.getFilter(filterName);
this.log.warn.ln("conflict in filters, '"+filterName+"' is already set");
this.log.warn.ln('conflict in filters, \''+filterName+'\' is already set');
return false;
} catch(e) {}
} catch(e) {
// Filter doesn't exist
}
this.log.debug.ln("add filter '"+filterName+"'");
this.log.debug.ln('add filter \''+filterName+'\'');
this.env.addFilter(filterName, this.bindContext(function() {
var ctx = this;
var args = Array.prototype.slice.apply(arguments);
@@ -160,29 +146,65 @@ TemplateEngine.prototype.addFilter = function(filterName, func) {
return true;
};
// Add multiple filters
TemplateEngine.prototype.addFilters = function(filters) {
_.each(filters, function(filter, name) {
this.addFilter(name, filter);
}, this);
};
// Return nunjucks extension name of a block
TemplateEngine.prototype.blockExtName = function(name) {
return 'Block'+name+'Extension';
};
// Test if a block is defined
TemplateEngine.prototype.hasBlock = function(name) {
return this.env.hasExtension(this.blockExtName(name));
};
// Remove a block
TemplateEngine.prototype.removeBlock = function(name) {
if (!this.hasBlock(name)) return;
// Remove nunjucks extension
this.env.removeExtension(this.blockExtName(name));
// Cleanup shortcuts
this.shortcuts = _.reject(this.shortcuts, {
block: name
});
};
// Add a block
TemplateEngine.prototype.addBlock = function(name, block) {
var that = this;
var that = this, Ext, extName;
if (_.isFunction(block)) block = { process: block };
block = _.defaults(block || {}, {
shortcuts: [],
end: "end"+name,
end: 'end'+name,
process: _.identity,
blocks: []
});
var extName = 'Block'+name+'Extension';
if (this.env.getExtension(extName)) {
this.log.warn.ln("conflict in blocks, '"+name+"' is already defined");
return false;
extName = this.blockExtName(name);
if (this.hasBlock(name) && !defaultBlocks[name]) {
this.log.warn.ln('conflict in blocks, \''+name+'\' is already defined');
}
this.log.debug.ln("add block '"+name+"'");
// Cleanup previous block
this.removeBlock(name);
var Ext = function () {
this.log.debug.ln('add block \''+name+'\'');
this.blocks[name] = block;
Ext = function () {
this.tags = [name];
this.parse = function(parser, nodes, lexer) {
this.parse = function(parser, nodes) {
var body = null;
var lastBlockName = null;
var lastBlockArgs = null;
@@ -223,7 +245,7 @@ TemplateEngine.prototype.addBlock = function(name, block) {
var bodies = [body];
_.each(block.blocks, function(blockName) {
subbodies[blockName] = subbodies[blockName] || [];
if (subbodies[blockName].length == 0) {
if (subbodies[blockName].length === 0) {
subbodies[blockName].push({
args: new nodes.NodeList(),
body: new nodes.NodeList()
@@ -250,7 +272,7 @@ TemplateEngine.prototype.addBlock = function(name, block) {
// Extract main body and kwargs
var body = args.pop();
var kwargs = args.pop() || {};
var kwargs = _.isObject(_.last(args))? args.pop() : {};
// Extract blocks body
var _blocks = _.map(block.blocks, function(blockName, i){
@@ -260,33 +282,31 @@ TemplateEngine.prototype.addBlock = function(name, block) {
};
});
var func = that.bindContext(block.process);
Q()
.then(function() {
return func.call(context, {
return that.applyBlock(name, {
body: body(),
args: args,
kwargs: kwargs,
blocks: _blocks
});
}, context);
})
// process the block returned
.then(that.processBlock)
.nodeify(callback)
.nodeify(callback);
};
};
// Add the Extension
this.env.addExtension(extName, new Ext());
// Add shortcuts
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.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,
@@ -298,24 +318,66 @@ TemplateEngine.prototype.addBlock = function(name, block) {
}, this);
};
// Add multiple blocks
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 (Q.isPromise(r)) return r.then(normBlockResult);
else return normBlockResult(r);
};
// Apply a shortcut to a string
TemplateEngine.prototype._applyShortcut = function(parser, content, shortcut) {
if (!_.contains(shortcut.parsers, parser)) return content;
var regex = new RegExp(
escapeStringRegexp(shortcut.start) + "([\\s\\S]*?[^\\$])" + escapeStringRegexp(shortcut.end),
escapeStringRegexp(shortcut.start) + '([\\s\\S]*?[^\\$])' + escapeStringRegexp(shortcut.end),
'g'
);
return content.replace(regex, function(all, match) {
return "{% "+shortcut.tag.start+" %}"+ match + "{% "+shortcut.tag.end+" %}";
return '{% '+shortcut.tag.start+' %}'+ match + '{% '+shortcut.tag.end+' %}';
});
};
// Apply all shortcuts to some template string
TemplateEngine.prototype.applyShortcuts = function(type, content) {
return _.reduce(this.shortcuts, _.partial(this._applyShortcut.bind(this), type), content);
};
// Render a string from the book
TemplateEngine.prototype.renderString = function(content, context, options) {
var context = _.extend({}, context, {
context = _.extend({}, context, {
// Variables from book.json
book: this.book.options.variables,
// Complete book.json
config: this.book.options,
// infos about gitbook
gitbook: {
version: pkg.version,
@@ -327,22 +389,26 @@ TemplateEngine.prototype.renderString = function(content, context, options) {
type: null
});
if (options.path) options.path = this.book.resolve(options.path);
if (!options.type && options.path) {
var parser = parsers.get(path.extname(options.path));
options.type = parser? parser.name : null;
}
// Replace shortcuts
content = _.reduce(this.shortcuts, _.partial(this._applyShortcut.bind(this), options.type), content);
content = this.applyShortcuts(options.type, content);
return Q.nfcall(this.env.renderString.bind(this.env), content, context, options)
.fail(function(err) {
if (_.isString(err)) err = new Error(err);
err.message = err.message.replace(/^Error: /, "");
err.message = err.message.replace(/^Error: /, '');
throw err;
});
};
// Render a file from the book
TemplateEngine.prototype.renderFile = function(filename, options) {
var that = this, context;
TemplateEngine.prototype.renderFile = function(filename) {
var that = this;
return that.book.readFile(filename)
.then(function(content) {
@@ -354,11 +420,11 @@ TemplateEngine.prototype.renderFile = function(filename, options) {
// Render a page from the book
TemplateEngine.prototype.renderPage = function(page) {
var that = this, context;
var that = this;
return that.book.statFile(page.path)
.then(function(stat) {
context = {
var context = {
// infos about the file
file: {
path: page.path,
@@ -380,7 +446,7 @@ TemplateEngine.prototype.postProcess = function(content) {
return Q(content)
.then(that.replaceBlocks)
.then(function(_content) {
return batch.execEach(that.blocks, {
return batch.execEach(that.blockBodies, {
max: 20,
fn: function(blk, blkId) {
return Q()
@@ -389,7 +455,7 @@ TemplateEngine.prototype.postProcess = function(content) {
return blk.post();
})
.then(function() {
delete that.blocks[blkId];
delete that.blockBodies[blkId];
});
}
})
+4 -4
View File
@@ -3,12 +3,12 @@ var _ = require("lodash");
// Execute a method for all element
function execEach(items, options) {
if (_.size(items) == 0) return Q();
if (_.size(items) === 0) return Q();
var concurrents = 0, d = Q.defer(), pending = [];
options = _.defaults(options || {}, {
max: 100,
fn: function(item) {}
fn: function() {}
});
@@ -29,7 +29,7 @@ function execEach(items, options) {
// Next pending
var next = pending.shift();
if (concurrents == 0 && !next) {
if (concurrents === 0 && !next) {
d.resolve();
} else if (next) {
startItem.apply(null, next);
@@ -38,7 +38,7 @@ function execEach(items, options) {
.fail(function(err) {
pending = [];
d.reject(err);
})
});
}
_.each(items, startItem);
-36
View File
@@ -1,36 +0,0 @@
var hljs = require('highlight.js');
var MAP = {
'py': 'python',
'js': 'javascript',
'json': 'javascript',
'rb': 'ruby',
'csharp': 'cs',
};
function normalize(lang) {
if(!lang) { return null; }
var lower = lang.toLowerCase();
return MAP[lower] || lower;
}
function highlight(lang, code) {
if(!lang) return code;
// Normalize lang
lang = normalize(lang);
try {
return hljs.highlight(lang, code).value;
} catch(e) { }
return code;
}
// Exports
module.exports = {
highlight: highlight,
normalize: normalize,
MAP: MAP
};
+31 -16
View File
@@ -1,18 +1,18 @@
var _ = require("lodash");
var Q = require("q");
var tmp = require("tmp");
var path = require("path");
var fs = require("graceful-fs");
var fsExtra = require("fs-extra");
var Ignore = require("fstream-ignore");
var _ = require('lodash');
var Q = require('q');
var tmp = require('tmp');
var path = require('path');
var fs = require('graceful-fs');
var fsExtra = require('fs-extra');
var Ignore = require('fstream-ignore');
var fsUtils = {
tmp: {
file: function(opt) {
return Q.nfcall(tmp.file.bind(tmp), opt).get(0)
return Q.nfcall(tmp.file.bind(tmp), opt).get(0);
},
dir: function() {
return Q.nfcall(tmp.dir.bind(tmp)).get(0)
return Q.nfcall(tmp.dir.bind(tmp)).get(0);
}
},
list: listFiles,
@@ -30,18 +30,19 @@ var fsUtils = {
fs.exists(path, d.resolve);
return d.promise;
},
findFile: findFile,
existsSync: fs.existsSync.bind(fs),
readFileSync: fs.readFileSync.bind(fs),
clean: cleanFolder,
getUniqueFilename: getUniqueFilename,
}
getUniqueFilename: getUniqueFilename
};
// Write a file
function writeFile(filename, data, options) {
var d = Q.defer();
try {
fs.writeFileSync(filename, data, options)
fs.writeFileSync(filename, data, options);
} catch(err) {
d.reject(err);
}
@@ -64,6 +65,10 @@ function writeStream(filename, st) {
d.reject(err);
});
st.on('error', function(err) {
d.reject(err);
});
st.pipe(wstream);
return d.promise;
@@ -73,7 +78,7 @@ function writeStream(filename, st) {
function getUniqueFilename(base, filename) {
if (!filename) {
filename = base;
base = "/";
base = '/';
}
filename = path.resolve(base, filename);
@@ -83,9 +88,8 @@ function getUniqueFilename(base, filename) {
var _filename = filename+ext;
var i = 0;
while (1) {
if (!fs.existsSync(filename)) break;
_filename = filename+"_"+i+ext;
while (fs.existsSync(filename)) {
_filename = filename+'_'+i+ext;
i = i + 1;
}
@@ -175,4 +179,15 @@ function cleanFolder(root) {
});
}
// Find a file in a folder (case incensitive)
// Return the real filename
function findFile(root, filename) {
return Q.nfcall(fs.readdir, root)
.then(function(files) {
return _.find(files, function(file) {
return (file.toLowerCase() == filename.toLowerCase());
});
});
}
module.exports = fsUtils;
+39 -24
View File
@@ -1,15 +1,14 @@
var Q = require("q");
var _ = require("lodash");
var url = require("url");
var tmp = require("tmp");
var path = require("path");
var crc = require("crc");
var exec = Q.denodeify(require("child_process").exec);
var URI = require("URIjs");
var Q = require('q');
var _ = require('lodash');
var path = require('path');
var crc = require('crc');
var exec = Q.denodeify(require('child_process').exec);
var URI = require('urijs');
var pathUtil = require('./path');
var fs = require("./fs");
var fs = require('./fs');
var GIT_PREFIX = "git+";
var GIT_PREFIX = 'git+';
var GIT_TMP = null;
@@ -31,20 +30,20 @@ function parseGitUrl(giturl) {
giturl = giturl.slice(GIT_PREFIX.length);
uri = new URI(giturl);
ref = uri.fragment() || "master";
ref = uri.fragment() || 'master';
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);
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");
uri.path(_.first(fileParts)+'.git');
return {
host: uri.toString(),
ref: ref || "master",
ref: ref || 'master',
filepath: filepath
};
}
@@ -53,7 +52,7 @@ function parseGitUrl(giturl) {
function cloneGitRepo(host, ref) {
var isBranch = false;
ref = ref || "master";
ref = ref || 'master';
if (!validateSha(ref)) isBranch = true;
return Q()
@@ -70,7 +69,7 @@ function cloneGitRepo(host, ref) {
// Return or clone the git repo
.then(function() {
// Unique ID for repo/ref combinaison
var repoId = crc.crc32(host+"#"+ref).toString(16);
var repoId = crc.crc32(host+'#'+ref).toString(16);
// Absolute path to the folder
var repoPath = path.resolve(GIT_TMP, repoId);
@@ -80,16 +79,15 @@ function cloneGitRepo(host, ref) {
if (doExists) return;
// Clone repo
return exec("git clone "+host+" "+repoPath)
return exec('git clone '+host+' '+repoPath)
.then(function() {
return exec("git checkout "+ref, { cwd: repoPath });
})
return exec('git checkout '+ref, { cwd: repoPath });
});
})
.thenResolve(repoPath);
});
}
// Get file from a git repo
function resolveFileFromGit(giturl) {
if (_.isString(giturl)) giturl = parseGitUrl(giturl);
@@ -102,11 +100,28 @@ function resolveFileFromGit(giturl) {
// Resolve relative path
return path.resolve(repo, giturl.filepath);
});
};
}
// Return root of git repo from a filepath
function resolveGitRoot(filepath) {
var relativeToGit, repoId;
// No git repo cloned, or file is not in a git repository
if (!GIT_TMP || !pathUtil.isInRoot(GIT_TMP, filepath)) return null;
// Extract first directory (is the repo id)
relativeToGit = path.relative(GIT_TMP, filepath);
repoId = _.first(relativeToGit.split(path.sep));
if (!repoId) return;
// Return an absolute file
return path.resolve(GIT_TMP, repoId);
}
module.exports = {
checkUrl: checkGitUrl,
parseUrl: parseGitUrl,
resolveFile: resolveFileFromGit
resolveFile: resolveFileFromGit,
resolveRoot: resolveGitRoot
};
+23 -23
View File
@@ -1,13 +1,13 @@
var _ = require("lodash");
var path = require("path");
var fs = require("fs");
var _ = require('lodash');
var path = require('path');
var fs = require('fs');
var i18n = require("i18n");
var i18n = require('i18n');
var I18N_PATH = path.resolve(__dirname, "../../theme/i18n/")
var DEFAULT_LANGUAGE = "en";
var I18N_PATH = path.resolve(__dirname, '../../theme/i18n/');
var DEFAULT_LANGUAGE = 'en';
var LOCALES = _.map(fs.readdirSync(I18N_PATH), function(lang) {
return path.basename(lang, ".json");
return path.basename(lang, '.json');
});
i18n.configure({
@@ -17,18 +17,18 @@ i18n.configure({
updateFiles: false
});
var compareLocales = function(lang, locale) {
var langMain = _.first(lang.split("-"));
var langSecond = _.last(lang.split("-"));
function compareLocales(lang, locale) {
var langMain = _.first(lang.split('-'));
var langSecond = _.last(lang.split('-'));
var localeMain = _.first(locale.split("-"));
var localeSecond = _.last(locale.split("-"));
var localeMain = _.first(locale.split('-'));
var localeSecond = _.last(locale.split('-'));
if (locale == lang) return 100;
if (localeMain == langMain) return 50;
if (localeSecond == langSecond) return 20;
return 0;
};
}
var normalizeLanguage = _.memoize(function(lang) {
var language = _.chain(LOCALES)
@@ -37,37 +37,37 @@ var normalizeLanguage = _.memoize(function(lang) {
return {
locale: locale,
score: compareLocales(lang, locale)
}
};
})
.filter(function(lang) {
return lang.score > 0;
})
.sortBy("score")
.pluck("locale")
.sortBy('score')
.pluck('locale')
.last()
.value();
return language || lang;
});
var translate = function(locale, phrase) {
function translate(locale, phrase) {
var args = Array.prototype.slice.call(arguments, 2);
return i18n.__.apply({}, [{
locale: locale,
phrase: phrase
}].concat(args));
};
}
var getCatalog = function(locale) {
function getCatalog(locale) {
locale = normalizeLanguage(locale);
return i18n.getCatalog(locale);
};
}
var getLocales = function() {
function getLocales() {
return LOCALES;
};
}
var hasLocale = function(locale) {
function hasLocale(locale) {
return _.contains(LOCALES, locale);
}
+5 -7
View File
@@ -1,9 +1,7 @@
var _ = require("lodash");
var Q = require("q");
var fs = require("./fs");
var spawn = require('spawn-cmd').spawn;
var links = require("./links");
var spawn = require("spawn-cmd").spawn;
// Convert a svg file
var convertSVG = function(source, dest, options) {
@@ -14,16 +12,16 @@ var convertSVG = function(source, dest, options) {
});
//var command = shellescape(['svgexport', source, dest]);
var child = spawn('svgexport', [source, dest]);
//var command = shellescape(["svgexport", source, dest]);
var child = spawn("svgexport", [source, dest]);
child.on("error", function(error) {
if (error.code == "ENOENT") error = new Error("Need to install 'svgexport' using 'npm install svgexport -g'");
if (error.code == "ENOENT") error = new Error("Need to install \"svgexport\" using \"npm install svgexport -g\"");
return d.reject(error);
});
child.on("close", function(code) {
if (code == 0 && fs.existsSync(dest)) {
if (code === 0 && fs.existsSync(dest)) {
d.resolve();
} else {
d.reject(new Error("Error converting "+source+" into "+dest));
+4 -4
View File
@@ -1,5 +1,5 @@
var url = require('url');
var path = require('path');
var url = require("url");
var path = require("path");
// Is the link an external link
var isExternal = function(href) {
@@ -33,7 +33,7 @@ var isAnchor = function(href) {
// Normalize a path to be a link
var normalizeLink = function(s) {
return s.replace(/\\/g, '/');
return s.replace(/\\/g, "/");
};
// Relative to absolute path
@@ -43,7 +43,7 @@ var normalizeLink = function(s) {
var toAbsolute = function(_href, dir, outdir) {
if (isExternal(_href)) return _href;
// Path '_href' inside the base folder
// Path "_href" inside the base folder
var hrefInRoot = path.normalize(path.join(dir, _href));
if (_href[0] == "/") hrefInRoot = path.normalize(_href.slice(1));
+9 -9
View File
@@ -1,6 +1,6 @@
var _ = require('lodash');
var util = require('util');
var color = require('bash-color');
var _ = require("lodash");
var util = require("util");
var color = require("bash-color");
var LEVELS = {
DEBUG: 0,
@@ -19,7 +19,7 @@ var COLORS = {
module.exports = function(_write, logLevel) {
var logger = {};
var lastChar = '\n';
var lastChar = "\n";
if (_.isString(logLevel)) logLevel = LEVELS[logLevel.toUpperCase()];
// Write a simple message
@@ -47,14 +47,14 @@ module.exports = function(_write, logLevel) {
var args = Array.prototype.slice.apply(arguments, [1]);
var msg = logger.format.apply(logger, args);
if (lastChar == '\n') {
if (lastChar == "\n") {
msg = COLORS[levelKey](levelKey.toLowerCase()+":")+" "+msg;
}
return logger.write(msg);
};
logger.logLn = function() {
if (lastChar != '\n') logger.write("\n");
if (lastChar != "\n") logger.write("\n");
var args = Array.prototype.slice.apply(arguments);
args.push("\n");
@@ -66,7 +66,7 @@ module.exports = function(_write, logLevel) {
var args = Array.prototype.slice.apply(arguments, [1]);
var msg = logger.format.apply(logger, args);
if (arguments.length > 1) {
logger.logLn(level, color.green('>> ') + msg.trim().replace(/\n/g, color.green('\n>> ')));
logger.logLn(level, color.green(">> ") + msg.trim().replace(/\n/g, color.green("\n>> ")));
} else {
logger.log(level, color.green("OK"), "\n");
}
@@ -77,7 +77,7 @@ module.exports = function(_write, logLevel) {
return logger.log(level, color.red("ERROR")+"\n");
};
_.each(_.omit(LEVELS, 'DISABLED'), function(level, levelKey) {
_.each(_.omit(LEVELS, "DISABLED"), function(level, levelKey) {
levelKey = levelKey.toLowerCase();
logger[levelKey] = _.partial(logger.log, level);
@@ -93,7 +93,7 @@ module.exports = function(_write, logLevel) {
logger[levelKey].fail();
throw err;
});
}
};
});
return logger;
+6 -9
View File
@@ -1,9 +1,9 @@
var _ = require('lodash');
var _ = require("lodash");
// Cleans up an article/chapter object
// remove 'articles' attributes
// remove "articles" attributes
function clean(obj) {
return obj && _.omit(obj, ['articles']);
return obj && _.omit(obj, ["articles"]);
}
function flattenChapters(chapters) {
@@ -27,7 +27,7 @@ function navigation(summary, files) {
files = _.isArray(files) ? files : (_.isString(files) ? [files] : null);
// List of all navNodes
// Flatten chapters, then add in default README node if needed etc ...
// Flatten chapters
var navNodes = flattenChapters(summary.chapters);
// Mapping of prev/next for a give path
@@ -39,8 +39,7 @@ function navigation(summary, files) {
if(!current.exists) return null;
// Find prev
prev = _.chain(navNodes)
.slice(0, i)
prev = _.chain(navNodes.slice(0, i))
.reverse()
.find(function(node) {
return node.exists && !node.external;
@@ -48,14 +47,12 @@ function navigation(summary, files) {
.value();
// Find next
next = _.chain(navNodes)
.slice(i+1)
next = _.chain(navNodes.slice(i+1))
.find(function(node) {
return node.exists && !node.external;
})
.value();
return [current.path, {
index: i,
title: current.title,
+102 -83
View File
@@ -5,26 +5,28 @@ var path = require('path');
var cheerio = require('cheerio');
var domSerializer = require('dom-serializer');
var request = require('request');
var crc = require("crc");
var crc = require('crc');
var slug = require('github-slugid');
var links = require('./links');
var imgUtils = require('./images');
var fs = require('./fs');
var batch = require('./batch');
var code = require('./code');
var parsableExtensions = require('gitbook-parsers').extensions;
// Map of images that have been converted
var imgConversionCache = {};
// Render a cheerio dom as html
var renderDom = function($, dom, options) {
function renderDom($, dom, options) {
if (!dom && $._root && $._root.children) {
dom = $._root.children;
}
options = options|| dom.options || $._options;
return domSerializer(dom, options);
};
// Map of images that have been converted
var imgConversionCache = {};
}
function replaceText($, el, search, replace, text_only ) {
return $(el).each(function(){
@@ -39,7 +41,8 @@ function replaceText($, el, search, replace, text_only ) {
if ( node ) {
// Loop over all childNodes.
do {
while (node) {
// Only process text nodes.
if ( node.nodeType === 3 ) {
@@ -67,17 +70,18 @@ function replaceText($, el, search, replace, text_only ) {
}
}
} while ( node = node.nextSibling );
node = node.nextSibling;
}
}
// Time to remove those elements!
remove.length && $(remove).remove();
if (remove.length) $(remove).remove();
});
};
}
function pregQuote( str ) {
return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
};
return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, '\\$1');
}
// Adapt an html snippet to be relative to a base folder
@@ -98,23 +102,31 @@ function normalizeHtml(src, options) {
// Find svg images to extract and process
if (options.convertImages) {
$("svg").each(function() {
$('svg').each(function() {
var content = renderDom($, $(this));
var svgId = _.uniqueId("svg");
var dest = svgId+".svg";
var svgId = _.uniqueId('svg');
var dest = svgId+'.svg';
// Generate filename
dest = "/"+fs.getUniqueFilename(outputRoot, dest);
dest = '/'+fs.getUniqueFilename(outputRoot, dest);
svgContent[dest] = '<?xml version="1.0" encoding="UTF-8"?>'+content;
$(this).replaceWith($("<img>").attr("src", dest));
$(this).replaceWith($('<img>').attr('src', dest));
});
}
// Generate ID for headings
$('h1,h2,h3,h4,h5,h6').each(function() {
if ($(this).attr('id')) return;
$(this).attr('id', slug($(this).text()));
});
// Find images to normalize
$("img").each(function() {
var origin = undefined;
var src = $(this).attr("src");
$('img').each(function() {
var origin;
var src = $(this).attr('src');
if (!src) return;
var isExternal = links.isExternal(src);
@@ -128,13 +140,13 @@ function normalizeHtml(src, options) {
// If image is external and ebook, then downlaod the images
if (isExternal) {
origin = src;
src = "/"+crc.crc32(origin).toString(16)+path.extname(origin);
src = '/'+crc.crc32(origin).toString(16)+path.extname(url.parse(origin).pathname);
src = links.toAbsolute(src, options.base, options.output);
isExternal = false;
}
var ext = path.extname(src);
var srcAbs = links.join("/", options.base, src);
var srcAbs = links.join('/', options.base, src);
// Test image extension
if (_.contains(imgUtils.INVALID, ext)) {
@@ -143,29 +155,29 @@ function normalizeHtml(src, options) {
src = imgConversionCache[outputRoot][srcAbs];
} else {
// Not converted yet
var dest = "";
var dest = '';
// Replace extension
dest = links.join(path.dirname(srcAbs), path.basename(srcAbs, ext)+".png");
dest = dest[0] == "/"? dest.slice(1) : dest;
dest = links.join(path.dirname(srcAbs), path.basename(srcAbs, ext)+'.png');
dest = dest[0] == '/'? dest.slice(1) : dest;
// Get a name that doesn't exists
dest = fs.getUniqueFilename(outputRoot, dest);
options.book.log.debug.ln("detect invalid image (will be converted to png):", srcAbs);
options.book.log.debug.ln('detect invalid image (will be converted to png):', srcAbs);
// Add to cache
imgConversionCache[outputRoot][srcAbs] = "/"+dest;
imgConversionCache[outputRoot][srcAbs] = '/'+dest;
// Push to convert
toConvert.push({
origin: origin,
content: svgContent[srcAbs],
source: isExternal? srcAbs : path.join("./", srcAbs),
dest: path.join("./", dest)
source: isExternal? srcAbs : path.join('./', srcAbs),
dest: path.join('./', dest)
});
src = links.join("/", dest);
src = links.join('/', dest);
}
// Reset as relative to output
@@ -176,17 +188,17 @@ function normalizeHtml(src, options) {
// Need to downlaod image
toConvert.push({
origin: origin,
source: path.join("./", srcAbs)
source: path.join('./', srcAbs)
});
}
}
$(this).attr("src", src);
$(this).attr('src', src);
});
// Normalize links
$("a").each(function() {
var href = $(this).attr("href");
$('a').each(function() {
var href = $(this).attr('href');
if (!href) return;
if (links.isAnchor(href)) {
@@ -194,59 +206,75 @@ function normalizeHtml(src, options) {
} else if (links.isRelative(href)) {
var parts = url.parse(href);
var absolutePath = links.join(options.base, parts.pathname);
var anchor = parts.hash || "";
var anchor = parts.hash || '';
// If is in navigation relative: transform as content
if (options.navigation[absolutePath]) {
absolutePath = options.book.contentLink(absolutePath);
}
// If md/adoc/rst files is not in summary
// or for ebook, signal all files that are outside the summary
else if (_.contains(parsableExtensions, path.extname(absolutePath)) ||
_.contains(['epub', 'pdf', 'mobi'], options.book.options.generator)) {
options.book.log.warn.ln('page', options.input, 'contains an hyperlink to resource outside spine \''+href+'\'');
}
// Transform as absolute
href = links.toAbsolute("/"+absolutePath, options.base, options.output)+anchor;
href = links.toAbsolute('/'+absolutePath, options.base, options.output)+anchor;
} else {
// External links
$(this).attr("target", "_blank");
$(this).attr('target', '_blank');
}
// Transform extension
$(this).attr("href", href);
$(this).attr('href', href);
});
// Highlight code blocks
$("code").each(function() {
// Extract language
$('code').each(function() {
// Normalize language
var lang = _.chain(
($(this).attr("class") || "").split(" ")
($(this).attr('class') || '').split(' ')
)
.map(function(cl) {
if (cl.search("lang-") === 0) return cl.slice("lang-".length);
// 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();
if (lang) {
var html = code.highlight(
lang,
$(this).text()
);
var source = $(this).text();
var blk = options.book.template.applyBlock('code', {
body: source,
kwargs: {
language: lang
}
});
$(this).html(html);
}
if (blk.html === false) $(this).text(blk.body);
else $(this).html(blk.body);
});
// Replace glossary terms
var glossary = _.sortBy(options.glossary, function(term) {
return -term.name.length;
});
_.each(glossary, function(term) {
var r = new RegExp( "\\b(" + pregQuote(term.name.toLowerCase()) + ")\\b" , 'gi' );
var r = new RegExp( '\\b(' + pregQuote(term.name.toLowerCase()) + ')\\b' , 'gi' );
var includedInFiles = false;
$("*").each(function() {
$('*').each(function() {
// Ignore codeblocks
if (_.contains(["code", "pre", "a"], this.name.toLowerCase())) return;
if (_.contains(['code', 'pre', 'a', 'script'], this.name.toLowerCase())) return;
replaceText($, this, r, function(match) {
// Add to files index in glossary
@@ -255,7 +283,7 @@ function normalizeHtml(src, options) {
term.files = term.files || [];
term.files.push(options.navigation[options.input]);
}
return "<a href='"+links.toAbsolute("/GLOSSARY.html", options.base, options.output)+"#"+term.id+"' class='glossary-term' title='"+_.escape(term.description)+"'>"+match+"</a>";
return '<a href=\''+links.toAbsolute('/GLOSSARY.html', options.base, options.output) + '#' + term.id+'\' class=\'glossary-term\' title=\''+_.escape(term.description)+'\'>'+match+'</a>';
});
});
});
@@ -264,14 +292,14 @@ function normalizeHtml(src, options) {
html: renderDom($),
images: toConvert
};
};
}
// Convert svg images to png
function convertImages(images, options) {
if (!options.convertImages) return Q();
var downloaded = [];
options.book.log.debug.ln("convert ", images.length, "images to png");
options.book.log.debug.ln('convert ', images.length, 'images to png');
return batch.execEach(images, {
max: 100,
@@ -283,9 +311,15 @@ function convertImages(images, options) {
// Write image if need to be download
.then(function() {
if (!image.origin && !_.contains(downloaded, image.origin)) return;
options.book.log.debug("download image", image.origin, "...");
options.book.log.debug('download image', image.origin, '...');
downloaded.push(image.origin);
return options.book.log.debug.promise(fs.writeStream(imgin, request(image.origin)));
return options.book.log.debug.promise(fs.writeStream(imgin, request(image.origin)))
.fail(function(err) {
if (!_.isError(err)) err = new Error(err);
err.message = 'Fail downloading '+image.origin+': '+err.message;
throw err;
});
})
// Write svg if content
@@ -298,16 +332,15 @@ function convertImages(images, options) {
.then(function() {
if (!image.dest) return;
var imgout = path.resolve(options.book.options.output, image.dest);
options.book.log.debug("convert image", image.source, "to", image.dest, "...");
options.book.log.debug('convert image', image.source, 'to', image.dest, '...');
return options.book.log.debug.promise(imgUtils.convertSVG(imgin, imgout));
});
}
})
.then(function() {
options.book.log.debug.ok(images.length+" images converted with success");
options.book.log.debug.ok(images.length+' images converted with success');
});
};
}
// Adapt page content to be relative to a base folder
function normalizePage(sections, options) {
@@ -319,16 +352,16 @@ function normalizePage(sections, options) {
convertImages: false,
// Current file path
input: ".",
input: '.',
// Navigation to use to transform path
navigation: {},
// Directory parent of the file currently in rendering process
base: "./",
base: './',
// Directory parent from the html output
output: "./",
output: './',
// Glossary terms
glossary: []
@@ -338,9 +371,9 @@ function normalizePage(sections, options) {
var toConvert = [];
sections = _.map(sections, function(section) {
if (section.type != "normal") return section;
if (section.type != 'normal') return section;
var out = normalizeHtml(section.content, options);;
var out = normalizeHtml(section.content, options);
toConvert = toConvert.concat(out.images);
section.content = out.html;
@@ -353,23 +386,9 @@ function normalizePage(sections, options) {
return convertImages(toConvert, options);
})
.thenResolve(sections);
};
}
// Extract text from sections
function extractText(sections) {
return _.reduce(sections, function(prev, section) {
if (section.type != "normal") return prev;
var $ = cheerio.load(section.content);
$("*").each(function() {
prev = prev+" "+$(this).text();
});
return prev;
}, "");
};
module.exports = {
normalize: normalizePage,
extractText: extractText
normalize: normalizePage
};
+40
View File
@@ -0,0 +1,40 @@
var _ = require("lodash");
var path = require("path");
// Return true if file path is inside a folder
function isInRoot(root, filename) {
filename = path.normalize(filename);
return (filename.substr(0, root.length) === root);
}
// Resolve paths in a specific folder
// Throw error if file is outside this folder
function resolveInRoot(root) {
var input, result, err;
input = _.chain(arguments)
.toArray()
.slice(1)
.reduce(function(current, p) {
// Handle path relative to book root ("/README.md")
if (p[0] == "/" || p[0] == "\\") return p.slice(1);
return current? path.join(current, p) : path.normalize(p);
}, "")
.value();
result = path.resolve(root, input);
if (!isInRoot(root, result)) {
err = new Error("EACCESS: \"" + result + "\" not in \"" + root + "\"");
err.code = "EACCESS";
throw err;
}
return result;
}
module.exports = {
isInRoot: isInRoot,
resolveInRoot: resolveInRoot
};
+2 -2
View File
@@ -1,7 +1,7 @@
var _ = require("lodash");
var _ = require('lodash');
// Returns from a navigation and a current file, a snapshot of current detailed state
var calculProgress = function(navigation, current) {
function calculProgress(navigation, current) {
var n = _.size(navigation);
var percent = 0, prevPercent = 0, currentChapter = null;
var done = true;
+15 -17
View File
@@ -1,11 +1,9 @@
var Q = require('q');
var _ = require('lodash');
var events = require('events');
var http = require('http');
var send = require('send');
var util = require('util');
var url = require('url');
var Q = require("q");
var events = require("events");
var http = require("http");
var send = require("send");
var util = require("util");
var url = require("url");
var Server = function() {
this.running = null;
@@ -17,7 +15,7 @@ util.inherits(Server, events.EventEmitter);
// Return true if the server is running
Server.prototype.isRunning = function() {
return this.running != null;
return !!this.running;
};
// Stop the server
@@ -57,25 +55,25 @@ Server.prototype.start = function(dir, port) {
res.end(err.message);
}
// Redirect to directory's index.html
// Redirect to directory"s index.html
function redirect() {
res.statusCode = 301;
res.setHeader('Location', req.url + '/');
res.end('Redirecting to ' + req.url + '/');
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)
.on("error", error)
.on("directory", redirect)
.pipe(res);
});
that.running.on('connection', function (socket) {
that.running.on("connection", function (socket) {
that.sockets.push(socket);
socket.setTimeout(4000);
socket.on('close', function () {
socket.on("close", function () {
that.sockets.splice(that.sockets.indexOf(socket), 1);
});
});
@@ -91,6 +89,6 @@ Server.prototype.start = function(dir, port) {
return d.promise;
});
}
};
module.exports = Server;
+2 -2
View File
@@ -1,7 +1,7 @@
var _ = require("lodash");
function escapeShellArg(arg) {
var ret = '';
var ret = "";
ret = arg.replace(/"/g, '\\"');
@@ -11,7 +11,7 @@ function escapeShellArg(arg) {
function optionsToShellArgs(options) {
return _.chain(options)
.map(function(value, key) {
if (value == null || value === false) return null;
if (value === null || value === undefined || value === false) return null;
if (value === true) return key;
return key+"="+escapeShellArg(value);
})
+6 -5
View File
@@ -1,9 +1,9 @@
var Q = require('q');
var _ = require('lodash');
var path = require('path');
var chokidar = require('chokidar');
var Q = require("q");
var _ = require("lodash");
var path = require("path");
var chokidar = require("chokidar");
var parsers = require('gitbook-parsers')
var parsers = require("gitbook-parsers");
function watch(dir) {
var d = Q.defer();
@@ -19,6 +19,7 @@ function watch(dir) {
var watcher = chokidar.watch(toWatch, {
cwd: dir,
ignored: "_book/**",
ignoreInitial: true
});
+19
View File
@@ -0,0 +1,19 @@
var semver = require('semver');
var pkg = require('../package.json');
var VERSION = pkg.version;
var VERSION_STABLE = VERSION.replace(/\-(\S+)/g, '');
// Test if current current gitbook version satisfies a condition
// We can't directly use samver.satisfies since it will break all plugins when gitbook version is a prerelease (beta, alpha)
function satisfies(condition) {
// Test with real version
if (semver.satisfies(VERSION, condition)) return true;
// Test with future stable release
return semver.satisfies(VERSION_STABLE, condition);
}
module.exports = {
satisfies: satisfies
};
+35 -19
View File
@@ -1,49 +1,65 @@
{
"name": "gitbook",
"version": "2.1.0",
"version": "2.6.6",
"homepage": "https://www.gitbook.com",
"description": "Library and cmd utility to generate GitBooks",
"main": "lib/index.js",
"dependencies": {
"q": "1.0.1",
"lunr": "0.5.7",
"lodash": "3.5.0",
"lodash": "3.10.1",
"graceful-fs": "3.0.5",
"resolve": "0.6.3",
"fs-extra": "0.16.5",
"fstream-ignore": "1.0.2",
"gitbook-parsers": "0.7.6",
"nunjucks": "mozilla/nunjucks#103513c294835bcbe64222de6db494e2555e294e",
"gitbook-parsers": "0.8.9",
"gitbook-plugin-highlight": "1.0.3",
"gitbook-plugin-sharing": "1.0.1",
"gitbook-plugin-search": "1.1.0",
"gitbook-plugin-fontsettings": "1.0.2",
"nunjucks": "2.2.0",
"nunjucks-autoescape": "1.0.0",
"nunjucks-filter": "1.0.0",
"i18n": "0.5.0",
"semver": "2.2.1",
"semver": "5.0.1",
"npmi": "0.1.1",
"cheerio": "0.19.0",
"gitbook-plugin-livereload": "0.0.1",
"chokidar": "~1.0.1",
"chokidar": "~1.0.5",
"send": "0.2.0",
"tiny-lr": "0.1.5",
"tiny-lr": "0.2.1",
"tmp": "0.0.24",
"crc": "3.2.1",
"bash-color": "0.0.3",
"URIjs": "1.14.1",
"urijs": "1.17.0",
"request": "2.51.0",
"npm": "2.4.1",
"dom-serializer": "0.1.0",
"spawn-cmd": "0.0.2",
"highlight.js": "8.4.0",
"escape-string-regexp": "1.0.3"
"escape-string-regexp": "1.0.3",
"juice": "1.5.0",
"jsonschema": "1.0.2",
"json-schema-defaults": "0.1.1",
"merge-defaults": "0.2.1",
"github-slugid": "1.0.0"
},
"devDependencies": {
"mocha": "2.2.1",
"should": "5.2.0",
"grunt": "~0.4.2",
"grunt-cli": "0.1.11",
"grunt-contrib-copy": "0.5.0",
"grunt-contrib-less": "~0.5.0",
"grunt-contrib-requirejs": "0.4.1",
"grunt-bower-install-simple": "0.9.2"
"eslint": "1.5.0",
"mocha": "2.3.2",
"should": "7.1.0",
"should-promised": "0.3.1",
"gulp": "^3.8.11",
"gulp-rename": "^1.2.2",
"gulp-uglify": "1.1.0",
"gulp-less": "3.0.2",
"gulp-minify-css": "1.0.0",
"gulp-util": "3.0.6",
"browserify": "11.0.1",
"merge-stream": "0.1.7",
"vinyl-source-stream": "1.1.0",
"jquery": "2.1.4",
"mousetrap": "1.5.3",
"font-awesome": "4.1.0",
"gitbook-markdown-css": "1.0.1"
},
"scripts": {
"test": "node_modules/.bin/mocha --reporter spec --timeout 15000"
+22 -7
View File
@@ -4,36 +4,51 @@ var path = require('path');
var should = require('should');
var cheerio = require('cheerio');
require('should-promised');
should.Assertion.add('file', function(file, description) {
this.params = { actual: this.obj.toString(), operator: 'have file ' + file, message: description };
this.obj.should.have.property('options').which.is.an.Object;
this.obj.options.should.have.property('output').which.is.a.String;
this.obj.should.have.property('options').which.is.an.Object();
this.obj.options.should.have.property('output').which.is.a.String();
this.assert(fs.existsSync(path.resolve(this.obj.options.output, file)));
});
should.Assertion.add('jsonfile', function(file, description) {
this.params = { actual: this.obj.toString(), operator: 'have valid jsonfile ' + file, message: description };
this.obj.should.have.property('options').which.is.an.Object;
this.obj.options.should.have.property('output').which.is.a.String;
this.assert(JSON.parse(fs.readFileSync(path.resolve(this.obj.options.output, file), { encoding: "utf-8" })));
this.obj.should.have.property('options').which.is.an.Object();
this.obj.options.should.have.property('output').which.is.a.String();
this.assert(JSON.parse(fs.readFileSync(path.resolve(this.obj.options.output, file), { encoding: 'utf-8' })));
});
should.Assertion.add('html', function(rules, description) {
this.params = { actual: "HTML string", operator: 'valid html', message: description };
this.params = { actual: 'HTML string', operator: 'valid html', message: description };
var $ = cheerio.load(this.obj);
_.each(rules, function(validations, query) {
validations = _.defaults(validations || {}, {
// Select a specific element in the list of matched elements
index: null,
// Check that there is the correct count of elements
count: 1,
// Check attribute values
attributes: {},
// Trim inner text
trim: false,
// Check inner text
text: undefined
});
var $el = $(query);
// Select correct element
if (_.isNumber(validations.index)) $el = $($el.get(validations.index));
// Test number of elements
$el.length.should.be.equal(validations.count);
@@ -47,7 +62,7 @@ should.Assertion.add('html', function(rules, description) {
// Test attributes
_.each(validations.attributes, function(value, name) {
var attr = $el.attr(name);
should(attr).be.ok;
should(attr).be.ok();
attr.should.be.equal(value);
});
});
+7 -2
View File
@@ -1,10 +1,15 @@
# Readme
<p id="test-plugin-block-shortcuts-1">$$test_block1$$</p>
<p id="test-plugin-block-shortcuts-2">{% include "./block.md" %}</p>
### Relative
<p id="t1">{% include "./hello.md" %}</p>
<p id="t2">{% include "/hello.md" %}</p>
### Git
<p id="t2">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md" %}</p>
<p id="t3">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test2.md" %}</p>
<p id="t3">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md" %}</p>
<p id="t4">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test2.md" %}</p>
<p id="t5">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test3.md" %}</p>
+1
View File
@@ -0,0 +1 @@
$$test_block2$$
+3
View File
@@ -0,0 +1,3 @@
# Hello World
## Hello {#hello-custom}
View File
+16
View File
@@ -0,0 +1,16 @@
# Readme
Block without language
```
test 1
```
Block with a language
```lang
test 2
```
Inline code: `test 3`
Inline code with html: `<test>`
+1
View File
@@ -0,0 +1 @@
# Summary
+3
View File
@@ -4,3 +4,6 @@
* [Hello 2](hello2.md)
* Hello 3
* [Hello 4](hello3/hello4.md)
* Hello 5
* [Hello 6](hello3/hello5/hello6.md)
+1
View File
@@ -0,0 +1 @@
== Readme for the bookk
+1
View File
@@ -0,0 +1 @@
# Readme for GitHub
+1
View File
@@ -0,0 +1 @@
# Summary
+5
View File
@@ -0,0 +1,5 @@
{
"structure": {
"readme": "README.adoc"
}
}
+5
View File
@@ -0,0 +1,5 @@
# Glossary
### Hello
Hello world
+65
View File
@@ -0,0 +1,65 @@
var path = require('path');
var fs = require('fs');
var Plugin = require('../lib/plugin');
var PLUGINS_ROOT = path.resolve(__dirname, 'plugins');
describe('Code Highlighting', function () {
var book, PAGE;
before(function() {
return books.generate('highlight', 'website', {
prepare: function(_book) {
book = _book;
var plugin = new Plugin(book, 'replace_highlight');
plugin.load('./replace_highlight', PLUGINS_ROOT);
book.plugins.load(plugin);
}
})
.then(function() {
PAGE = fs.readFileSync(
path.join(book.options.output, 'index.html'),
{ encoding: 'utf-8' }
);
});
});
it('should correctly replace highlighting', function() {
PAGE.should.be.html({
'code': {
index: 0,
text: 'code_test 1\n_code'
}
});
});
it('should correctly replace highlighting with language', function() {
PAGE.should.be.html({
'code': {
index: 1,
text: 'lang_test 2\n_lang'
}
});
});
it('should correctly replace highlighting for inline code', function() {
PAGE.should.be.html({
'code': {
index: 2,
text: 'code_test 3_code'
}
});
});
it('should correctly replace highlighting for inline code with html tags', function() {
PAGE.should.be.html({
'code': {
index: 3,
text: 'code_<test>_code'
}
});
});
});
+16 -11
View File
@@ -1,32 +1,37 @@
var fs = require('fs');
var path = require('path');
describe('Configuration', function () {
it('should extract default title from README', function() {
return books.parse("basic")
return books.parse('basic')
.then(function(book) {
book.options.title.should.be.equal("Readme");
book.options.title.should.be.equal('Readme');
});
});
it('should extract default description from README', function() {
return books.parse("basic")
return books.parse('basic')
.then(function(book) {
book.options.description.should.be.equal("Default description for the book.");
book.options.description.should.be.equal('Default description for the book.');
});
});
it('should correctly load from json (book.json)', function() {
return books.parse("config-json")
return books.parse('config-json')
.then(function(book) {
book.options.title.should.be.equal("json-config");
book.options.title.should.be.equal('json-config');
});
});
it('should correctly load from JavaScript (book.js)', function() {
return books.parse("config-js")
return books.parse('config-js')
.then(function(book) {
book.options.title.should.be.equal("js-config");
book.options.title.should.be.equal('js-config');
});
});
it('should provide configuration on book.config.get', function() {
return books.parse('basic')
.then(function(book) {
book.config.get('description').should.be.equal('Default description for the book.');
book.getConfig('description').should.be.equal('Default description for the book.');
});
});
});
+36 -11
View File
@@ -5,37 +5,62 @@ describe('ConRefs', function () {
var book, readme;
before(function() {
return books.generate("conrefs", "website")
return books.generate('conrefs', 'website')
.then(function(_book) {
book = _book;
readme = fs.readFileSync(
path.join(book.options.output, "index.html"),
{ encoding: "utf-8" }
path.join(book.options.output, 'index.html'),
{ encoding: 'utf-8' }
);
});
});
it('should handle local references', function() {
readme.should.be.html({
".page-inner p#t1": {
'.page-inner p#t1': {
count: 1,
text: "Hello World",
text: 'Hello World',
trim: true
}
});
});
it('should handle git references', function() {
it('should handle local references with absolute paths', function() {
readme.should.be.html({
".page-inner p#t2": {
'.page-inner p#t2': {
count: 1,
text: "Hello from git",
text: 'Hello World',
trim: true
},
".page-inner p#t3": {
}
});
});
it('should correctly include file from git reference', function() {
readme.should.be.html({
'.page-inner p#t3': {
count: 1,
text: "First Hello. Hello from git",
text: 'Hello from git',
trim: true
}
});
});
it('should correctly handle deep include in git reference', function() {
readme.should.be.html({
'.page-inner p#t4': {
count: 1,
text: 'First Hello. Hello from git',
trim: true
}
});
});
it('should correctly handle absolute include in git reference', function() {
readme.should.be.html({
'.page-inner p#t5': {
count: 1,
text: 'First Hello. Hello from git',
trim: true
}
});
+33 -18
View File
@@ -6,19 +6,23 @@ describe('eBook generator', function () {
var book;
before(function() {
return books.generate("basic", "ebook")
return books.generate('basic', 'ebook')
.then(function(_book) {
book = _book;
});
});
it('should correctly output a SUMMARY.html', function() {
book.should.have.file("SUMMARY.html");
book.should.have.file('SUMMARY.html');
});
it('should correctly copy assets', function() {
book.should.have.file("gitbook");
book.should.have.file("gitbook/style.css");
book.should.have.file('gitbook');
book.should.have.file('gitbook/ebook.css');
});
it('should not copy website assets', function() {
book.should.not.have.file('gitbook/style.css');
});
});
@@ -26,30 +30,41 @@ describe('eBook generator', function () {
var book;
before(function() {
return books.generate("style-print", "ebook")
return books.generate('style-print', 'ebook')
.then(function(_book) {
book = _book;
});
});
it('should correctly copy print.css', function() {
book.should.have.file('styles');
book.should.have.file('styles/print.css');
});
it('should remove default print.css', function() {
var PAGE = fs.readFileSync(
path.join(book.options.output, "index.html"),
{ encoding: "utf-8" }
path.join(book.options.output, 'index.html'),
{ encoding: 'utf-8' }
);
// There are 2 styles (one from plugin-highlight and the new style)
PAGE.should.be.html({
"link": {
count: 1,
attributes: {
href: "./styles/print.css"
}
'link': {
count: 2
}
});
PAGE.should.be.html({
'link[href=\'./styles/print.css\']': {
count: 1
}
});
PAGE.should.be.html({
'link[href="gitbook/plugins/gitbook-plugin-highlight/ebook.css"]': {
count: 1
}
});
});
it('should correctly print.css', function() {
book.should.have.file("styles");
book.should.have.file("styles/print.css");
});
})
});
});
+11
View File
@@ -0,0 +1,11 @@
describe('Formatting', function () {
it('should provide formatting with book.formatString', function() {
return books.parse('basic')
.then(function(book) {
return book.formatString('markdown', 'this is a **test**');
})
.then(function(content) {
content.should.equal('<p>this is a <strong>test</strong></p>\n');
});
});
});
+6 -6
View File
@@ -1,8 +1,8 @@
var should = require('should');
var git = require('../lib/utils/git');
var should = require("should");
var git = require("../lib/utils/git");
describe('GIT parser and getter', function () {
it('should correctly parse an https url', function() {
describe("GIT parser and getter", function () {
it("should correctly parse an https url", function() {
var parts = git.parseUrl("git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md");
should.exist(parts);
@@ -11,7 +11,7 @@ describe('GIT parser and getter', function () {
parts.filepath.should.be.equal("test.md");
});
it('should correctly parse an https url with a reference', function() {
it("should correctly parse an https url with a reference", function() {
var parts = git.parseUrl("git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md#0.1.2");
should.exist(parts);
@@ -20,7 +20,7 @@ describe('GIT parser and getter', function () {
parts.filepath.should.be.equal("test.md");
});
it('should correctly parse an ssh url', function() {
it("should correctly parse an ssh url", function() {
var parts = git.parseUrl("git+git@github.com:GitbookIO/gitbook.git/directory/README.md#e1594cde2c32e4ff48f6c4eff3d3d461743d74e1");
should.exist(parts);
+15 -15
View File
@@ -1,8 +1,8 @@
var fs = require('fs');
var path = require('path');
var fs = require("fs");
var path = require("path");
describe('Glossary', function () {
describe('Parsing', function() {
describe("Glossary", function () {
describe("Parsing", function() {
var book;
before(function() {
@@ -12,13 +12,13 @@ describe('Glossary', function () {
});
});
it('should correctly list items', function() {
it("should correctly list items", function() {
book.should.have.property("glossary");
book.glossary.should.have.lengthOf(3);
});
});
describe('Generation', function() {
describe("Generation", function() {
var book;
before(function() {
@@ -28,11 +28,11 @@ describe('Glossary', function () {
});
});
it('should correctly generate a GLOSSARY.html', function() {
it("should correctly generate a GLOSSARY.html", function() {
book.should.have.file("GLOSSARY.html");
});
describe('Page Integration', function() {
describe("Page Integration", function() {
var readme, page;
before(function() {
@@ -46,9 +46,9 @@ describe('Glossary', function () {
);
});
it('should correctly replaced terms by links', function() {
it("should correctly replaced terms by links", function() {
readme.should.be.html({
".page-inner a[href='GLOSSARY.html#test']": {
".page-inner a[href=\"GLOSSARY.html#test\"]": {
count: 1,
text: "test",
attributes: {
@@ -58,15 +58,15 @@ describe('Glossary', function () {
});
});
it('should correctly replaced terms by links (relative)', function() {
it("should correctly replaced terms by links (relative)", function() {
page.should.be.html({
".page-inner a[href='../GLOSSARY.html#test']": {
".page-inner a[href=\"../GLOSSARY.html#test\"]": {
count: 1
}
});
});
it('should not replace terms in codeblocks', function() {
it("should not replace terms in codeblocks", function() {
readme.should.be.html({
".page-inner code a": {
count: 0
@@ -74,9 +74,9 @@ describe('Glossary', function () {
});
});
it('should correctly select the longest term', function() {
it("should correctly select the longest term", function() {
readme.should.be.html({
".page-inner a[href='GLOSSARY.html#test_long']": {
".page-inner a[href=\"GLOSSARY.html#test_long\"]": {
count: 1,
text: "test long"
}
+37
View File
@@ -0,0 +1,37 @@
var path = require('path');
var fs = require('fs');
describe('Headings', function () {
var book, PAGE;
before(function() {
return books.generate('headings', 'website')
.then(function(_book) {
book = _book;
PAGE = fs.readFileSync(
path.join(book.options.output, 'index.html'),
{ encoding: 'utf-8' }
);
});
});
describe('IDs', function() {
it('should correctly generate an ID', function() {
PAGE.should.be.html({
'h1#hello-world': {
count: 1
}
});
});
it('should correctly accept custom ID', function() {
PAGE.should.be.html({
'h2#hello-custom': {
count: 1
}
});
});
});
});
+28 -18
View File
@@ -1,15 +1,13 @@
var os = require('os');
var path = require('path');
var Q = require('q');
var fs = require('fs');
var _ = require('lodash');
var should = require('should');
var fsUtil = require('../lib/utils/fs');
var Book = require('../').Book;
var LOG_LEVELS = require('../').LOG_LEVELS;
require("./assertions");
require('./assertions');
var BOOKS = {};
@@ -17,30 +15,44 @@ var TMPDIR = os.tmpdir();
// Generate and return a book
function generateBook(bookId, test) {
return parseBook(bookId, test)
function generateBook(bookId, test, opts) {
opts = _.defaults(opts || {}, {
prepare: function() {}
});
return parseBook(bookId, test, opts)
.then(function(book) {
return book.generate(test)
return Q(opts.prepare(book))
.then(function() {
return book.generate(test);
})
.thenResolve(book);
});
}
// Generate and return a book
function parseBook(bookId, test) {
test = test || "website";
BOOKS[bookId] = BOOKS[bookId] || {};
if (BOOKS[bookId][test]) return Q(BOOKS[bookId][test]);
function parseBook(bookId, test, opts) {
opts = _.defaults(opts || {}, {
testId: ''
});
BOOKS[bookId][test] = new Book(path.resolve(__dirname, "books", bookId), {
test = test || 'website';
var testId = [test, opts.testId].join('-');
BOOKS[bookId] = BOOKS[bookId] || {};
if (BOOKS[bookId][testId]) return Q(BOOKS[bookId][testId]);
BOOKS[bookId][testId] = new Book(path.resolve(__dirname, 'books', bookId), {
logLevel: LOG_LEVELS.DISABLED,
config: {
output: path.resolve(TMPDIR, bookId+"-"+test)
output: path.resolve(TMPDIR, bookId+'-'+testId)
}
});
return BOOKS[bookId][test].parse()
return BOOKS[bookId][testId].parse()
.then(function() {
return BOOKS[bookId][test];
return BOOKS[bookId][testId];
});
}
@@ -53,16 +65,14 @@ global.books = {
// Cleanup all tests
after(function() {
return _.chain(BOOKS)
.map(function(types, bookId) {
.map(function(types) {
return _.values(types);
})
.flatten()
.reduce(function(prev, book) {
return prev.then(function() {
return fsUtil.remove(book.options.output);
})
});
}, Q())
.value();
});
+14 -12
View File
@@ -1,9 +1,9 @@
var fs = require('fs');
var _ = require('lodash');
var path = require('path');
var cheerio = require('cheerio');
var fs = require("fs");
var _ = require("lodash");
var path = require("path");
var cheerio = require("cheerio");
describe('Images', function () {
describe("Images", function () {
var book, readme, $, $img, srcs;
before(function() {
@@ -17,38 +17,40 @@ describe('Images', function () {
);
$ = cheerio.load(readme);
$img = $("img");
srcs = $img.map(function() { return $(this).attr("src"); })
srcs = $img.map(function() {
return $(this).attr("src");
});
});
});
it('should detect all images', function() {
it("should detect all images", function() {
_.uniq(srcs).should.have.lengthOf(4);
});
it('should keep image tags', function() {
it("should keep image tags", function() {
srcs.should.have.lengthOf(5);
});
it('should not have .svg files', function() {
it("should not have .svg files", function() {
_.each(srcs, function(src) {
path.extname(src).should.not.equal(".svg");
});
});
it('should correctly convert svg images to png', function() {
it("should correctly convert svg images to png", function() {
_.each(srcs, function(src) {
book.should.have.file(src);
});
});
it('should handle relative paths', function() {
it("should handle relative paths", function() {
var PAGE = fs.readFileSync(
path.join(book.options.output, "folder/PAGE.html"),
{ encoding: "utf-8" }
);
PAGE.should.be.html({
"img[src='../test.png']": {
"img[src=\"../test.png\"]": {
count: 1
}
});
+6 -5
View File
@@ -9,15 +9,16 @@ describe('Init Books', function () {
var initRoot;
before(function() {
initRoot = path.resolve(__dirname, "books/init");
initRoot = path.resolve(__dirname, 'books/init');
return Book.init(initRoot, {
logLevel: LOG_LEVELS.DISABLED,
logLevel: LOG_LEVELS.DISABLED
});
});
it('should create all chapters', function() {
should(fs.existsSync(path.resolve(initRoot, "hello.md"))).be.ok;
should(fs.existsSync(path.resolve(initRoot, "hello2.md"))).be.ok;
should(fs.existsSync(path.resolve(initRoot, "hello3/hello4.md"))).be.ok;
should(fs.existsSync(path.resolve(initRoot, 'hello.md'))).be.ok();
should(fs.existsSync(path.resolve(initRoot, 'hello2.md'))).be.ok();
should(fs.existsSync(path.resolve(initRoot, 'hello3/hello4.md'))).be.ok();
should(fs.existsSync(path.resolve(initRoot, 'hello3/hello5/hello6.md'))).be.ok();
});
});
+22 -22
View File
@@ -6,18 +6,18 @@ describe('JSON generator', function () {
var book;
before(function() {
return books.generate("basic", "json")
return books.generate('basic', 'json')
.then(function(_book) {
book = _book;
});
});
it('should correctly output a README.json', function() {
book.should.have.file("README.json");
book.should.have.file('README.json');
});
it('should output a valid json', function() {
book.should.have.jsonfile("README.json");
book.should.have.jsonfile('README.json');
});
describe('Page Format', function() {
@@ -26,26 +26,26 @@ describe('JSON generator', function () {
before(function() {
page = JSON.parse(
fs.readFileSync(
path.join(book.options.output, "README.json"),
{ encoding: "utf-8" }
path.join(book.options.output, 'README.json'),
{ encoding: 'utf-8' }
)
);
});
it('should contains valid section', function() {
page.should.have.property("sections").with.lengthOf(1);
page.sections[0].should.have.property("content").which.is.a.String;
page.sections[0].should.have.property("type").which.is.a.String.which.equal("normal");
page.should.have.property('sections').with.lengthOf(1);
page.sections[0].should.have.property('content').which.is.a.String();
page.sections[0].should.have.property('type', 'normal');
});
it('should contains valid progress', function() {
page.should.have.property("progress");
page.progress.should.have.property("chapters").with.lengthOf(1);
page.progress.should.have.property("current");
page.should.have.property('progress');
page.progress.should.have.property('chapters').with.lengthOf(1);
page.progress.should.have.property('current');
});
it('should contains no languages', function() {
page.should.have.property("langs").with.lengthOf(0);
page.should.have.property('langs').with.lengthOf(0);
});
});
});
@@ -54,22 +54,22 @@ describe('JSON generator', function () {
var book;
before(function() {
return books.generate("languages", "json")
return books.generate('languages', 'json')
.then(function(_book) {
book = _book;
});
});
it('should correctly output READMEs', function() {
book.should.have.file("README.json");
book.should.have.file("en/README.json");
book.should.have.file("fr/README.json");
book.should.have.file('README.json');
book.should.have.file('en/README.json');
book.should.have.file('fr/README.json');
});
it('should output valid json', function() {
book.should.have.jsonfile("README.json");
book.should.have.jsonfile("en/README.json");
book.should.have.jsonfile("fr/README.json");
book.should.have.jsonfile('README.json');
book.should.have.jsonfile('en/README.json');
book.should.have.jsonfile('fr/README.json');
});
describe('Page Format', function() {
@@ -78,14 +78,14 @@ describe('JSON generator', function () {
before(function() {
page = JSON.parse(
fs.readFileSync(
path.join(book.options.output, "README.json"),
{ encoding: "utf-8" }
path.join(book.options.output, 'README.json'),
{ encoding: 'utf-8' }
)
);
});
it('should contains no languages', function() {
page.should.have.property("langs").with.lengthOf(2);
page.should.have.property('langs').with.lengthOf(2);
});
});
});
+5 -8
View File
@@ -1,8 +1,5 @@
var fs = require('fs');
var path = require('path');
describe('Languages', function () {
describe('Parsing', function() {
describe("Languages", function () {
describe("Parsing", function() {
var book;
before(function() {
@@ -12,7 +9,7 @@ describe('Languages', function () {
});
});
it('should correctly list languages', function() {
it("should correctly list languages", function() {
book.should.have.property("books");
book.books.should.have.lengthOf(2);
@@ -21,7 +18,7 @@ describe('Languages', function () {
});
});
describe('Generation', function() {
describe("Generation", function() {
var book;
before(function() {
@@ -31,7 +28,7 @@ describe('Languages', function () {
});
});
it('should correctly create books', function() {
it("should correctly create books", function() {
book.should.have.file("index.html");
book.should.have.file("en/index.html");
book.should.have.file("fr/index.html");
+14 -16
View File
@@ -1,13 +1,11 @@
var should = require('should');
var fs = require('fs');
var _ = require('lodash');
var path = require('path');
var cheerio = require('cheerio');
var fs = require("fs");
var path = require("path");
var cheerio = require("cheerio");
var links = require("../lib/utils/links");
describe('Links', function () {
it('should correctly test external links', function() {
describe("Links", function () {
it("should correctly test external links", function() {
links.isExternal("http://google.fr").should.be.exactly(true);
links.isExternal("https://google.fr").should.be.exactly(true);
links.isExternal("test.md").should.be.exactly(false);
@@ -15,32 +13,32 @@ describe('Links', function () {
links.isExternal("/folder/test.md").should.be.exactly(false);
});
it('should correctly detect anchor links', function() {
it("should correctly detect anchor links", function() {
links.isAnchor("#test").should.be.exactly(true);
links.isAnchor(" #test").should.be.exactly(true);
links.isAnchor("https://google.fr#test").should.be.exactly(false);
links.isAnchor("test.md#test").should.be.exactly(false);
});
describe('toAbsolute', function() {
it('should correctly transform as absolute', function() {
describe("toAbsolute", function() {
it("should correctly transform as absolute", function() {
links.toAbsolute("http://google.fr").should.be.equal("http://google.fr");
links.toAbsolute("test.md", "./", "./").should.be.equal("test.md");
links.toAbsolute("folder/test.md", "./", "./").should.be.equal("folder/test.md");
});
it('should correctly handle windows path', function() {
it("should correctly handle windows path", function() {
links.toAbsolute("folder\\test.md", "./", "./").should.be.equal("folder/test.md");
});
it('should correctly handle absolute path', function() {
it("should correctly handle absolute path", function() {
links.toAbsolute("/test.md", "./", "./").should.be.equal("test.md");
links.toAbsolute("/test.md", "test", "test").should.be.equal("../test.md");
links.toAbsolute("/sub/test.md", "test", "test").should.be.equal("../sub/test.md");
});
});
describe('page', function() {
describe("page", function() {
var book;
before(function() {
@@ -50,7 +48,7 @@ describe('Links', function () {
});
});
it('should correctly replace relative links', function() {
it("should correctly replace relative links", function() {
var readme = fs.readFileSync(
path.join(book.options.output, "folder1/index.html"),
{ encoding: "utf-8" }
@@ -58,8 +56,8 @@ describe('Links', function () {
var $ = cheerio.load(readme);
var $a = $(".page-inner a");
$a.attr('href').should.be.exactly("../folder2/index.html");
})
$a.attr("href").should.be.exactly("../folder2/index.html");
});
});
});
+61
View File
@@ -0,0 +1,61 @@
var should = require("should");
describe("Navigation", function () {
var book;
before(function() {
return books.parse("summary")
.then(function(_book) {
book = _book;
});
});
it("should correctly parse navigation as a map", function() {
book.should.have.property("navigation");
book.navigation.should.have.property("README.md");
book.navigation.should.have.property("README.md");
});
it("should correctly include filenames", function() {
book.navigation.should.have.property("README.md");
book.navigation.should.have.property("PAGE1.md");
book.navigation.should.have.property("folder/PAGE2.md");
book.navigation.should.not.have.property("NOTFOUND.md");
});
it("should correctly detect next/prev for README", function() {
var README = book.navigation["README.md"];
README.index.should.equal(0);
README.should.have.property("next");
should(README.prev).not.be.ok();
README.next.should.have.property("path");
README.next.path.should.equal("PAGE1.md");
});
it("should correctly detect next/prev a page", function() {
var PAGE = book.navigation["PAGE1.md"];
PAGE.index.should.equal(1);
PAGE.should.have.property("next");
PAGE.should.have.property("prev");
PAGE.prev.should.have.property("path");
PAGE.prev.path.should.equal("README.md");
PAGE.next.should.have.property("path");
PAGE.next.path.should.equal("folder/PAGE2.md");
});
it("should correctly detect next/prev for last page", function() {
var PAGE = book.navigation["folder/PAGE2.md"];
PAGE.index.should.equal(2);
PAGE.should.have.property("prev");
should(PAGE.next).not.be.ok();
PAGE.prev.should.have.property("path");
PAGE.prev.path.should.equal("PAGE1.md");
});
});
+177 -37
View File
@@ -1,26 +1,28 @@
var _ = require('lodash');
var fs = require('fs');
var should = require('should');
var path = require('path');
var Plugin = require('../lib/plugin');
var parsers = require('gitbook-parsers');
var PLUGINS_ROOT = path.resolve(__dirname, 'plugins');
describe('Plugins', function () {
var book;
before(function() {
return books.parse("basic")
.then(function(_book) {
book = _book;
});
return books.parse('basic')
.then(function(_book) {
book = _book;
});
});
describe('Invalid', function() {
var plugin;
before(function() {
plugin = new Plugin(book, "invalid");
plugin.load("./invalid", PLUGINS_ROOT);
plugin = new Plugin(book, 'invalid');
plugin.load('./invalid', PLUGINS_ROOT);
});
it('should be detected', function() {
@@ -32,8 +34,8 @@ describe('Plugins', function () {
var plugin;
before(function() {
plugin = new Plugin(book, "empty");
plugin.load("./empty", PLUGINS_ROOT);
plugin = new Plugin(book, 'empty');
plugin.load('./empty', PLUGINS_ROOT);
});
it('should valid a plugin', function() {
@@ -50,12 +52,43 @@ describe('Plugins', function () {
});
});
describe('Configuration', function() {
var plugin;
before(function() {
plugin = new Plugin(book, 'testconfig');
plugin.load('./config', PLUGINS_ROOT);
});
it('should throw error for invalid configuration', function() {
return plugin.validateConfig({})
.should.be.rejectedWith('Configuration Error: pluginsConfig.testconfig.testRequired is required');
});
it('should throw error for invalid types', function() {
return plugin.validateConfig({
testRequired: 'hello'
})
.should.be.rejectedWith('Configuration Error: pluginsConfig.testconfig.testRequired is not of a type(s) number');
});
it('should extend with default values', function() {
return plugin.validateConfig({
testRequired: 12
})
.should.be.fulfilledWith({
hello: 'world',
testRequired: 12
});
});
});
describe('Resources', function() {
var plugin;
before(function() {
plugin = new Plugin(book, "resources");
plugin.load("./resources", PLUGINS_ROOT);
plugin = new Plugin(book, 'resources');
plugin.load('./resources', PLUGINS_ROOT);
return book.plugins.load(plugin);
});
@@ -66,29 +99,34 @@ describe('Plugins', function () {
describe('Website', function() {
it('should return a valid list of resources', function() {
return plugin.getResources("website")
return plugin.getResources('website')
.then(function(resources) {
resources["js"].should.have.lengthOf(1);
resources.js.should.have.lengthOf(1);
});
});
it('should extend books plugins', function() {
var resources = book.plugins.resources("website");
resources["js"].should.have.lengthOf(1);
var resources = book.plugins.resources('website');
resources.js.should.have.lengthOf(5);
});
});
describe('eBook', function() {
it('should return a valid list of resources', function() {
return plugin.getResources("ebook")
return plugin.getResources('ebook')
.then(function(resources) {
resources["css"].should.have.lengthOf(1);
resources.css.should.have.lengthOf(1);
});
});
it('should extend books plugins', function() {
var resources = book.plugins.resources("ebook");
resources["css"].should.have.lengthOf(1);
var resources = book.plugins.resources('ebook');
// There is resources from highlight plugin and this plugin
resources.css.should.have.lengthOf(2);
should.exist(_.find(resources.css, {
path: 'gitbook-plugin-resources/test'
}));
});
});
});
@@ -97,8 +135,8 @@ describe('Plugins', function () {
var plugin;
before(function() {
plugin = new Plugin(book, "filters");
plugin.load("./filters", PLUGINS_ROOT);
plugin = new Plugin(book, 'filters');
plugin.load('./filters', PLUGINS_ROOT);
return book.plugins.load(plugin);
});
@@ -111,21 +149,21 @@ describe('Plugins', function () {
var filters = plugin.getFilters();
_.size(filters).should.equal(2);
filters.should.have.property("hello");
filters.should.have.property("helloCtx");
filters.should.have.property('hello');
filters.should.have.property('helloCtx');
});
it('should correctly extend template filters', function() {
return book.template.renderString('{{ "World"|hello }}')
return book.template.renderString('{{ \'World\'|hello }}')
.then(function(content) {
content.should.equal("Hello World");
content.should.equal('Hello World');
});
});
it('should correctly set book as context', function() {
return book.template.renderString('{{ "root"|helloCtx }}')
return book.template.renderString('{{ \'root\'|helloCtx }}')
.then(function(content) {
content.should.equal("root:"+book.root);
content.should.equal('root:'+book.root);
});
});
});
@@ -134,15 +172,15 @@ describe('Plugins', function () {
var plugin;
before(function() {
plugin = new Plugin(book, "blocks");
plugin.load("./blocks", PLUGINS_ROOT);
plugin = new Plugin(book, 'blocks');
plugin.load('./blocks', PLUGINS_ROOT);
return book.plugins.load(plugin);
});
var testTpl = function(str, args, options) {
return book.template.renderString(str, args, options)
.then(book.template.postProcess)
.then(book.template.postProcess);
};
it('should valid a plugin', function() {
@@ -152,37 +190,139 @@ describe('Plugins', function () {
it('should correctly extend template blocks', function() {
return testTpl('{% test %}hello{% endtest %}')
.then(function(content) {
content.should.equal("testhellotest");
content.should.equal('testhellotest');
});
});
it('should correctly accept shortcuts', function() {
return testTpl('$$hello$$', {}, {
type: "markdown"
describe('Shortcuts', function() {
it('should correctly accept shortcuts', function() {
return testTpl('$$hello$$', {}, {
type: 'markdown'
})
.then(function(content) {
content.should.equal("testhellotest");
content.should.equal('testhellotest');
});
});
it('should correctly apply shortcuts to included file', function() {
return books.generate('conrefs', 'website', {
testId: 'include-plugins',
prepare: function(bookConref) {
plugin = new Plugin(bookConref, 'blocks');
plugin.load('./blocks', PLUGINS_ROOT);
return bookConref.plugins.load(plugin);
}
})
.then(function(bookConref) {
var readme = fs.readFileSync(
path.join(bookConref.options.output, 'index.html'),
{ encoding: 'utf-8' }
);
readme.should.be.html({
'.page-inner p#test-plugin-block-shortcuts-1': {
count: 1,
text: 'testtest_block1test',
trim: true
},
'.page-inner p#test-plugin-block-shortcuts-2': {
count: 1,
text: 'testtest_block2test',
trim: true
}
});
});
});
});
it('should correctly extend template blocks with defined end', function() {
return testTpl('{% test2 %}hello{% endtest2end %}')
.then(function(content) {
content.should.equal("test2hellotest2");
content.should.equal('test2hellotest2');
});
});
it('should correctly extend template blocks with sub-blocks', function() {
return testTpl('{% test3join separator=";" %}hello{% also %}world{% endtest3join %}')
.then(function(content) {
content.should.equal("hello;world");
content.should.equal('hello;world');
});
});
it('should correctly extend template blocks with different sub-blocks', function() {
return testTpl('{% test4join separator=";" %}hello{% also %}the{% finally %}world{% endtest4join %}')
.then(function(content) {
content.should.equal("hello;the;world");
content.should.equal('hello;the;world');
});
});
it('should correctly extend template blocks with arguments (1)', function() {
return testTpl('{% test5args "a" %}{% endtest5args %}')
.then(function(content) {
content.should.equal('test5atest5');
});
});
it('should correctly extend template blocks with arguments (2)', function() {
return testTpl('{% test5args "a", "b" %}{% endtest5args %}')
.then(function(content) {
content.should.equal('test5a,btest5');
});
});
it('should correctly extend template blocks with arguments (3)', function() {
return testTpl('{% test5args "a", "b", "c" %}{% endtest5args %}')
.then(function(content) {
content.should.equal('test5a,b,ctest5');
});
});
it('should correctly extend template blocks with args and kwargs', function() {
return testTpl('{% test5kwargs "a", "b", "c", d="test", e="test2" %}{% endtest5kwargs %}')
.then(function(content) {
content.should.equal('test5a,b,c,d:test,e:test2,__keywords:truetest5');
});
});
it('should correctly extend template blocks with access to context', function() {
return testTpl('{% set name = "john" %}{% test6context %}{% endtest6context %}', {})
.then(function(content) {
content.should.equal('test6johntest6');
});
});
});
describe('Blocks without parsing', function() {
var plugin;
before(function() {
plugin = new Plugin(book, 'blocks');
plugin.load('./blocks', PLUGINS_ROOT);
return book.plugins.load(plugin);
});
var testTpl = function(markup, str, args, options) {
var filetype = parsers.get(markup);
return book.template.renderString(str, args, options)
.then(filetype.page).get('sections').get(0).get('content')
.then(book.template.postProcess);
};
it('should correctly process unparsable for markdown', function() {
return testTpl('.md', '{% test %}**hello**{% endtest %}')
.then(function(content) {
content.should.equal('<p>test**hello**test</p>\n');
});
});
it('should correctly process unparsable for asciidoc', function() {
return testTpl('.adoc', '{% test %}**hello**{% endtest %}')
.then(function(content) {
content.should.equal('<div class="paragraph">\n<p>test**hello**test</p>\n</div>');
});
});
});
+21 -1
View File
@@ -36,6 +36,26 @@ module.exports = {
assert(blk.blocks[1].name, "finally");
return [blk.body, blk.blocks[0].body, blk.blocks[1].body].join(blk.kwargs.separator);
}
}
},
"test5args": {
process: function(blk) {
return "test5"+blk.args.join(",")+"test5";
}
},
"test5kwargs": {
process: function(blk) {
var s = blk.args.join(",");
for (var key in blk.kwargs) {
s = s + ","+key+":"+blk.kwargs[key];
}
return "test5"+s+"test5";
}
},
"test6context": {
process: function() {
return "test6"+(this.ctx.name)+"test6";
}
},
}
};
+1
View File
@@ -0,0 +1 @@
module.exports = {};
+21
View File
@@ -0,0 +1,21 @@
{
"name": "gitbook-plugin-testconfig",
"description": "Test plugin configuration",
"main": "index.js",
"version": "0.0.1",
"engines": {
"gitbook": "*"
},
"gitbook": {
"properties": {
"hello": {
"type": "string",
"default": "world"
},
"testRequired": {
"type": "number",
"required": true
}
}
}
}
+14
View File
@@ -0,0 +1,14 @@
module.exports = {
blocks: {
"code": {
process: function(blk) {
var lang = blk.kwargs.language || "code";
return {
body: lang+"_"+blk.body+"_"+lang,
html: false
};
}
}
}
};
@@ -0,0 +1,9 @@
{
"name": "gitbook-plugin-replace_highlight",
"description": "Test replacing default code highlighter",
"main": "index.js",
"version": "0.0.1",
"engines": {
"gitbook": "*"
}
}
+60
View File
@@ -0,0 +1,60 @@
var path = require("path");
describe("Resolve Files", function () {
var book;
before(function() {
return books.parse("basic")
.then(function(_book) {
book = _book;
});
});
describe("book.fileIsInBook", function() {
it("should return true for correct paths", function() {
book.fileIsInBook(path.join(book.root, "README.md")).should.equal(true);
book.fileIsInBook(path.join(book.root, "styles/website.css")).should.equal(true);
});
it("should return true for root folder", function() {
book.fileIsInBook(path.join(book.root, "./")).should.equal(true);
book.fileIsInBook(book.root).should.equal(true);
});
it("should return false for files out of scope", function() {
book.fileIsInBook(path.join(book.root, "../")).should.equal(false);
book.fileIsInBook("README.md").should.equal(false);
book.fileIsInBook(path.resolve(book.root, "../README.md")).should.equal(false);
});
it("should correctly handle windows paths", function() {
book.fileIsInBook(path.join(book.root, "\\styles\\website.css")).should.equal(true);
});
});
describe("book.resolve", function() {
it("should resolve a file to its absolute path", function() {
book.resolve("README.md").should.equal(path.resolve(book.root, "README.md"));
book.resolve("website/README.md").should.equal(path.resolve(book.root, "website/README.md"));
});
it("should correctly handle windows paths", function() {
book.resolve("styles\\website.css").should.equal(path.resolve(book.root, "styles\\website.css"));
});
it("should correctly resolve all arguments", function() {
book.resolve("test", "hello", "..", "README.md").should.equal(path.resolve(book.root, "test/README.md"));
});
it("should correctly resolve to root folder", function() {
book.resolve("test", "/README.md").should.equal(path.resolve(book.root, "README.md"));
book.resolve("test", "\\README.md").should.equal(path.resolve(book.root, "README.md"));
});
it("should throw an error for file out of book", function() {
(function() {
return book.resolve("../README.md");
}).should.throw();
});
});
});
+20
View File
@@ -0,0 +1,20 @@
describe('Structure', function () {
var book;
before(function() {
return books.parse('structure')
.then(function(_book) {
book = _book;
});
});
it('should prioritize structure defined in book.json', function() {
book.readmeFile.should.equal('README.adoc');
});
it('should be case incensitive', function() {
book.glossaryFile.should.equal('glossary.md');
book.glossary.should.have.lengthOf(1);
});
});
+12 -12
View File
@@ -1,8 +1,8 @@
var fs = require('fs');
var path = require('path');
var fs = require("fs");
var path = require("path");
describe('Summary', function () {
describe('Parsing', function() {
describe("Summary", function () {
describe("Parsing", function() {
var book;
before(function() {
@@ -12,13 +12,13 @@ describe('Summary', function () {
});
});
it('should correctly list items', function() {
it("should correctly list items", function() {
book.should.have.property("summary");
book.summary.should.have.property("chapters");
book.summary.chapters.should.have.lengthOf(4);
});
it('should correctly mark non-existant entries', function() {
it("should correctly mark non-existant entries", function() {
book.summary.chapters[0].exists.should.have.equal(true);
book.summary.chapters[1].exists.should.have.equal(true);
book.summary.chapters[2].exists.should.have.equal(true);
@@ -26,7 +26,7 @@ describe('Summary', function () {
});
});
describe('Generation', function() {
describe("Generation", function() {
var book;
before(function() {
@@ -36,30 +36,30 @@ describe('Summary', function () {
});
});
it('should create files according to summary', function() {
it("should create files according to summary", function() {
book.should.have.file("index.html");
book.should.have.file("PAGE1.html");
book.should.have.file("folder/PAGE2.html");
});
it('should correctly output summary', function() {
it("should correctly output summary", function() {
var PAGE = fs.readFileSync(
path.join(book.options.output, "index.html"),
{ encoding: "utf-8" }
);
PAGE.should.be.html({
".book-summary .chapter[data-level='0'] a": {
".book-summary .chapter[data-level=\"0\"] a": {
attributes: {
href: "./index.html"
}
},
".book-summary .chapter[data-level='1'] a": {
".book-summary .chapter[data-level=\"1\"] a": {
attributes: {
href: "./PAGE1.html"
}
},
".book-summary .chapter[data-level='2'] a": {
".book-summary .chapter[data-level=\"2\"] a": {
attributes: {
href: "./folder/PAGE2.html"
}
+7 -7
View File
@@ -1,6 +1,6 @@
var pkg = require("../package.json");
describe('Templating', function () {
describe("Templating", function () {
var book;
before(function() {
@@ -12,19 +12,19 @@ describe('Templating', function () {
var testTpl = function(str, args, options) {
return book.template.renderString(str, args, options)
.then(book.template.postProcess)
.then(book.template.postProcess);
};
describe('Context', function() {
it('should correctly have access to generator', function() {
return testTpl('{{ gitbook.generator }}')
describe("Context", function() {
it("should correctly have access to generator", function() {
return testTpl("{{ gitbook.generator }}")
.then(function(content) {
content.should.equal("website");
});
});
it('should correctly have access to gitbook version', function() {
return testTpl('{{ gitbook.version }}')
it("should correctly have access to gitbook version", function() {
return testTpl("{{ gitbook.version }}")
.then(function(content) {
content.should.equal(pkg.version.toString());
});
+9 -8
View File
@@ -1,25 +1,26 @@
var fs = require('fs');
var path = require('path');
describe('Website generator', function () {
describe('Basic Book', function() {
var book;
before(function() {
return books.generate("basic", "website")
return books.generate('basic', 'website')
.then(function(_book) {
book = _book;
});
});
it('should correctly output an index.html', function() {
book.should.have.file("index.html");
book.should.have.file('index.html');
});
it('should correctly copy assets', function() {
book.should.have.file("gitbook");
book.should.have.file("gitbook/app.js");
book.should.have.file("gitbook/style.css");
book.should.have.file('gitbook');
book.should.have.file('gitbook/app.js');
book.should.have.file('gitbook/style.css');
});
it('should not copy ebook assets', function() {
book.should.not.have.file('gitbook/ebook.css');
});
});
});
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
.section.glossary>h1,.section.toc>h1{text-align:center}.page .section b,.page .section dl dt,.page .section strong,.page .section table th{font-weight:700}.link-inherit,.link-inherit:focus,.link-inherit:hover{color:inherit}.hidden{display:none}.section.toc>ol{margin:0;padding:0}.section.toc li{list-style:none}.section.toc li .inner{display:block;border-bottom:1px dotted #eee;margin-bottom:4px}.section.toc li .inner a,.section.toc li .inner span{padding-right:5px;border-bottom:1px solid #fff;margin-bottom:-1px}.section.toc li .inner .page{float:right;padding-left:5px}.section.toc li ol{margin:0;padding:0 0 0 2em}.dir-rtl .section.toc li .inner .page{float:left}.dir-rtl .section.toc li ol{margin:0;padding:0 2em 0 0}.section.glossary .glossary-entry{margin-bottom:40px}.section.glossary .glossary-entry h2 a,.section.glossary .glossary-entry h2 a:hover{color:inherit;text-decoration:none}.page .section,body{color:#000;text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.section.glossary .glossary-entry .glossary-index{list-style:none;margin:0;padding:0}.section.glossary .glossary-entry .glossary-index li{display:inline;margin:0 8px;white-space:nowrap}body{font-family:sans-serif}.page .section{display:block;word-wrap:break-word;overflow:hidden;line-height:1.6;-moz-text-size-adjust:100%}.page .section *{box-sizing:border-box;-webkit-box-sizing:border-box;font-size:inherit}.page .section>:first-child{margin-top:0!important}.page .section>:last-child{margin-bottom:0!important}.page .section blockquote,.page .section code,.page .section figure,.page .section img,.page .section pre,.page .section table,.page .section tr{page-break-inside:avoid}.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section p{orphans:3;widows:3}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5{page-break-after:avoid}.page .section em{font-style:italic}.page .section blockquote,.page .section dl,.page .section ol,.page .section p,.page .section table,.page .section ul{margin-top:0;margin-bottom:.8em}.page .section a{color:#4183c4;text-decoration:none;background:0 0}.page .section a:active,.page .section a:focus,.page .section a:hover{outline:0;text-decoration:underline}.page .section img{border:0;max-width:100%}.page .section hr{height:4px;padding:0;margin:1.6em 0;overflow:hidden;background-color:#e7e7e7;border:none}.page .section hr:after,.page .section hr:before{display:table;content:" "}.page .section hr:after{clear:both}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section h6{margin-top:1.2em;margin-bottom:.8em;font-weight:700}.page .section h1{font-size:2em}.page .section h2{font-size:1.75em}.page .section h3{font-size:1.5em}.page .section h4{font-size:1.25em}.page .section h5{font-size:1em}.page .section h6{font-size:1em;color:#777}.page .section code,.page .section pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;direction:ltr;border:none;color:inherit}.page .section pre{overflow:auto;word-wrap:normal;margin:0 0 1.2em;padding:.85em 1em;background:#f7f7f7}.page .section pre>code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;font-size:.85em;white-space:pre;background:0 0}.page .section pre>code:after,.page .section pre>code:before{content:normal}.page .section code{padding:.2em;margin:0;font-size:.85em;background-color:#f7f7f7}.page .section code:after,.page .section code:before{letter-spacing:-.2em;content:"\00a0"}.page .section table{display:table;width:100%;border-collapse:collapse;border-spacing:0;overflow:auto}.page .section table td,.page .section table th{padding:6px 13px;border:1px solid #ddd}.page .section table tr{background-color:#fff;border-top:1px solid #ccc}.page .section table tr:nth-child(2n){background-color:#f8f8f8}.page .section ol,.page .section ul{padding:0 0 0 2em;margin:0 0 .8em}.page .section ol ol,.page .section ol ul,.page .section ul ol,.page .section ul ul{margin-top:0;margin-bottom:0}.page .section ol ol{list-style-type:lower-roman}.page .section blockquote{margin:0 0 .8em;padding:0 15px;color:#858585;border-left:4px solid #e5e5e5}.page .section blockquote:first-child{margin-top:0}.page .section blockquote:last-child{margin-bottom:0}.page .section dl{padding:0}.page .section dl dt{padding:0;margin-top:.8em;font-style:italic}.page .section dl dd{padding:0 .8em;margin-bottom:.8em}.page .section dd{margin-left:0}.page .book-chapter{display:none}
+1
View File
@@ -0,0 +1 @@
.section.glossary>h1,.section.toc>h1{text-align:center}.page .section b,.page .section dl dt,.page .section strong,.page .section table th{font-weight:700}.link-inherit,.link-inherit:focus,.link-inherit:hover{color:inherit}.hidden{display:none}.section.toc>ol{margin:0;padding:0}.section.toc li{list-style:none}.section.toc li .inner{display:block;border-bottom:1px dotted #eee;margin-bottom:4px}.section.toc li .inner a,.section.toc li .inner span{padding-right:5px;border-bottom:1px solid #fff;margin-bottom:-1px}.section.toc li .inner .page{float:right;padding-left:5px}.section.toc li ol{margin:0;padding:0 0 0 2em}.dir-rtl .section.toc li .inner .page{float:left}.dir-rtl .section.toc li ol{margin:0;padding:0 2em 0 0}.section.glossary .glossary-entry{margin-bottom:40px}.section.glossary .glossary-entry h2 a,.section.glossary .glossary-entry h2 a:hover{color:inherit;text-decoration:none}.page .section,body{color:#000;text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.section.glossary .glossary-entry .glossary-index{list-style:none;margin:0;padding:0}.section.glossary .glossary-entry .glossary-index li{display:inline;margin:0 8px;white-space:nowrap}body{font-family:serif}.page .section{display:block;word-wrap:break-word;overflow:hidden;line-height:1.6;-moz-text-size-adjust:100%}.page .section *{box-sizing:border-box;-webkit-box-sizing:border-box;font-size:inherit}.page .section>:first-child{margin-top:0!important}.page .section>:last-child{margin-bottom:0!important}.page .section blockquote,.page .section code,.page .section figure,.page .section img,.page .section pre,.page .section table,.page .section tr{page-break-inside:avoid}.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section p{orphans:3;widows:3}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5{page-break-after:avoid}.page .section em{font-style:italic}.page .section blockquote,.page .section dl,.page .section ol,.page .section p,.page .section table,.page .section ul{margin-top:0;margin-bottom:.8em}.page .section a{color:#4183c4;text-decoration:none;background:0 0}.page .section a:active,.page .section a:focus,.page .section a:hover{outline:0;text-decoration:underline}.page .section img{border:0;max-width:100%}.page .section hr{height:4px;padding:0;margin:1.6em 0;overflow:hidden;background-color:#e7e7e7;border:none}.page .section hr:after,.page .section hr:before{display:table;content:" "}.page .section hr:after{clear:both}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section h6{margin-top:1.2em;margin-bottom:.8em;font-weight:700}.page .section h1{font-size:2em}.page .section h2{font-size:1.75em}.page .section h3{font-size:1.5em}.page .section h4{font-size:1.25em}.page .section h5{font-size:1em}.page .section h6{font-size:1em;color:#777}.page .section code,.page .section pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;direction:ltr;border:none;color:inherit}.page .section pre{overflow:auto;word-wrap:normal;margin:0 0 1.2em;padding:.85em 1em;background:#f7f7f7}.page .section pre>code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;font-size:.85em;white-space:pre;background:0 0}.page .section pre>code:after,.page .section pre>code:before{content:normal}.page .section code{padding:.2em;margin:0;font-size:.85em;background-color:#f7f7f7}.page .section code:after,.page .section code:before{letter-spacing:-.2em;content:"\00a0"}.page .section table{display:table;width:100%;border-collapse:collapse;border-spacing:0;overflow:auto}.page .section table td,.page .section table th{padding:6px 13px;border:1px solid #ddd}.page .section table tr{background-color:#fff;border-top:1px solid #ccc}.page .section table tr:nth-child(2n){background-color:#f8f8f8}.page .section ol,.page .section ul{padding:0 0 0 2em;margin:0 0 .8em}.page .section ol ol,.page .section ol ul,.page .section ul ol,.page .section ul ul{margin-top:0;margin-bottom:0}.page .section ol ol{list-style-type:lower-roman}.page .section blockquote{margin:0 0 .8em;padding:0 15px;color:#858585;border-left:4px solid #e5e5e5}.page .section blockquote:first-child{margin-top:0}.page .section blockquote:last-child{margin-bottom:0}.page .section dl{padding:0}.page .section dl dt{padding:0;margin-top:.8em;font-style:italic}.page .section dl dd{padding:0 .8em;margin-bottom:.8em}.page .section dd{margin-left:0}.page .book-chapter{display:none}
+1
View File
@@ -0,0 +1 @@
.section.glossary>h1,.section.toc>h1{text-align:center}.page .section,body{text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.page .section b,.page .section dl dt,.page .section strong,.page .section table th{font-weight:700}.link-inherit,.link-inherit:focus,.link-inherit:hover{color:inherit}.hidden{display:none}.section.toc>ol{margin:0;padding:0}.section.toc li{list-style:none}.section.toc li .inner{display:block;border-bottom:1px dotted #eee;margin-bottom:4px}.section.toc li .inner a,.section.toc li .inner span{padding-right:5px;border-bottom:1px solid #fff;margin-bottom:-1px}.section.toc li .inner .page{float:right;padding-left:5px}.section.toc li ol{margin:0;padding:0 0 0 2em}.dir-rtl .section.toc li .inner .page{float:left}.dir-rtl .section.toc li ol{margin:0;padding:0 2em 0 0}.section.glossary .glossary-entry{margin-bottom:40px}.section.glossary .glossary-entry h2 a,.section.glossary .glossary-entry h2 a:hover{color:inherit;text-decoration:none}.section.glossary .glossary-entry .glossary-index{list-style:none;margin:0;padding:0}.section.glossary .glossary-entry .glossary-index li{display:inline;margin:0 8px;white-space:nowrap}body{font-family:serif;color:#000}.page .section{display:block;word-wrap:break-word;overflow:hidden;color:#000;line-height:1.6;-moz-text-size-adjust:100%}.page .section *{box-sizing:border-box;-webkit-box-sizing:border-box;font-size:inherit}.page .section>:first-child{margin-top:0!important}.page .section>:last-child{margin-bottom:0!important}.page .section blockquote,.page .section code,.page .section figure,.page .section img,.page .section pre,.page .section table,.page .section tr{page-break-inside:avoid}.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section p{orphans:3;widows:3}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5{page-break-after:avoid}.page .section em{font-style:italic}.page .section blockquote,.page .section dl,.page .section ol,.page .section p,.page .section table,.page .section ul{margin-top:0;margin-bottom:.8em}.page .section a{color:#000;text-decoration:none;background:0 0}.page .section a:active,.page .section a:focus,.page .section a:hover{outline:0;text-decoration:underline}.page .section img{border:0;max-width:100%}.page .section hr{height:4px;padding:0;margin:1.6em 0;overflow:hidden;background-color:#e7e7e7;border:none}.page .section hr:after,.page .section hr:before{display:table;content:" "}.page .section hr:after{clear:both}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section h6{margin-top:1.2em;margin-bottom:.8em;font-weight:700}.page .section h1{font-size:2em}.page .section h2{font-size:1.75em}.page .section h3{font-size:1.5em}.page .section h4{font-size:1.25em}.page .section h5{font-size:1em}.page .section h6{font-size:1em;color:#777}.page .section code,.page .section pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;direction:ltr;border:none;color:inherit}.page .section pre{overflow:auto;word-wrap:normal;margin:0 0 1.2em;padding:.85em 1em;background:#f7f7f7}.page .section pre>code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;font-size:.85em;white-space:pre;background:0 0}.page .section pre>code:after,.page .section pre>code:before{content:normal}.page .section code{padding:.2em;margin:0;font-size:.85em;background-color:#f7f7f7}.page .section code:after,.page .section code:before{letter-spacing:-.2em;content:"\00a0"}.page .section table{display:table;width:100%;border-collapse:collapse;border-spacing:0;overflow:auto}.page .book-chapter,.page-toc li .inner span.page{display:none}.page .section table td,.page .section table th{padding:6px 13px;border:1px solid #ddd}.page .section table tr{background-color:#fff;border-top:1px solid #ccc}.page .section table tr:nth-child(2n){background-color:#f8f8f8}.page .section ol,.page .section ul{padding:0 0 0 2em;margin:0 0 .8em}.page .section ol ol,.page .section ol ul,.page .section ul ol,.page .section ul ul{margin-top:0;margin-bottom:0}.page .section ol ol{list-style-type:lower-roman}.page .section blockquote{margin:0 0 .8em;padding:0 15px;color:#858585;border-left:4px solid #e5e5e5}.page .section blockquote:first-child{margin-top:0}.page .section blockquote:last-child{margin-bottom:0}.page .section dl{padding:0}.page .section dl dt{padding:0;margin-top:.8em;font-style:italic}.page .section dl dd{padding:0 .8em;margin-bottom:.8em}.page .section dd{margin-left:0}a{color:#000;text-decoration:underline!important}.page-toc li{list-style-type:lower-alpha}.page-toc li .inner{border:none}
+1
View File
@@ -0,0 +1 @@
.section.glossary>h1,.section.toc>h1{text-align:center}.page .section b,.page .section dl dt,.page .section strong,.page .section table th{font-weight:700}.link-inherit,.link-inherit:focus,.link-inherit:hover{color:inherit}.hidden{display:none}.section.toc>ol{margin:0;padding:0}.section.toc li{list-style:none}.section.toc li .inner{display:block;border-bottom:1px dotted #eee;margin-bottom:4px}.section.toc li .inner a,.section.toc li .inner span{padding-right:5px;border-bottom:1px solid #fff;margin-bottom:-1px}.section.toc li .inner .page{float:right;padding-left:5px}.section.toc li ol{margin:0;padding:0 0 0 2em}.dir-rtl .section.toc li .inner .page{float:left}.dir-rtl .section.toc li ol{margin:0;padding:0 2em 0 0}.section.glossary .glossary-entry{margin-bottom:40px}.section.glossary .glossary-entry h2 a,.section.glossary .glossary-entry h2 a:hover{color:inherit;text-decoration:none}.page .section,body{color:#000;text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.section.glossary .glossary-entry .glossary-index{list-style:none;margin:0;padding:0}.section.glossary .glossary-entry .glossary-index li{display:inline;margin:0 8px;white-space:nowrap}body{font-family:sans-serif}.page .section{display:block;word-wrap:break-word;overflow:hidden;line-height:1.6;-moz-text-size-adjust:100%}.page .section *{box-sizing:border-box;-webkit-box-sizing:border-box;font-size:inherit}.page .section>:first-child{margin-top:0!important}.page .section>:last-child{margin-bottom:0!important}.page .section blockquote,.page .section code,.page .section figure,.page .section img,.page .section pre,.page .section table,.page .section tr{page-break-inside:avoid}.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section p{orphans:3;widows:3}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5{page-break-after:avoid}.page .section em{font-style:italic}.page .section blockquote,.page .section dl,.page .section ol,.page .section p,.page .section table,.page .section ul{margin-top:0;margin-bottom:.8em}.page .section a{color:#4183c4;text-decoration:none;background:0 0}.page .section a:active,.page .section a:focus,.page .section a:hover{outline:0;text-decoration:underline}.page .section img{border:0;max-width:100%}.page .section hr{height:4px;padding:0;margin:1.6em 0;overflow:hidden;background-color:#e7e7e7;border:none}.page .section hr:after,.page .section hr:before{display:table;content:" "}.page .section hr:after{clear:both}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section h6{margin-top:1.2em;margin-bottom:.8em;font-weight:700}.page .section h1{font-size:2em}.page .section h2{font-size:1.75em}.page .section h3{font-size:1.5em}.page .section h4{font-size:1.25em}.page .section h5{font-size:1em}.page .section h6{font-size:1em;color:#777}.page .section code,.page .section pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;direction:ltr;border:none;color:inherit}.page .section pre{overflow:auto;word-wrap:normal;margin:0 0 1.2em;padding:.85em 1em;background:#f7f7f7}.page .section pre>code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;font-size:.85em;white-space:pre;background:0 0}.page .section pre>code:after,.page .section pre>code:before{content:normal}.page .section code{padding:.2em;margin:0;font-size:.85em;background-color:#f7f7f7}.page .section code:after,.page .section code:before{letter-spacing:-.2em;content:"\00a0"}.page .section table{display:table;width:100%;border-collapse:collapse;border-spacing:0;overflow:auto}.page .section table td,.page .section table th{padding:6px 13px;border:1px solid #ddd}.page .section table tr{background-color:#fff;border-top:1px solid #ccc}.page .section table tr:nth-child(2n){background-color:#f8f8f8}.page .section ol,.page .section ul{padding:0 0 0 2em;margin:0 0 .8em}.page .section ol ol,.page .section ol ul,.page .section ul ol,.page .section ul ul{margin-top:0;margin-bottom:0}.page .section ol ol{list-style-type:lower-roman}.page .section blockquote{margin:0 0 .8em;padding:0 15px;color:#858585;border-left:4px solid #e5e5e5}.page .section blockquote:first-child{margin-top:0}.page .section blockquote:last-child{margin-bottom:0}.page .section dl{padding:0}.page .section dl dt{padding:0;margin-top:.8em;font-style:italic}.pdf-footer,.pdf-header{margin-top:20px;color:#aaa}.page .section dl dd{padding:0 .8em;margin-bottom:.8em}.page .section dd{margin-left:0}.page .book-chapter{display:none}.pdf-footer{padding-top:10px;border-top:1px solid #eee}.pdf-footer .footer-pages-count{float:right}.pdf-header{padding-bottom:10px;border-bottom:1px solid #eee}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

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