Compare commits

..

418 Commits

Author SHA1 Message Date
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
Samy Pessé d895c63211 Bump version to 2.1.0 2015-06-02 14:52:11 +02:00
Samy Pessé 8c86da67ca Update gitbook-parsers@0.7.6 2015-06-02 14:47:21 +02:00
Samy Pessé a9a230dfc3 Merge pull request #781 from GitbookIO/fix/775
Fix #775: add optional print.css style
2015-06-02 11:00:22 +02:00
Samy Pessé 6d53e52771 Handle empty string for styles configuration 2015-06-02 11:00:15 +02:00
Samy Pessé 52e10b7cfe Remove default print.css when styles/print.css is present 2015-06-02 10:23:31 +02:00
Samy Pessé addac85696 Add test for custom print style 2015-06-02 10:18:44 +02:00
Samy Pessé 13fb949d68 Merge pull request #774 from wilhelmmatilainen/patch-1
Styling should go to <head>, not to <body>
2015-05-30 18:11:15 +02:00
Wilhelm f1b593cfbd Styling should go to <head>, not to <body>
https://developers.google.com/speed/pagespeed/module/filter-css-to-head
2015-05-29 15:42:40 +03:00
Samy Pesse f998e2dec9 Bump version to 2.0.4 2015-05-28 18:07:20 +02:00
Samy Pesse 2c0bd53a91 Fix i18n used in multilingual book 2015-05-28 17:06:09 +02:00
Samy Pesse 6dd7f6162f Add title to languages chooser
Related to #757
2015-05-28 16:14:03 +02:00
Samy Pesse 475b0bb2b3 Adapt to nunjucks 1.3.x 2015-05-28 15:59:14 +02:00
Samy Pesse e6343935e3 Update nunjucks to fix raw tags 2015-05-28 15:50:46 +02:00
Samy Pessé 946883fd4c Merge pull request #772 from GitbookIO/feature/glossary_order
Fix #768: sort glossary, start with longest matchs
2015-05-28 15:21:03 +02:00
Samy Pessé b1b7e3148b Merge pull request #743 from chudaol/master
Added vk sharing button.
2015-05-28 14:54:03 +02:00
Samy Pesse 32203f6438 Sort glossary before applying it on pages 2015-05-28 14:49:53 +02:00
Samy Pesse f2d3c5d2ed Add test for #768 2015-05-28 14:38:21 +02:00
Samy Pessé 7e2f0ed4bc Merge pull request #758 from aschempp/hotfix/x-ua-compatible
Fix the X-UA-Compatible meta tag
2015-05-28 14:18:19 +02:00
Samy Pessé a877acf905 Merge pull request #759 from aschempp/hotfix/stylesheets
Move style sheets to the <head> section
2015-05-28 14:17:39 +02:00
Samy Pesse ac756ddf7e Bump version to 2.0.3 2015-05-27 21:11:58 +02:00
Samy Pesse 36e2236e6b Fix #765: in init handle correctly empty entries
Add tests for it, fix #769
2015-05-27 21:05:36 +02:00
Samy Pesse 72df665520 Update gitbook-parsers@0.7.5 2015-05-27 19:11:50 +02:00
Samy Pessé 07785528a5 Merge pull request #767 from wilhelmmatilainen/patch-1
Don't create panic by telling about false overwrite
2015-05-27 13:17:05 +02:00
Wilhelm bb579dd581 Don't create panic by telling about false overwrite 2015-05-27 14:13:02 +03:00
Samy Pessé a7d3064340 Change license to valid SPDX code 2015-05-26 16:10:11 +02:00
Andreas Schempp 1a60778248 Move style sheets to the <head> section
see https://github.com/GitbookIO/gitbook/issues/756
2015-05-23 09:14:26 +02:00
Andreas Schempp cda4bb5bda Fix the X-UA-Compatible meta tag
see https://github.com/GitbookIO/gitbook/issues/755
2015-05-23 09:06:58 +02:00
Olga Filipova 7543781046 Added vk sharing button. 2015-05-12 22:30:09 +02:00
Samy Pessé 82f3ffad06 Merge pull request #730 from creynders/patch-1
Fix typo in warning in `lib/plugin`
2015-05-04 14:47:56 +02:00
creynders dc4d2a1731 Fix typo in warning in lib/plugin 2015-05-04 13:16:47 +02:00
Samy Pessé 2523925915 Bump version to 2.0.2 2015-05-02 12:39:05 +02:00
Samy Pesse d5bbd8475c Fix #721: use links.join instead of path.join for links in page 2015-04-23 12:58:24 -05:00
Samy Pesse 54d195268a Add test relative links (related to #721) 2015-04-23 12:05:04 -05:00
Samy Pesse a0deb9509f Merge branch 'awrelll-master' 2015-04-20 07:53:16 -05:00
Samy Pesse b0b1433594 Fix authors list merge 2015-04-20 07:52:12 -05:00
Samy Pessé 17a2c0a37c Merge pull request #706 from savikko/patch-1
Added Finnish author
2015-04-14 18:56:31 -05:00
Samy Pessé fd68ea1cc2 Merge pull request #705 from savikko/master
Added Finnish
2015-04-14 18:56:21 -05:00
Tommi Savikko 784874c776 Added Finnish author 2015-04-14 22:39:23 +03:00
Thomas Clayton f45c6d3466 Added Finnish 2015-04-14 21:50:46 +03:00
Samy Pessé 98af70902f Merge pull request #701 from anthonyfok/patch-1
Fix typo in lib/book.js: lanuages → languages
2015-04-10 20:50:35 -05:00
Anthony Fok 41a67b5c4c Fix typo in lib/book.js: lanuages → languages 2015-04-10 07:41:12 -06:00
Samy Pessé 83ee903af4 Merge pull request #700 from paulmillr/master
Switch from gaze to chokidar.
2015-04-10 10:39:13 +02:00
Samy Pessé e591b0e485 Merge pull request #696 from sindresorhus/escaperegex-module
use the `escape-string-regexp` module
2015-04-10 10:37:53 +02:00
Paul Miller bcc9a34742 Switch from gaze to chokidar. 2015-04-10 06:09:04 +03:00
Sindre Sorhus 6b56c7de7f use the escape-string-regexp module
no point in hardcoding this.

https://github.com/sindresorhus/escape-string-regexp
2015-04-08 18:24:26 +07:00
Iancu Aurel 7b42719699 korean and japanese added 2015-04-07 17:34:40 +03:00
awrelll 64b8343f67 korean support added 2015-04-07 17:32:33 +03:00
awrelll fcc5089fa6 japan support added 2015-04-07 17:26:45 +03:00
Samy Pessé 334f782aa8 Merge pull request #693 from awrelll/patch-1
Romanian author added
2015-04-07 16:21:56 +02:00
Samy Pessé 1776f2bf77 Merge pull request #691 from awrelll/master
support for romanian language
2015-04-07 16:21:34 +02:00
Iancu Aurel e259412f79 Romanian author added 2015-04-07 17:21:05 +03:00
awrelll 999a02f110 support for romanian language 2015-04-07 17:12:41 +03:00
Samy Pessé 604d0fba65 Update nunjucks@1.3.0 2015-04-03 22:31:55 +02:00
Samy Pessé 0e8f7b870b Bump version to 2.0.1 2015-04-02 19:41:14 +02:00
Samy Pessé 1eebc9a46e Normalize structure parsing error 2015-04-02 19:21:39 +02:00
Samy Pessé 0a94c80c44 Normalize template errors 2015-04-02 18:59:56 +02:00
Samy Pessé c70bb2fbd8 Update gitbook-parsers@0.7.4 2015-04-02 18:31:49 +02:00
Samy Pessé 12c06b384f Don't fail if config doesn't exists 2015-04-02 09:43:48 +02:00
Samy Pessé 82a58e7ac3 Invalidate configuration cache when reloading 2015-04-02 09:38:57 +02:00
Samy Pessé dcb56ef433 Merge pull request #683 from GitbookIO/fix/i18n
Fix i18n in json format
2015-04-01 22:23:23 +02:00
Samy Pessé 8da91de514 Don't update translations 2015-04-01 22:21:13 +02:00
Samy Pessé 4b20685562 Use i18n node module for template translation 2015-04-01 22:17:12 +02:00
Samy Pessé fff7c5cc3a Add back support for options.originalInput
Related to rtCamp/gitbook-plugin-edit-link#2
2015-03-30 16:50:26 +02:00
Samy Pessé 3065742b73 Separate book.contentLink and book.contentPath 2015-03-28 15:54:34 +01:00
Samy Pessé ffd69ad1f2 Add test for summary paths
Related to #674
2015-03-28 15:28:28 +01:00
Samy Pessé 9004011479 Merge pull request #673 from lszeremeta/master
Polish (pl) added in theme/i18n
2015-03-28 14:54:50 +01:00
Łukasz Szeremeta 1046bb4133 Add @lszeremeta to translators 2015-03-28 14:48:16 +01:00
Łukasz Szeremeta 5578505213 Polish (pl) added in theme/i18n 2015-03-27 22:03:28 +01:00
Samy Pessé a61f56662a Bump version to 2.0.0 2015-03-25 10:43:58 +01:00
Samy Pessé f42e48be03 Fix scroll reset on small resolutions (mobile & tablet) 2015-03-24 23:59:03 +01:00
Samy Pessé f640e101ba Fix scroling position when preparing page 2015-03-24 23:52:41 +01:00
Samy Pessé b7da276ae0 Fix title for introduction (with i18n) 2015-03-24 23:49:12 +01:00
Samy Pessé bf7c0989f5 Update gitbook-parsers@0.7.2 2015-03-24 23:35:17 +01:00
Samy Pessé 65fdbe7f55 Simplify examples of commands 2015-03-24 23:19:00 +01:00
Samy Pessé c9af209a93 Merge pull request #667 from GitbookIO/better-testing
Better Unit Tests
2015-03-24 23:14:17 +01:00
Samy Pessé 48ab44a776 Add tests for conrefs 2015-03-24 18:22:44 +01:00
Samy Pessé b2e684af8f Add test for templating context 2015-03-24 17:34:47 +01:00
Samy Pessé b38a2c11a4 Add tests for plugins 2015-03-24 17:31:15 +01:00
Samy Pessé c31acef47a Add tests for images in ebook 2015-03-24 16:26:19 +01:00
Samy Pessé e9a573e18a Add test for default description from readme 2015-03-24 14:57:29 +01:00
Samy Pessé c07ddc77f7 Add tests for links 2015-03-24 14:52:24 +01:00
Samy Pessé 303fc992d4 Add test for git url parsing 2015-03-24 14:44:07 +01:00
Samy Pessé 4b7797f61b Add basic tests for multilingual books 2015-03-24 14:16:03 +01:00
Samy Pessé f775d41933 Add base tests for ebook generation 2015-03-24 13:52:29 +01:00
Samy Pessé 7722e7bdfa Update gitbook-parsers@0.7.1 2015-03-24 13:33:56 +01:00
Samy Pessé 346c05f0de Add tests for configuration parsing 2015-03-24 13:24:20 +01:00
Samy Pessé 73c69923b3 Add test for summary parsing
Update gitbook-parsers
2015-03-24 13:11:29 +01:00
Samy Pessé afbca4bdf5 Improve tests for glossary 2015-03-24 11:54:03 +01:00
Samy Pessé b61371942b Add test for glossary replacements 2015-03-24 11:27:37 +01:00
Samy Pessé 61c07e8876 Add test for glossary parsing 2015-03-24 10:24:49 +01:00
Samy Pessé d317741144 Add basic test for multilingual book 2015-03-24 09:36:51 +01:00
Samy Pessé 275739a778 Add basic test for website generation 2015-03-24 08:49:39 +01:00
Samy Pessé cb3a43ed8a Add string representation for books 2015-03-24 08:49:22 +01:00
Samy Pessé ec8d1d8fc9 Improve test for json format 2015-03-23 23:48:46 +01:00
Samy Pessé 868a3186dd Add assertion to test that a file exists 2015-03-23 23:32:07 +01:00
Samy Pessé 17b546181c Fix cleanup after tests 2015-03-23 23:23:25 +01:00
Samy Pessé 2d1aa15af8 New cleaner helper for testing 2015-03-23 23:18:15 +01:00
Samy Pessé 63ee94ff89 Ignore codeblocks when replacing glossary terms 2015-03-23 11:00:50 +01:00
Samy Pessé 7d9dc08a38 Highlight codeblocks in gitbook instead than the parsers 2015-03-23 10:31:48 +01:00
Samy Pessé a836bdedb4 Bump version to 2.0.0-beta.5 2015-03-17 11:23:35 +01:00
Samy Pessé 15ebae8cb1 Fix #641: fix progress order in json format 2015-03-17 11:22:56 +01:00
Samy Pessé 315556310f Bump version to 2.0.0-beta.4 2015-03-17 10:48:25 +01:00
Samy Pessé 9c0b26304b Merge branch master 2015-03-17 10:46:17 +01:00
Samy Pessé acdaea9f6c Fix #649: add option "author" in book.json to set meta author in html 2015-03-17 10:38:56 +01:00
Samy Pessé e481deb5bb Merge pull request #650 from wcandillon/patch-2
Update default generator
2015-03-14 16:00:09 -07:00
William Candillon 6d3ac6074c 💄 Update default generator 2015-03-14 15:45:15 -07:00
Samy Pessé 5e611bde48 Merge pull request #648 from wcandillon/patch-1
Bump to lodash 3.5.0
2015-03-14 15:25:39 -07:00
Samy Pessé 9448562156 Add editorconfig and normalize indent 2015-03-14 23:23:28 +01:00
William Candillon e7e8fe4d08 ⬆️ Bump to lodash 3.5.0 2015-03-14 11:35:28 -07:00
Samy Pessé 8320484b88 Fix #647: Fix links normalization (content and hash) 2015-03-13 23:59:37 +01:00
Samy Pessé 0e243e6f23 Bump version to 2.0.0-beta.3 2015-03-12 21:23:25 +01:00
Samy Pessé 5cd25b0d21 Update gitbook-parsers@0.5.4 2015-03-12 21:20:51 +01:00
Samy Pessé 3218ee7455 Fix tests to adapt to changes in parsers 2015-03-11 22:05:58 +01:00
Samy Pessé 0552007b8a Bump version to 2.0.0-beta.2 2015-03-11 22:03:54 +01:00
Samy Pessé 50bc89d9d9 Fix define of introduction from summary 2015-03-11 22:02:46 +01:00
Samy Pessé 743a88f53a Fix typo in bin message 2015-03-11 11:40:56 +01:00
Samy Pessé ee92c6dd99 Add changelog for 2.0.0-beta.1 2015-03-11 11:23:48 +01:00
Samy Pessé b6c54c17d1 Bump version to 2.0.0-beta.1 2015-03-11 11:17:56 +01:00
Samy Pessé 992ef1ab74 Use latest commit from nunjucks windows fix branch 2015-03-11 11:17:43 +01:00
Samy Pessé 960848ba34 Ensure inline svgs have xml header 2015-03-11 09:38:18 +01:00
Samy Pessé d9aca7a9df Fix #639: update nunjucks to support windows 2015-03-11 00:57:07 +01:00
Samy Pessé 05a0de2d86 Remove require of old dependency "shell-escape" 2015-03-11 00:50:48 +01:00
Samy Pessé 19e4f498cf Fix inline svg extraction on windows 2015-03-10 16:45:22 -07:00
Samy Pessé 3055139297 Fix run of svgexport on windows 2015-03-10 16:31:43 -07:00
Samy Pessé 3d6b099dea Fix links normalization on windows 2015-03-10 22:44:54 +01:00
Samy Pessé dd970e33b8 Remove --no-exit option from mocha tests runner 2015-03-10 21:31:43 +01:00
Samy Pessé 59ba479465 Test option --no-exit for mocha tests 2015-03-10 16:14:04 +01:00
Samy Pessé 54c9ecf3c2 Use mocha from node_modules/.bin 2015-03-10 15:44:39 +01:00
Samy Pessé 8308ae69a2 Remove command export from "npm test" 2015-03-10 15:01:51 +01:00
Samy Pessé bd6c3287ed Add badge for windows testing 2015-03-10 14:58:23 +01:00
Samy Pessé 24fc36deb1 Add appveyor.yml for windows testing
Related to #639
2015-03-10 14:52:09 +01:00
Samy Pessé 2f44b61cec Merge pull request #638 from wapmorgan/patch-1
Update README.md
2015-03-09 21:13:29 +01:00
wapmorgan 45ee503db1 Update README.md
Fix link
2015-03-10 00:12:53 +04:00
Samy Pessé dc826da99b Update README.md 2015-03-09 10:58:11 +01:00
Samy Pessé 3ea659d582 Improve readme presentation 2015-03-09 10:57:23 +01:00
Samy Pessé 34fc2831e0 Merge branch 'version/2.0' 2015-03-09 10:43:12 +01:00
Samy Pessé 1bf68a5aa0 Merge branch 'master' into version/2.0
Conflicts:
	README.md
2015-03-09 10:42:42 +01:00
Samy Pessé d72c397f23 Update gitbook-parsers@0.5.2 2015-03-08 23:09:50 +01:00
Samy Pessé 2c69525148 Add GitBook as book producer in ebook-convert 2015-03-08 22:53:18 +01:00
Samy Pessé ee3a0ba801 Fix #633: update markdown parser to fix codeblocks
Sort glossary and fix #634
2015-03-08 22:53:18 +01:00
Samy Pessé 7a4179a2a2 Merge pull request #631 from MHM5000/version/2.0
[i18n] - fa: Persian/Farsi
2015-03-07 18:48:05 +01:00
Mohammad Hossein Mojtahedi 1d0083c5f4 added @mhm5000 in translators 2015-03-07 18:22:40 +03:30
Mohammad Hossein Mojtahedi 33a6111a30 [i18n] - fa: Persian/Farsi 2015-03-07 16:48:44 +03:30
Samy Pessé a5d7a87e31 Enable rtl in ebooks 2015-03-06 22:45:53 +01:00
Samy Pessé c7f7c5b5ab Base translation for Arabic (with rtl support) 2015-03-06 22:32:45 +01:00
Samy Pessé 55a7b99b4d Fix #227: Add base rtl support for website 2015-03-06 22:32:25 +01:00
Samy Pessé 8bfba604b3 Update gitbook-parsers@0.5.0 and include page.prepare
Fix #625: escape code blocks in markdown
2015-03-06 16:54:46 +01:00
Samy Pessé 2a5fa1c5fb Fix #629: add generator name in template context 2015-03-06 14:34:55 +01:00
Samy Pessé a3e95feb26 Add instructions to use version from git 2015-03-06 13:58:23 +01:00
Samy Pessé 145b00f590 Fix #628: preserve pure anchor links and hash in relative links 2015-03-06 10:25:06 +01:00
Samy Pessé 5f2785f0a8 Escape svgexport args
Fixes shakiba/svgexport#5 and #624
2015-03-05 10:06:45 +01:00
Samy Pessé 412529c548 Merge pull request #621 from nasirkhan/version/2.0
Bengali (bn) added in theme/i18n
2015-02-27 17:48:07 +01:00
nasirkhan 547ec6ade8 bn language added 2015-02-27 22:29:10 +06:00
nasirkhan eac513cfa3 Merge remote-tracking branch 'upstream/version/2.0' into version/2.0 2015-02-27 22:16:11 +06:00
Aaron O'Mullan 5ec8a1f18c Add en.json
Fixes #620
2015-02-27 16:02:31 +01:00
Samy Pessé c923a7e01a Fix transformation of README.md links in index.html 2015-02-26 01:14:27 +01:00
Samy Pessé 366e6ed105 Bump version to 2.0.0-alpha.9 2015-02-24 13:57:20 +01:00
Samy Pessé 52078eb666 Use latest nunjucks from mozilla/nunjucks 2015-02-24 13:54:08 +01:00
Samy Pessé d9ddbff443 Fix links in sidebar 2015-02-24 13:51:10 +01:00
Samy Pessé 01b160f9a1 Fix normalization of html link (README to index) 2015-02-21 13:40:38 +01:00
Samy Pessé 23fcb817a4 Fix html snippets escaping 2015-02-20 20:52:07 +01:00
Samy Pessé f531a3f984 Update nunjucks-autoescape 2015-02-20 12:24:33 +01:00
Samy Pessé c777023e9f Add introduction to changelog 2015-02-17 14:18:49 +01:00
Samy Pessé 62c99759c1 Bump version to 2.0.0-alpha.8 2015-02-14 16:43:19 +01:00
Samy Pessé 6951c21d19 Update gitbook-parsers@0.4.1 2015-02-14 16:26:16 +01:00
Samy Pessé 3a8289483e Improve language detection from book.json 2015-02-14 15:34:19 +01:00
Samy Pessé 4079de9587 Fix #608: fix chapter name for glossary 2015-02-14 15:29:41 +01:00
Samy Pessé b86d171082 Escape description in title attr of glossary link
Related to #609
2015-02-14 11:53:30 +01:00
Samy Pessé 959b272e65 Fix #609: don't escape html in glossary 2015-02-14 11:52:33 +01:00
Samy Pessé 684440e2d1 Fix generation of multilingual book as ebook 2015-02-14 11:49:42 +01:00
Samy Pessé 4740cd3b06 Merge pull request #605 from GitbookIO/update/lodash
Update lodash to 3.x.x
2015-02-13 08:18:45 +01:00
Samy Pessé b3131d99d2 Update lodash@3.2.0 2015-02-13 08:16:28 +01:00
Samy Pessé b0fd2a67ea Fix test with post in blocks 2015-02-13 08:09:28 +01:00
Samy Pessé 7c83869869 Limit concurrent blocks processed 2015-02-13 00:04:27 +01:00
Samy Pessé 57d5c23eaf Accept post function from block to post process content 2015-02-12 23:36:22 +01:00
Samy Pessé 8d92b74e61 Fix page normalization with empty links or images 2015-02-12 18:43:06 +01:00
Samy Pessé 4e2cda99cc Complete changelog for 2.0.0-alpha.7 2015-02-12 16:40:50 +01:00
Samy Pessé 12a7a4c4cf Fix test for lodash 3.1.0 (from git) 2015-02-11 17:53:46 +01:00
Samy Pessé e1594cde2c Remove doc about custom styles 2015-01-19 13:14:52 +01:00
268 changed files with 30941 additions and 4938 deletions
+10
View File
@@ -0,0 +1,10 @@
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
+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
+32 -9
View File
@@ -21,20 +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)
- Polish
- Ł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)
- Japanese
- Iancu Aurel (@awrelll)
- Korean
- Iancu Aurel (@awrelll)
- SangYeob Yu (@deminoth)
- Vietnamese
- Hong Nguyen (@nghong)
- Hebrew
- @a-moses
- Ukrainian
- @Karnaukhov
- Czech
- @mjanda
- Swedish
- Jacob Burenstam (@buren)
+174
View File
@@ -1,4 +1,178 @@
# Release notes
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 2.6.1
- Use CamelCase for `gitbook.state.innerLanguage`
## 2.6.0
- Close sidebar after clicking a link on mobile
- Add root for multilingual books: `gitbook.state.bookRoot`
- Fix color of h6 headings
- Fix bottom margin of lists and blockquotes
- Add Swedish translation (`sv`)
- Add Czech translation (`cs`)
- Fix locale names for `zh-hans` and `jp`
- Update plugin `search` to fix crashes
- Aceept case-incensitive structure files (README, GLOSSARY, etc)
## 2.5.2
- Fix custom stylesheets for ebook generation
- Trigger event `start` before `page.change`
## 2.5.1
- Fix calcul of `gitbook.state.root` when serving HTML index as `/`
## 2.5.0
- Font settings, sharing and search are externalized as default plugins
- Plugins can define a configuration schema in the manifest, this schema will be used to validate configuration during build
- New Node.js API for plugin: `book.formatString(type, content)`
- New client side API for website plugins: `gitbook.toolbar.createButton(opts)`
- Better header/footer for PDF, CSS wil be inlined to easily style the header/footer
- Cleaner table of contents for ebooks
- Support for RTL in ebook's table of contents
- Better colors for mobi (links and code blocks)
- Fix installation of plugins when using a pre-release
- Fix support for pre-releases
- Update asciidoc parser to remove git dependency
- Fix templating in imported content
- Fix querystring in image urls
- Normalize heading IDs like GitHub
- Use Arial as default font for PDFs
- Add `root`, `chapterTitle` and `filepath` to `gitbook.state` JS API
## 2.4.3
- Add ukrainian translation (`uk`)
- Add `book.json` configuration for maximum size of search index
- Improve reliability of summary parser
## 2.4.2
- Default plugins should not be installed by `gitbook install`
- Limit search index size to avoid crash during generation
- Fix code highlighting for html without language specified
- Fix warning message for gitbook version when building a multilingual book
## 2.4.1
- Fix disabling of default plugins, ex: `-highlight`
## 2.4.0
- Fix page being updated when user wants to open a link in a new tab
- Plugins can now replaced default code highlighter
- Add semantic information for screen readers (web version)
- Content references accept absolute paths, resolved to book folder
- Improve overall reliability
## 2.3.3
- Fix bug in SUMMARY parsing preventing multiple entries without filenames
## 2.3.2
- Fix blocks (like maths) in Asciidoc
- Fix error when checking gitbook version
## 2.3.1
- Fix black font color for ebooks (mobi, pdf and epub)
- Fix ISO code for korean language
- Fix korean translation
- Fix syntax highlighting for asciidoc
- Fix inline html escaping in markdown
- Add warning for file outside SUMMARY
- Force SUMMARY entries to be unique by filename
## 2.3.0
- Fix nunjucks issue with multiple `{% raw %}` blocks
- Fix crash when git conref failed
- Fix crash when failed to download remote image (better error message)
- Fix flicking effect when changing page (big UX improvement)
- Add Hebrew translation (`he`)
- Add utility method `book.config.get` for plugins
- Hooks `page:before` and `page` are no longer deprecated
- Remove webfonts to make website lighter
- Make glossary's order case insensitive
## 2.2.0
- Fix direction in code blocks (always LTR)
- Add options `chapterMark` and `pageBreaksBefore` for PDF
- Update code highlighting library
- `book.json` accessible as `config` in templating syntax
- Add Vietnamese translation (`vi`)
## 2.1.0
- Fix error in calcul of `levels` in table of contents, error introduced a few versions ago
- Add optional `styles/print.css` to replace `print.css` used in ebook
## 2.0.4
- Fix `{% raw %}`, got confused with "fake" variable declarations
- Fix title of language chooser
- Fix the X-UA-Compatible meta tag
- Move style sheets to the <head> section
## 2.0.3
- Fix `gitbook init` for SUMMARY with empty entries
- Fix escaping of code blocks in markdown
## 2.0.2
- Fix relative links in windows
- Improve watcher in serve command (switch to chokidar)
- Add Romanian translation (`ro`)
- Add Finish translation (`fi`)
- Add Japanese translation (`jp`)
- Add Korean translation (`kr`)
## 2.0.1
- Improve error logging (display file, line and column)
- Add back support for `options.originalInput`
- Don't process math in markdown parser (delegated to `mathjax` plugin)
- Fix some cases of code blocks escaping
- Fix i18n for introduction title in json format
- Fix reload when book configuration is updated
- Fix backslashes in url when building on windows
## 2.0.0
- Fix page title of introduction
- Ignore codeblocks when replacing glossary terms
- Improve Unit Tests
- Fix scrolling position in website when preparing page
## 2.0.0-beta.5
- Fix progress order in json format
## 2.0.0-beta.4
- Fix default generator for use programmatically
- Add option "author" for html meta tags
- Fix links normalization (content and hash)
## 2.0.0-beta.3
- Fix odd cases with code blocks escaping
## 2.0.0-beta.2
- Fix definition of entry point title using SUMMARY.md
## 2.0.0-beta.1
- Fix windows incompatibility
- Add support for rtl (enabled by default for `ar` and `fa`)
- Escape code blocks in markdown parser
- Add Persian/Farsi translation (`fa`)
- Add Arabic translation (`ar`)
- Add Bengali translation (`bn`)
- Provide generator name in template context
## 2.0.0-alpha.9
- Fix links in sidebar
- Fix normalization of html link (README to index)
- Fix html snippets escaping
## 2.0.0-alpha.8
- Improve locale detection for i18n
- Fix chapter name for Glossary in pdf
- Don't escape html in glossary items
- Fix generation of multilingual book as ebook
- Add "post" block attribute to post-process
## 2.0.0-alpha.7
- Fix display of glossary in ebook formats
- Add default footer and header to pdf
- Fix generation of json format compatible with 1.x.x
- Add Simplifiled Chinese and Traditional Chinese translations
## 2.0.0-alpha.6
- Add es and pt translations
-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'
]);
};
+35 -16
View File
@@ -1,12 +1,18 @@
GitBook
=======
:warning: This branch contains the version 2.0.
[![Build Status](https://travis-ci.org/GitbookIO/gitbook.png?branch=master)](https://travis-ci.org/GitbookIO/gitbook)
[![NPM version](https://badge.fury.io/js/gitbook.svg)](http://badge.fury.io/js/gitbook)
[![Linux Build Status](https://travis-ci.org/GitbookIO/gitbook.png?branch=master)](https://travis-ci.org/GitbookIO/gitbook)
[![Windows Build status](https://ci.appveyor.com/api/projects/status/63nlflxcwmb2pue6?svg=true)](https://ci.appveyor.com/project/GitBook/gitbook)
[![Slack Status](https://slack.gitbook.com/badge.svg)](https://slack.gitbook.com)
GitBook is a command line tool (and Node.js library) for building beautiful books using GitHub/Git and Markdown (or AsciiDoc). Here is an example: [Learn Javascript](https://www.gitbook.com/book/GitBookIO/javascript). You can publish and host book easily online using [gitbook.com](https://www.gitbook.com), a web-editor is [also available](https://www.gitbook.com/editor). You can follow [@GitBookIO](https://twitter.com/GitBookIO) on Twitter. Complete documentation is available at [help.gitbook.io](http://help.gitbook.io/).
GitBook is a command line tool (and Node.js library) for building beautiful books using GitHub/Git and Markdown (or AsciiDoc). Here is an example: [Learn Javascript](https://www.gitbook.com/book/GitBookIO/javascript).
You can publish and host book easily online using [gitbook.com](https://www.gitbook.com), a desktop editor is [also available](https://www.gitbook.com/editor).
Check out the [GitBook Community Slack Channel](https://slack.gitbook.com), Stay updated by following [@GitBookIO](https://twitter.com/GitBookIO) on Twitter or [GitBook](https://www.facebook.com/gitbookcom) on Facebook.
Complete documentation is available at [help.gitbook.com](http://help.gitbook.com/).
![Image](https://raw.github.com/GitbookIO/gitbook/master/preview.png)
@@ -27,25 +33,25 @@ $ gitbook init
You can serve a repository as a book using:
```
$ gitbook serve ./repository
$ gitbook serve
```
Or simply build the static website using:
```
$ gitbook build ./repository ./outputFolder
$ gitbook build
```
## Features
* [Output as a website or ebook (pdf, epub, mobi)](#output-formats)
* [Multi-Languages](#multi-languages)
* [Glossary](#glossary)
* [Cover](#cover)
* [AsciiDoc Support](#asciidoc)
* [Variables and Templating](#variables-and-templating)
* [Content References](#content-references)
* [Plugins](#plugins)
* [Output as a website or ebook (pdf, epub, mobi)](http://help.gitbook.com/format/output.html)
* [Multi-Languages](http://help.gitbook.com/format/languages.html)
* [Glossary](http://help.gitbook.com/format/glossary.html)
* [Cover](http://help.gitbook.com/format/cover.html)
* [AsciiDoc Support](http://help.gitbook.com/format/asciidoc.html)
* [Variables and Templating](http://help.gitbook.com/format/templating.html)
* [Content References](http://help.gitbook.com/format/conrefs.html)
* [Plugins](http://help.gitbook.com/format/plugins.html)
* Interractive reader website:
* Search
* Font Settings (Serif, Sans Serif)
@@ -199,7 +205,7 @@ The platform [GitBook.com](https://www.gitbook.com/) is like an "Heroku for book
Plugins can be used to extend your book's functionality. Read [GitbookIO/plugin](https://github.com/GitbookIO/plugin) for more information about how to build a plugin for GitBook.
Plugins needed to build a book can be installed using: `gitbook install ./`. You can find plugins at [plugins.gitbook.com](plugins.gitbook.com).
Plugins needed to build a book can be installed using: `gitbook install ./`. You can find plugins at [plugins.gitbook.com](http://plugins.gitbook.com).
## Debugging
@@ -207,5 +213,18 @@ 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
To use the latest commit from `GitBook/gitbook` with `gitbook-cli`:
```
$ git clone https://github.com/GitbookIO/gitbook.git ./gitbook
$ gitbook versions:link ./gitbook
```
Now `gitbook-cli` will be using the `./gitbook` folder.
You can uninstall it using: `gitbook versions:uninstall latest`.
+29
View File
@@ -0,0 +1,29 @@
# Fix line endings in Windows. (runs before repo cloning)
init:
- git config --global core.autocrlf input
# Test against these versions of Node.js.
environment:
matrix:
- nodejs_version: "0.12"
- nodejs_version: "4.1"
# Install scripts. (runs after repo cloning)
install:
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version
# install svgexport
- npm install svgexport -g
# install modules
- npm install
# Post-install test scripts.
test_script:
# Output useful info for debugging.
- node --version
- npm --version
# run tests
- npm test
# Don't actually build.
build: off
+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 installe 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
};
+516 -478
View File
File diff suppressed because it is too large Load Diff
+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
}
};
+135 -160
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,55 @@ 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;
}
});
this.book = book;
this.replace(options);
};
// Read and parse the configuration
Configuration.prototype.load = function() {
var that = this;
var that = this;
return Q()
return Q()
.then(function() {
var configPath, _config;
try {
var _config = require(path.resolve(that.book.root, that.options.configFile));
configPath = require.resolve(
that.book.resolve(that.options.configFile)
);
// Invalidate node.js cache for livreloading
delete require.cache[configPath];
_config = require(configPath);
that.options = _.merge(
that.options,
_.omit(_config, 'configFile', 'defaultsPlugins', 'generator', 'extension')
that.options,
_.omit(_config, 'configFile', 'defaultsPlugins', 'generator', 'extension')
);
}
catch(err) {
@@ -96,17 +116,25 @@ Configuration.prototype.load = function() {
}
})
.then(function() {
that.options.output = path.resolve(that.options.output || path.join(that.book.root, "_book"));
that.options.plugins = normalizePluginsList(that.options.plugins);
that.options.defaultsPlugins = normalizePluginsList(that.options.defaultsPlugins || "");
that.options.plugins = _.union(that.options.plugins, that.options.defaultsPlugins);
that.options.plugins = _.uniq(that.options.plugins, "name");
if (!semver.satisfies(pkg.version, that.options.gitbook)) {
throw "GitBook version doesn't satisfy version required by the book: "+that.options.gitbook;
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');
}
}
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"));
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 || '', false);
that.options.plugins = _.union(that.options.plugins, that.options.defaultsPlugins);
that.options.plugins = _.uniq(that.options.plugins, 'name');
// Default value for text direction (from language)
if (!that.options.direction) {
var lang = i18n.getCatalog(that.options.language);
if (lang) that.options.direction = lang.direction;
}
that.options.gitbook = pkg.version;
@@ -118,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
@@ -128,108 +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": "site",
"extension": null,
// Book metadats (somes are extracted from the README by default)
"title": null,
"description": null,
"isbn": null,
"language": "en",
// 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",
"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;
+97 -57
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(["ebook", this.ebookFormat]);
this.styles = _.compact(['print', 'ebook', this.ebookFormat]);
// Convert images (svg -> png)
this.convertImages = true;
@@ -26,89 +27,128 @@ 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();
return Q();
};
// Generate table of contents
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.resolve(this.styles.pdf), { encoding: 'utf-8' }));
}
tpl = juice(tpl, {
extraCss: stylesheets.join('\n\n')
});
return tpl;
};
Generator.prototype.finish = function() {
var that = this;
return BaseGenerator.prototype.finish.apply(this)
.then(function() {
return that.writeSummary();
})
return Q()
.then(this.copyAssets)
.then(this.copyCover)
.then(this.writeGlossary)
.then(this.writeSummary)
.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,
"--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') {
_.extend(_options, {
'--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);
}
+6 -6
View File
@@ -2,10 +2,10 @@ var _ = require("lodash");
var EbookGenerator = require("./ebook");
module.exports = {
json: require("./json"),
website: require("./website"),
ebook: EbookGenerator,
pdf: _.partialRight(EbookGenerator, "pdf"),
mobi: _.partialRight(EbookGenerator, "mobi"),
epub: _.partialRight(EbookGenerator, "epub")
json: require("./json"),
website: require("./website"),
ebook: EbookGenerator,
pdf: _.partialRight(EbookGenerator, "pdf"),
mobi: _.partialRight(EbookGenerator, "mobi"),
epub: _.partialRight(EbookGenerator, "epub")
};
+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)
);
});
+64 -80
View File
@@ -1,20 +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");
var FilterExtension = require("nunjucks-filter");
var I18nExtension = require("nunjucks-i18n");
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);
@@ -23,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;
@@ -51,12 +49,13 @@ Generator.prototype.prepareStyles = function() {
this.styles = _.chain(this.styles)
.map(function(style) {
var stylePath = that.options.styles[style];
if (fs.existsSync(path.resolve(that.book.root, stylePath))) {
return stylePath;
if (stylePath && fs.existsSync(that.book.resolve(stylePath))) {
return [style, stylePath];
}
return null;
})
.compact()
.object()
.value();
return Q();
@@ -64,11 +63,11 @@ 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();
return Q();
};
// Prepare template engine
@@ -79,8 +78,8 @@ Generator.prototype.prepareTemplateEngine = function() {
.then(function() {
var language = that.book.config.normalizeLanguage();
if (!i18n.getByLanguage(language) && language != "en") {
that.book.log.warn.ln("Language '"+language+"' is not available as a layout locales (en, "+i18n.getLanguages().join(", ")+")");
if (!i18n.hasLocale(language)) {
that.book.log.warn.ln('Language "'+language+'" is not available as a layout locales (en, '+i18n.getLocales().join(', ')+')');
}
var folders = _.chain(that.templates)
@@ -97,19 +96,14 @@ 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
that.env.addExtension('AutoEscapeExtension', new AutoEscapeExtension(that.env));
that.env.addExtension('FilterExtension', new FilterExtension(that.env));
that.env.addExtension('I18nExtension', new I18nExtension({
env: that.env,
translations: i18n.getLocales(),
locale: "language"
}));
});
};
@@ -118,8 +112,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
@@ -129,60 +122,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.contentLink(page.path);
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.info.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
@@ -199,7 +180,7 @@ Generator.prototype.normalizePage = function(page) {
return Q()
.then(function() {
return _callHook("page");
return _callHook('page');
})
.then(function() {
return page;
@@ -212,39 +193,42 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
interpolate = interpolate || _.identity;
return Q()
.then(function(sections) {
return that.env.render(
tpl,
_.extend({
.then(function() {
return that.env.render(
tpl,
_.extend({
gitbook: {
version: pkg.version
},
styles: that.styles,
styles: that.styles,
revision: that.revision,
revision: that.revision,
title: that.options.title,
description: that.options.description,
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,
glossary: that.book.glossary,
summary: that.book.summary,
allNavigation: that.book.navigation,
summary: that.book.summary,
allNavigation: that.book.navigation,
plugins: {
plugins: {
resources: that.book.plugins.resources(that.namespace)
},
pluginsConfig: JSON.stringify(that.options.pluginsConfig),
htmlSnippet: _.partial(_.partialRight(that.book.plugins.html, that, options), that.namespace),
pluginsConfig: JSON.stringify(that.options.pluginsConfig),
htmlSnippet: _.partial(_.partialRight(that.book.plugins.html, that, options), that.namespace),
options: that.options,
options: that.options,
basePath: ".",
staticBase: path.join(".", "gitbook"),
}, options)
);
basePath: '.',
staticBase: path.join('.', 'gitbook'),
'__': that.book.i18n.bind(that.book)
}, options)
);
})
.then(interpolate)
.then(function(html) {
@@ -261,15 +245,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);
})
);
+184 -179
View File
@@ -1,210 +1,215 @@
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",
values: _.chain(logger.LEVELS).keys().map(stringUtils.toLowerCase).value(),
defaults: "info"
name: 'log',
description: 'Minimum log level to display',
values: _.chain(logger.LEVELS).keys().map(stringUtils.toLowerCase).value(),
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,
Book: Book,
LOG_LEVELS: logger.LEVELS,
commands: _.flatten([
{
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");
commands: _.flatten([
{
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 book = new Book(input, _.extend({}, {
'config': {
'output': output
},
'logLevel': kwargs.log
}));
var book = new Book(input, _.extend({}, {
'config': {
'output': output
},
'logLevel': kwargs.log
}));
return book.parse()
.then(function() {
return book.generate(kwargs.format);
})
.then(function(){
console.log("");
console.log(color.green("Done, without error"));
});
}
},
return book.parse()
.then(function() {
return book.generate(kwargs.format);
})
.then(function(){
console.log('');
console.log(color.green('Done, without error'));
});
}
},
_.map(["pdf", "epub", "mobi"], function(ebookType) {
return {
name: ebookType+" [book] [output]",
description: "build a book to "+ebookType,
options: [
LOG_OPTION
],
exec: function(args, kwargs) {
var input = args[0] || process.cwd();
var output = args[1];
_.map(['pdf', 'epub', 'mobi'], function(ebookType) {
return {
name: ebookType+' [book] [output]',
description: 'build a book to '+ebookType,
options: [
LOG_OPTION
],
exec: function(args, kwargs) {
var input = args[0] || process.cwd();
var output = args[1];
var book = new Book(input, _.extend({}, {
'logLevel': kwargs.log
}));
var book = new Book(input, _.extend({}, {
'logLevel': kwargs.log
}));
return book.parse()
.then(function() {
return book.generateFile(output, {
ebookFormat: ebookType
});
})
.then(function(){
console.log("");
console.log(color.green("Done, without error"));
});
}
};
}),
return book.parse()
.then(function() {
return book.generateFile(output, {
ebookFormat: ebookType
});
})
.then(function(){
console.log('');
console.log(color.green('Done, without error'));
});
}
};
}),
{
name: "serve [book]",
description: "Build then serve a gitbook from a directory",
options: [
{
name: "port",
description: "Port for server to listen on",
defaults: 4000
},
{
name: "lrport",
description: "Port for livereload server to listen on",
defaults: 35729
},
{
name: "watch",
description: "Enable/disable file watcher",
defaults: true
},
FORMAT_OPTION,
LOG_OPTION
],
exec: function(args, kwargs) {
var input = args[0] || process.cwd();
var server = new Server();
{
name: 'serve [book]',
description: 'Build then serve a gitbook from a directory',
options: [
{
name: 'port',
description: 'Port for server to listen on',
defaults: 4000
},
{
name: 'lrport',
description: 'Port for livereload server to listen on',
defaults: 35729
},
{
name: 'watch',
description: 'Enable/disable file watcher',
defaults: true
},
FORMAT_OPTION,
LOG_OPTION
],
exec: function(args, kwargs) {
var input = args[0] || process.cwd();
var server = new Server();
// Init livereload server
var lrServer = tinylr({});
var lrPath = undefined;
// Init livereload server
var lrServer = tinylr({});
var lrPath;
var generate = function() {
if (server.isRunning()) console.log("Stopping server");
var generate = function() {
if (server.isRunning()) console.log('Stopping server');
return server.stop()
.then(function() {
var book = new Book(input, _.extend({}, {
'config': {
'defaultsPlugins': ["livereload"]
},
'logLevel': kwargs.log
}));
return server.stop()
.then(function() {
var book = new Book(input, _.extend({}, {
'config': {
'defaultsPlugins': ['livereload']
},
'logLevel': kwargs.log
}));
return book.parse()
.then(function() {
return book.generate(kwargs.format);
})
.thenResolve(book);
})
.then(function(book) {
console.log();
console.log('Starting server ...');
return server.start(book.options.output, kwargs.port)
.then(function() {
console.log('Serving book on http://localhost:'+kwargs.port);
return book.parse()
.then(function() {
return book.generate(kwargs.format);
})
.thenResolve(book);
})
.then(function(book) {
console.log();
console.log('Starting server ...');
return server.start(book.options.output, kwargs.port)
.then(function() {
console.log('Serving book on http://localhost:'+kwargs.port);
if (lrPath) {
// trigger livereload
lrServer.changed({
body: {
files: [lrPath]
}
});
}
if (lrPath) {
// trigger livereload
lrServer.changed({
body: {
files: [lrPath]
}
});
}
if (!kwargs.watch) return;
if (!kwargs.watch) return;
return watch(book.root)
.then(function(filepath) {
// set livereload path
lrPath = filepath;
console.log("Restart after change in files");
console.log('');
return generate();
})
})
});
};
return watch(book.root)
.then(function(filepath) {
// set livereload path
lrPath = filepath;
console.log('Restart after change in file', filepath);
console.log('');
return generate();
});
});
});
};
return Q.nfcall(lrServer.listen.bind(lrServer), kwargs.lrport)
.then(function() {
console.log('Live reload server started on port:', kwargs.lrport);
console.log('Press CTRL+C to quit ...');
console.log('')
return generate();
});
}
},
return Q.nfcall(lrServer.listen.bind(lrServer), kwargs.lrport)
.then(function() {
console.log('Live reload server started on port:', kwargs.lrport);
console.log('Press CTRL+C to quit ...');
console.log('');
return generate();
});
}
},
{
name: "install [book]",
description: "install plugins dependencies",
exec: function(args, kwargs) {
var input = args[0] || process.cwd();
{
name: 'install [book]',
description: 'install plugins dependencies',
exec: function(args) {
var input = args[0] || process.cwd();
var book = new Book(input);
var book = new Book(input);
return book.config.load()
.then(function() {
return book.plugins.install();
})
.then(function(){
console.log("");
console.log(color.green("Done, without error"));
});
}
},
return book.config.load()
.then(function() {
return book.plugins.install();
})
.then(function(){
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())
.then(function(){
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) {
return initBook(args[0] || process.cwd())
.then(function(){
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 remove 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;
+141 -136
View File
@@ -1,120 +1,128 @@
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' });
return Q.nfcall(npm.load, { silent: true, loglevel: 'silent' });
});
var PluginsList = function(book, plugins) {
this.book = book;
this.log = this.book.log;
this.book = book;
this.log = this.book.log;
// List of Plugin objects
this.list = [];
// List of Plugin objects
this.list = [];
// List of names of failed plugins
this.failed = [];
// List of names of failed plugins
this.failed = [];
// Namespaces
this.namespaces = _.chain(["website", "ebook"])
.map(function(namespace) {
return [
namespace,
{
html: {},
resources: _.chain(Plugin.RESOURCES)
.map(function(type) {
return [type, []];
})
.object()
.value()
}
];
})
.object()
.value();
// Namespaces
this.namespaces = _.chain(['website', 'ebook'])
.map(function(namespace) {
return [
namespace,
{
html: {},
resources: _.chain(Plugin.RESOURCES)
.map(function(type) {
return [type, []];
})
.object()
.value()
}
];
})
.object()
.value();
// Bind methods
_.bindAll(this);
// Bind methods
_.bindAll(this);
if (plugins) this.load(plugins);
if (plugins) this.load(plugins);
};
// return count of plugins
PluginsList.prototype.count = function() {
return this.list.length;
return this.list.length;
};
// Add and load a plugin
PluginsList.prototype.load = function(plugin, options) {
var that = this;
PluginsList.prototype.load = function(plugin) {
var that = this;
if (_.isArray(plugin)) {
return _.reduce(plugin, function(prev, p) {
return prev.then(function() {
return that.load(p);
});
}, Q());
}
if (_.isObject(plugin) && !(plugin instanceof Plugin)) plugin = plugin.name;
if (_.isString(plugin)) plugin = new Plugin(this.book, plugin);
if (_.isArray(plugin)) {
return _.reduce(plugin, function(prev, p) {
return prev.then(function() {
return that.load(p);
});
}, Q());
}
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, "....");
if (!plugin.isValid()) {
that.log.info.fail();
that.failed.push(plugin.name);
return Q();
that.log.info('load plugin', plugin.name, '....');
if (!plugin.isValid()) {
that.log.info.fail();
that.failed.push(plugin.name);
return Q();
} else {
that.log.info.ok();
that.log.info.ok();
// Push in the list
that.list.push(plugin);
// Push in the list
that.list.push(plugin);
}
// Extract filters
_.each(plugin.getFilters(), function(filterFunc, filterName) {
that.book.template.addFilter(filterName, filterFunc);
return Q()
// Validate and normalize configuration
.then(function() {
var config = plugin.getConfig();
return plugin.validateConfig(config);
})
.then(function(config) {
// Update configuration
plugin.setConfig(config);
// Extract filters
that.book.template.addFilters(plugin.getFilters());
// 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);
});
});
});
}, Q());
});
// Extract blocks
_.each(plugin.getBlocks(), function(block, blockName) {
that.book.template.addBlock(blockName, block);
});
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);
});
})
});
}, Q());
};
// Call a hook
PluginsList.prototype.hook = function(name, data) {
return _.reduce(this.list, function(prev, plugin) {
return _.reduce(this.list, function(prev, plugin) {
return prev.then(function(ret) {
return plugin.callHook(name, ret);
});
@@ -123,7 +131,7 @@ PluginsList.prototype.hook = function(name, data) {
// Return a template from a plugin
PluginsList.prototype.template = function(name) {
var withTpl = _.find(this.list, function(plugin) {
var withTpl = _.find(this.list, function(plugin) {
return (
plugin.infos.templates &&
plugin.infos.templates[name]
@@ -136,90 +144,87 @@ PluginsList.prototype.template = function(name) {
// Return an html snippet
PluginsList.prototype.html = function(namespace, tag, context, options) {
var htmlSnippets = this.namespaces[namespace].html[tag];
var htmlSnippets = this.namespaces[namespace].html[tag];
return _.map(htmlSnippets || [], function(code) {
return code.call(context, options);
}).join("\n");
}).join('\n');
};
// Return a resources map for a namespace
PluginsList.prototype.resources = function(namespace) {
return this.namespaces[namespace].resources;
return this.namespaces[namespace].resources;
};
// Install plugins from a book
PluginsList.prototype.install = function() {
var that = this;
var defaultsPlugins = _.pluck(that.book.options.defaultsPlugins)
var that = this;
// 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()
// Resolve version if needed
.then(function() {
if (plugin.version) return plugin.version;
if (plugin.version) return plugin.version;
that.book.log.info.ln("No version specified, resolve plugin", plugin.name);
return initNPM()
.then(function() {
return Q.nfcall(npm.commands.view, [fullname+"@*", "engines"], true);
})
.then(function(versions) {
return _.chain(versions)
.pairs()
.map(function(v) {
return {
version: v[0],
gitbook: (v[1].engines || {})["gitbook"]
}
})
.filter(function(v) {
return v.gitbook && semver.satisfies(pkg.version, v.gitbook);
})
.sort(function(v1, v2) {
return semver.lt(v1.version, v2.version)? 1 : -1;
})
.pluck("version")
.first()
.value();
});
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);
})
.then(function(versions) {
return _.chain(versions)
.pairs()
.map(function(v) {
return {
version: v[0],
gitbook: (v[1].engines || {}).gitbook
};
})
.filter(function(v) {
return v.gitbook && version.satisfies(v.gitbook);
})
.sort(function(v1, v2) {
return semver.lt(v1.version, v2.version)? 1 : -1;
})
.pluck('version')
.first()
.value();
});
})
// Install the plugin with the resolved version
.then(function(version) {
if (!version) {
throw "Found no satisfactory version for plugin "+plugin.name;
}
if (!version) {
throw 'Found no satisfactory version for plugin '+plugin.name;
}
that.book.log.info.ln("install plugin", plugin.name, "from npm ("+fullname+") with version", version);
return Q.nfcall(npmi, {
'name': fullname,
'version': version,
'path': that.book.root,
'npmLoad': {
'loglevel': 'silent',
'loaded': true,
'prefix': that.book.root
}
});
that.book.log.info.ln('install plugin', plugin.name, 'from npm ('+fullname+') with version', version);
return Q.nfcall(npmi, {
'name': fullname,
'version': version,
'path': that.book.root,
'npmLoad': {
'loglevel': 'silent',
'loaded': true,
'prefix': that.book.root
}
});
})
.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;
+222 -126
View File
@@ -1,122 +1,107 @@
var _ = require("lodash");
var Q = require("q");
var path = require("path");
var nunjucks = require("nunjucks");
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 stringUtils = require("./utils/string");
var fs = require("./utils/fs");
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) {
this.book = book;
var that = this;
this.book = book;
this.log = this.book.log;
// Nunjucks env
this.env = new nunjucks.Environment(
new BookLoader(book),
{
// Escaping is done after by the markdown parser
autoescape: false,
// 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;
// Tags
tags: {
blockStart: '{%',
blockEnd: '%}',
variableStart: '{{',
variableEnd: '}}',
commentStart: '{###',
commentEnd: '###}'
}
}
);
return that.applyShortcuts(type, source);
}
});
// Nunjucks env
this.env = new nunjucks.Environment(
this.loader,
{
// Escaping is done after by the markdown parser
autoescape: false,
// Tags
tags: {
blockStart: '{%',
blockEnd: '%}',
variableStart: '{{',
variableEnd: '}}',
commentStart: '{###',
commentEnd: '###}'
}
}
);
// 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
parse: false,
post: undefined
});
blk.id = _.uniqueId("blk");
blk.id = _.uniqueId('blk');
//Parsable block, just return it
var toAdd = (!blk.parse) || (blk.post !== undefined);
// Add to global map
if (toAdd) this.blockBodies[blk.id] = blk;
// Parsable block, just return it
if (blk.parse) {
return blk.body;
}
// Add to global map
this.blocks[blk.id] = blk;
// 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;
// Remove it from map
delete that.blocks[key];
return body;
});
};
@@ -140,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);
@@ -159,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;
@@ -222,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()
@@ -239,49 +262,51 @@ TemplateEngine.prototype.addBlock = function(name, block) {
var args = Array.prototype.slice.call(arguments, 1);
var callback = args.pop();
// Extract blocks body
var _blocks = _.chain(block.blocks)
.reverse()
.map(function(blockName){
return {
name: blockName,
body: args.pop()()
};
})
.reverse()
.value();
// Extract blocks
var blocks = args
.concat([])
.slice(-block.blocks.length);
// Eliminate blocks from list
if (block.blocks.length > 0) args = args.slice(0, -block.blocks.length);
// Extract main body and kwargs
var body = args.pop();
var kwargs = args.pop() || {};
var kwargs = _.isObject(_.last(args))? args.pop() : {};
var func = that.bindContext(block.process);
// Extract blocks body
var _blocks = _.map(block.blocks, function(blockName, i){
return {
name: blockName,
body: blocks[i]()
};
});
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,
@@ -293,27 +318,70 @@ 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(
stringUtils.escapeRegex(shortcut.start) + "([\\s\\S]*?[^\\$])" + stringUtils.escapeRegex(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
version: pkg.version,
generator: this.book.options.generator
}
});
options = _.defaults(options || {}, {
@@ -321,16 +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);
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: /, '');
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) {
@@ -342,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,
@@ -363,8 +441,26 @@ TemplateEngine.prototype.renderPage = function(page) {
// Post process content
TemplateEngine.prototype.postProcess = function(content) {
var that = this;
return Q(content)
.then(this.replaceBlocks);
.then(that.replaceBlocks)
.then(function(_content) {
return batch.execEach(that.blockBodies, {
max: 20,
fn: function(blk, blkId) {
return Q()
.then(function() {
if (!blk.post) return Q();
return blk.post();
})
.then(function() {
delete that.blockBodies[blkId];
});
}
})
.thenResolve(_content);
});
};
module.exports = TemplateEngine;
+52
View File
@@ -0,0 +1,52 @@
var Q = require("q");
var _ = require("lodash");
// Execute a method for all element
function execEach(items, options) {
if (_.size(items) === 0) return Q();
var concurrents = 0, d = Q.defer(), pending = [];
options = _.defaults(options || {}, {
max: 100,
fn: function() {}
});
function startItem(item, i) {
if (concurrents >= options.max) {
pending.push([item, i]);
return;
}
concurrents++;
Q()
.then(function() {
return options.fn(item, i);
})
.then(function() {
concurrents--;
// Next pending
var next = pending.shift();
if (concurrents === 0 && !next) {
d.resolve();
} else if (next) {
startItem.apply(null, next);
}
})
.fail(function(err) {
pending = [];
d.reject(err);
});
}
_.each(items, startItem);
return d.promise;
}
module.exports = {
execEach: execEach
};
+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;
+85 -70
View File
@@ -1,21 +1,20 @@
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;
// Check if an url is a git dependency url
function checkGitUrl(giturl) {
return (giturl.indexOf(GIT_PREFIX) === 0);
return (giturl.indexOf(GIT_PREFIX) === 0);
}
// Validates a SHA in hexadecimal
@@ -25,88 +24,104 @@ function validateSha(str) {
// Parse and extract infos
function parseGitUrl(giturl) {
var ref, uri, fileParts, filepath;
var ref, uri, fileParts, filepath;
if (!checkGitUrl(giturl)) return null;
giturl = giturl.slice(GIT_PREFIX.length);
if (!checkGitUrl(giturl)) return null;
giturl = giturl.slice(GIT_PREFIX.length);
uri = new URI(giturl);
ref = uri.fragment() || "master";
uri.fragment(null);
uri = new URI(giturl);
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);
// Extract file inside the repo (after the .git)
fileParts =uri.path().split('.git');
filepath = fileParts.length > 1? fileParts.slice(1).join('.git') : '';
if (filepath[0] == '/') filepath = filepath.slice(1);
// Recreate pathname without the real filename
uri.path(_.first(fileParts)+".git");
// Recreate pathname without the real filename
uri.path(_.first(fileParts)+'.git');
return {
host: uri.toString(),
ref: ref || "master",
filepath: filepath
};
return {
host: uri.toString(),
ref: ref || 'master',
filepath: filepath
};
}
// Clone a git repo from a specific ref
function cloneGitRepo(host, ref) {
var isBranch = false;
var isBranch = false;
ref = ref || "master";
if (!validateSha(ref)) isBranch = true;
ref = ref || 'master';
if (!validateSha(ref)) isBranch = true;
return Q()
return Q()
// Create temporary folder to store git repos
.then(function() {
if (GIT_TMP) return;
return fs.tmp.dir()
.then(function(_tmp) {
GIT_TMP = _tmp;
});
})
// Create temporary folder to store git repos
.then(function() {
if (GIT_TMP) return;
return fs.tmp.dir()
.then(function(_tmp) {
GIT_TMP = _tmp;
});
})
// Return or clone the git repo
.then(function() {
// Unique ID for repo/ref combinaison
var repoId = crc.crc32(host+"#"+ref).toString(16);
// Return or clone the git repo
.then(function() {
// Unique ID for repo/ref combinaison
var repoId = crc.crc32(host+'#'+ref).toString(16);
// Absolute path to the folder
var repoPath = path.resolve(GIT_TMP, repoId);
// Absolute path to the folder
var repoPath = path.resolve(GIT_TMP, repoId);
return fs.exists(repoPath)
.then(function(doExists) {
if (doExists) return;
return fs.exists(repoPath)
.then(function(doExists) {
if (doExists) return;
// Clone repo
return exec("git clone "+host+" "+repoPath)
.then(function() {
return exec("git checkout "+ref, { cwd: repoPath });
})
})
.thenResolve(repoPath);
});
// Clone repo
return exec('git clone '+host+' '+repoPath)
.then(function() {
return exec('git checkout '+ref, { cwd: repoPath });
});
})
.thenResolve(repoPath);
});
}
// Get file from a git repo
function resolveFileFromGit(giturl) {
if (_.isString(giturl)) giturl = parseGitUrl(giturl);
if (!giturl) return Q(null);
if (_.isString(giturl)) giturl = parseGitUrl(giturl);
if (!giturl) return Q(null);
// Clone or get from cache
return cloneGitRepo(giturl.host, giturl.ref)
.then(function(repo) {
// Clone or get from cache
return cloneGitRepo(giturl.host, giturl.ref)
.then(function(repo) {
// Resolve relative path
return path.resolve(repo, giturl.filepath);
});
};
// 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
checkUrl: checkGitUrl,
parseUrl: parseGitUrl,
resolveFile: resolveFileFromGit,
resolveRoot: resolveGitRoot
};
+67 -53
View File
@@ -1,66 +1,80 @@
var _ = require("lodash");
var path = require("path");
var fs = require("fs");
var _ = require('lodash');
var path = require('path');
var fs = require('fs');
var I18N_PATH = path.resolve(__dirname, "../../theme/i18n/")
var i18n = require('i18n');
var getLocales = _.memoize(function() {
var locales = fs.readdirSync(I18N_PATH);
return _.chain(locales)
.map(function(local) {
local = path.basename(local, ".json");
return [local, _.extend(require(path.join(I18N_PATH, local)), {
id: local
})];
})
.object()
.value();
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');
});
var getLanguages = function() {
return _.keys(getLocales());
};
i18n.configure({
locales: LOCALES,
directory: I18N_PATH,
defaultLocale: DEFAULT_LANGUAGE,
updateFiles: false
});
var getByLanguage = function(lang) {
lang = normalizeLanguage(lang);
var locales = getLocales();
return locales[lang];
};
function compareLocales(lang, locale) {
var langMain = _.first(lang.split('-'));
var langSecond = _.last(lang.split('-'));
var compareLocales = function(lang, locale) {
var langMain = _.first(lang.split("-"));
var localeMain = _.first(locale.split("-"));
var localeMain = _.first(locale.split('-'));
var localeSecond = _.last(locale.split('-'));
if (locale == lang) return 100;
if (localeMain == langMain) return 50;
return 0;
};
if (locale == lang) return 100;
if (localeMain == langMain) return 50;
if (localeSecond == langSecond) return 20;
return 0;
}
var normalizeLanguage = _.memoize(function(lang) {
var locales = getLocales();
var language = _.chain(locales)
.values()
.map(function(locale) {
locale = locale.id;
return {
locale: locale,
score: compareLocales(lang, locale)
}
})
.filter(function(lang) {
return lang.score > 0;
})
.sortBy("score")
.pluck("locale")
.last()
.value();
return language || lang;
var language = _.chain(LOCALES)
.values()
.map(function(locale) {
return {
locale: locale,
score: compareLocales(lang, locale)
};
})
.filter(function(lang) {
return lang.score > 0;
})
.sortBy('score')
.pluck('locale')
.last()
.value();
return language || lang;
});
function translate(locale, phrase) {
var args = Array.prototype.slice.call(arguments, 2);
return i18n.__.apply({}, [{
locale: locale,
phrase: phrase
}].concat(args));
}
function getCatalog(locale) {
locale = normalizeLanguage(locale);
return i18n.getCatalog(locale);
}
function getLocales() {
return LOCALES;
}
function hasLocale(locale) {
return _.contains(LOCALES, locale);
}
module.exports = {
getLocales: getLocales,
getLanguages: getLanguages,
getByLanguage: getByLanguage,
normalizeLanguage: normalizeLanguage
__: translate,
normalizeLanguage: normalizeLanguage,
getCatalog: getCatalog,
getLocales: getLocales,
hasLocale: hasLocale
};
+17 -18
View File
@@ -1,38 +1,37 @@
var _ = require("lodash");
var Q = require("q");
var fs = require("./fs");
var exec = require('child_process').exec;
var links = require("./links");
var spawn = require("spawn-cmd").spawn;
// Convert a svg file
var convertSVG = function(source, dest, options) {
if (!fs.existsSync(source)) return Q.reject(new Error("File doesn't exist: "+source));
if (!fs.existsSync(source)) return Q.reject(new Error("File doesn't exist: "+source));
var d = Q.defer();
var d = Q.defer();
options = _.defaults(options || {}, {
options = _.defaults(options || {}, {
});
});
//var command = shellescape(["svgexport", source, dest]);
var child = spawn("svgexport", [source, dest]);
var command = "svgexport "+source+" "+dest;
child.on("error", function(error) {
if (error.code == "ENOENT") error = new Error("Need to install \"svgexport\" using \"npm install svgexport -g\"");
return d.reject(error);
});
var child = exec(command, function (error, stdout, stderr) {
if (error) {
if (error.code == 127) error = new Error("Need to install 'svgexport' using 'npm install svgexport -g'");
return d.reject(error);
}
if (fs.existsSync(dest)) {
child.on("close", function(code) {
if (code === 0 && fs.existsSync(dest)) {
d.resolve();
} else {
d.reject(new Error("Error converting "+source));
d.reject(new Error("Error converting "+source+" into "+dest));
}
});
return d.promise;
return d.promise;
};
module.exports = {
convertSVG: convertSVG,
INVALID: [".svg"]
convertSVG: convertSVG,
INVALID: [".svg"]
};
+24 -11
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) {
@@ -15,12 +15,27 @@ var isRelative = function(href) {
try {
var parsed = url.parse(href);
return !!(!parsed.protocol && parsed.path);// && parsed.path[0] != '/';
return !!(!parsed.protocol && parsed.path);
} catch(err) {}
return true;
};
// Return true if the link is an achor
var isAnchor = function(href) {
try {
var parsed = url.parse(href);
return !!(!parsed.protocol && !parsed.path && parsed.hash);
} catch(err) {}
return false;
};
// Normalize a path to be a link
var normalizeLink = function(s) {
return s.replace(/\\/g, "/");
};
// Relative to absolute path
// dir: directory parent of the file currently in rendering process
// outdir: directory parent from the html output
@@ -28,7 +43,7 @@ var isRelative = function(href) {
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));
@@ -36,7 +51,7 @@ var toAbsolute = function(_href, dir, outdir) {
_href = path.relative(outdir, hrefInRoot);
// Normalize windows paths
_href = _href.replace(/\\/g, '/');
_href = normalizeLink(_href);
return _href;
};
@@ -45,11 +60,7 @@ var toAbsolute = function(_href, dir, outdir) {
var join = function() {
var _href = path.join.apply(path, arguments);
if (process.platform === 'win32') {
_href = _href.replace(/\\/g, '/');
}
return _href;
return normalizeLink(_href);
};
// Change extension
@@ -61,9 +72,11 @@ var changeExtension = function(filename, newext) {
};
module.exports = {
isAnchor: isAnchor,
isRelative: isRelative,
isExternal: isExternal,
toAbsolute: toAbsolute,
join: join,
changeExtension: changeExtension
changeExtension: changeExtension,
normalize: normalizeLink
};
+78 -78
View File
@@ -1,102 +1,102 @@
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,
INFO: 1,
WARN: 2,
ERROR: 3,
DISABLED: 10
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3,
DISABLED: 10
};
var COLORS = {
DEBUG: color.purple,
INFO: color.cyan,
WARN: color.yellow,
ERROR: color.red
DEBUG: color.purple,
INFO: color.cyan,
WARN: color.yellow,
ERROR: color.red
};
module.exports = function(_write, logLevel) {
var logger = {};
var lastChar = '\n';
if (_.isString(logLevel)) logLevel = LEVELS[logLevel.toUpperCase()];
var logger = {};
var lastChar = "\n";
if (_.isString(logLevel)) logLevel = LEVELS[logLevel.toUpperCase()];
// Write a simple message
logger.write = function(msg) {
msg = msg.toString();
lastChar = _.last(msg);
return _write(msg);
};
// Write a simple message
logger.write = function(msg) {
msg = msg.toString();
lastChar = _.last(msg);
return _write(msg);
};
// Format a message
logger.format = function() {
return util.format.apply(util, arguments);
};
// Format a message
logger.format = function() {
return util.format.apply(util, arguments);
};
// Write a line
logger.writeLn = function(msg) {
return this.write((msg || "")+"\n");
};
// Write a line
logger.writeLn = function(msg) {
return this.write((msg || "")+"\n");
};
// Write a message with a certain level
logger.log = function(level) {
if (level < logLevel) return;
// Write a message with a certain level
logger.log = function(level) {
if (level < logLevel) return;
var levelKey = _.findKey(LEVELS, function(v) { return v == level; });
var args = Array.prototype.slice.apply(arguments, [1]);
var msg = logger.format.apply(logger, args);
var levelKey = _.findKey(LEVELS, function(v) { return v == level; });
var args = Array.prototype.slice.apply(arguments, [1]);
var msg = logger.format.apply(logger, args);
if (lastChar == '\n') {
msg = COLORS[levelKey](levelKey.toLowerCase()+":")+" "+msg;
}
if (lastChar == "\n") {
msg = COLORS[levelKey](levelKey.toLowerCase()+":")+" "+msg;
}
return logger.write(msg);
};
logger.logLn = function() {
if (lastChar != '\n') logger.write("\n");
return logger.write(msg);
};
logger.logLn = function() {
if (lastChar != "\n") logger.write("\n");
var args = Array.prototype.slice.apply(arguments);
args.push("\n");
logger.log.apply(logger, args);
};
var args = Array.prototype.slice.apply(arguments);
args.push("\n");
logger.log.apply(logger, args);
};
// Write a OK
logger.ok = function(level) {
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>> ')));
} else {
logger.log(level, color.green("OK"), "\n");
}
};
// Write a OK
logger.ok = function(level) {
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>> ")));
} else {
logger.log(level, color.green("OK"), "\n");
}
};
// Write an "FAIL"
logger.fail = function(level) {
return logger.log(level, color.red("ERROR")+"\n");
};
// Write an "FAIL"
logger.fail = function(level) {
return logger.log(level, color.red("ERROR")+"\n");
};
_.each(_.omit(LEVELS, 'DISABLED'), function(level, levelKey) {
levelKey = levelKey.toLowerCase();
_.each(_.omit(LEVELS, "DISABLED"), function(level, levelKey) {
levelKey = levelKey.toLowerCase();
logger[levelKey] = _.partial(logger.log, level);
logger[levelKey].ln = _.partial(logger.logLn, level);
logger[levelKey].ok = _.partial(logger.ok, level);
logger[levelKey].fail = _.partial(logger.fail, level);
logger[levelKey].promise = function(p) {
return p.
then(function(st) {
logger[levelKey].ok();
return st;
}, function(err) {
logger[levelKey].fail();
throw err;
});
}
});
logger[levelKey] = _.partial(logger.log, level);
logger[levelKey].ln = _.partial(logger.logLn, level);
logger[levelKey].ok = _.partial(logger.ok, level);
logger[levelKey].fail = _.partial(logger.fail, level);
logger[levelKey].promise = function(p) {
return p.
then(function(st) {
logger[levelKey].ok();
return st;
}, function(err) {
logger[levelKey].fail();
throw err;
});
};
});
return logger;
return logger;
};
module.exports.LEVELS = LEVELS;
module.exports.COLORS = COLORS;
+9 -10
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
@@ -36,11 +36,10 @@ function navigation(summary, files) {
var prev = null, next = null;
// Skip if no path
if(!current.path) return null;
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,16 +47,16 @@ 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,
introduction: current.introduction,
prev: prev,
next: next,
level: current.level,
+161 -96
View File
@@ -1,27 +1,32 @@
var Q = require('q');
var _ = require('lodash');
var url = require('url');
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 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(){
@@ -36,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 ) {
@@ -64,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
@@ -95,23 +102,32 @@ 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] = content;
$(this).replaceWith($("<img>").attr("src", dest));
svgContent[dest] = '<?xml version="1.0" encoding="UTF-8"?>'+content;
$(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);
// Transform as relative to the bases
@@ -124,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 = path.join("/", options.base, src);
var srcAbs = links.join('/', options.base, src);
// Test image extension
if (_.contains(imgUtils.INVALID, ext)) {
@@ -139,29 +155,29 @@ function normalizeHtml(src, options) {
src = imgConversionCache[outputRoot][srcAbs];
} else {
// Not converted yet
var dest = "";
var dest = '';
// Replace extension
dest = path.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 = path.join("/", dest);
src = links.join('/', dest);
}
// Reset as relative to output
@@ -172,39 +188,94 @@ 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);
});
$("a").each(function() {
var href = $(this).attr("href");
// Normalize links
$('a').each(function() {
var href = $(this).attr('href');
if (!href) return;
if (links.isRelative(href)) {
var absolutePath = path.join(options.base, href);
if (links.isAnchor(href)) {
// Keep it as it is
} else if (links.isRelative(href)) {
var parts = url.parse(href);
var absolutePath = links.join(options.base, parts.pathname);
var anchor = parts.hash || '';
// If is in navigation relative: change extsnio nto be .html
if (options.navigation[absolutePath]) href = links.changeExtension(href, ".html");
// 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(href, options.base, options.output);
href = links.toAbsolute('/'+absolutePath, options.base, options.output)+anchor;
} else {
$(this).attr("target", "_blank");
// External links
$(this).attr('target', '_blank');
}
// Transform extension
$(this).attr("href", href);
$(this).attr('href', href);
});
// Replace glossayr terms
_.each(options.glossary, function(term) {
var r = new RegExp( "\\b(" + pregQuote(term.name.toLowerCase()) + ")\\b" , 'gi' );
// Highlight code blocks
$('code').each(function() {
// Normalize language
var lang = _.chain(
($(this).attr('class') || '').split(' ')
)
.map(function(cl) {
// Markdown
if (cl.search('lang-') === 0) return cl.slice('lang-'.length);
// Asciidoc
if (cl.search('language-') === 0) return cl.slice('language-'.length);
return null;
})
.compact()
.first()
.value();
var source = $(this).text();
var blk = options.book.template.applyBlock('code', {
body: source,
kwargs: {
language: lang
}
});
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 includedInFiles = false;
$("*").each(function() {
$('*').each(function() {
// Ignore codeblocks
if (_.contains(['code', 'pre', 'a'], this.name.toLowerCase())) return;
replaceText($, this, r, function(match) {
// Add to files index in glossary
if (!includedInFiles) {
@@ -212,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='"+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>';
});
});
});
@@ -221,47 +292,55 @@ 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 _.reduce(images, function(prev, image) {
var imgin = path.resolve(options.book.options.output, image.source);
return batch.execEach(images, {
max: 100,
fn: function(image) {
var imgin = path.resolve(options.book.options.output, image.source);
return prev
return Q()
// 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, "...");
downloaded.push(image.origin);
return options.book.log.debug.promise(fs.writeStream(imgin, request(image.origin)));
})
// 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, '...');
downloaded.push(image.origin);
return options.book.log.debug.promise(fs.writeStream(imgin, request(image.origin)))
.fail(function(err) {
if (!_.isError(err)) err = new Error(err);
// Write svg if content
.then(function() {
if (!image.content) return;
return fs.writeFile(imgin, image.content);
})
err.message = 'Fail downloading '+image.origin+': '+err.message;
throw err;
});
})
// Convert
.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, "...");
return options.book.log.debug.promise(imgUtils.convertSVG(imgin, imgout));
});
}, Q())
// Write svg if content
.then(function() {
if (!image.content) return;
return fs.writeFile(imgin, image.content);
})
// Convert
.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, '...');
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) {
@@ -273,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: []
@@ -292,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;
@@ -307,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
};
+10 -2
View File
@@ -1,16 +1,24 @@
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;
var chapters = _.chain(navigation)
// Transform as array
.map(function(nav, path) {
nav.path = path;
return nav;
})
// Sort entries
.sortBy(function(nav) {
return nav.index;
})
.map(function(nav, i) {
// Calcul percent
nav.percent = (i * 100) / Math.max((n - 1), 1);
+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 -7
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);
})
@@ -20,12 +20,7 @@ function optionsToShellArgs(options) {
.join(" ");
}
function escapeRegex(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
module.exports = {
escapeRegex: escapeRegex,
escapeShellArg: escapeShellArg,
optionsToShellArgs: optionsToShellArgs,
toLowerCase: String.prototype.toLowerCase.call.bind(String.prototype.toLowerCase)
+13 -11
View File
@@ -1,9 +1,9 @@
var Q = require('q');
var _ = require('lodash');
var path = require('path');
var Gaze = require('gaze').Gaze;
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();
@@ -17,17 +17,19 @@ function watch(dir) {
toWatch.push("**/*"+ext);
});
var gaze = new Gaze(toWatch, {
cwd: dir
var watcher = chokidar.watch(toWatch, {
cwd: dir,
ignored: "_book/**",
ignoreInitial: true
});
gaze.once("all", function(e, filepath) {
gaze.close();
watcher.once("all", function(e, filepath) {
watcher.close();
d.resolve(filepath);
});
gaze.once("error", function(err) {
gaze.close();
watcher.once("error", function(err) {
watcher.close();
d.reject(err);
});
+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
};
+43 -24
View File
@@ -1,48 +1,67 @@
{
"name": "gitbook",
"version": "2.0.0-alpha.7",
"version": "2.6.1",
"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": "2.4.1",
"lodash": "3.10.1",
"graceful-fs": "3.0.5",
"resolve": "0.6.3",
"fs-extra": "0.16.3",
"fs-extra": "0.16.5",
"fstream-ignore": "1.0.2",
"gitbook-parsers": "0.4.0",
"nunjucks": "git+https://github.com/SamyPesse/nunjucks.git#4019d1b7379372336b86ce1b0bf84352a2029747",
"nunjucks-autoescape": "0.1.0",
"nunjucks-filter": "0.1.0",
"nunjucks-i18n": "1.0.0",
"semver": "2.2.1",
"gitbook-parsers": "0.8.7",
"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.1.0",
"nunjucks-autoescape": "1.0.0",
"nunjucks-filter": "1.0.0",
"i18n": "0.5.0",
"semver": "5.0.1",
"npmi": "0.1.1",
"cheerio": "0.18.0",
"cheerio": "0.19.0",
"gitbook-plugin-livereload": "0.0.1",
"gaze": "~0.5.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"
"dom-serializer": "0.1.0",
"spawn-cmd": "0.0.2",
"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": "1.18.2",
"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"
},
"scripts": {
"test": "export TESTING=true; mocha --reporter list --timeout 15000"
"test": "node_modules/.bin/mocha --reporter spec --timeout 15000"
},
"repository": {
"type": "git",
@@ -57,7 +76,7 @@
"gitbook"
],
"author": "FriendCode Inc. <contact@gitbook.com>",
"license": "Apache 2",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/GitbookIO/gitbook/issues"
},
+69
View File
@@ -0,0 +1,69 @@
var _ = require('lodash');
var fs = require('fs');
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.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' })));
});
should.Assertion.add('html', function(rules, 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);
// Test text
if (validations.text !== undefined) {
var text = $el.text();
if (validations.trim) text = text.trim();
text.should.be.equal(validations.text);
}
// Test attributes
_.each(validations.attributes, function(value, name) {
var attr = $el.attr(name);
should(attr).be.ok();
attr.should.be.equal(value);
});
});
});
+3
View File
@@ -0,0 +1,3 @@
# Readme
Default description for the book.
+1
View File
@@ -0,0 +1 @@
# Readme
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
"title": "js-config"
};
+1
View File
@@ -0,0 +1 @@
# Readme
+3
View File
@@ -0,0 +1,3 @@
{
"title": "json-config"
}
+15
View File
@@ -0,0 +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="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 @@
# Summary
+1
View File
@@ -0,0 +1 @@
$$test_block2$$
+1
View File
@@ -0,0 +1 @@
Hello World
+13
View File
@@ -0,0 +1,13 @@
# Glossary
## test
Just a simple and easy to understand test.
## hakunamatata
This word is not present in the content.
## test long
This is a test with a longer text that the first entry to test order.
+10
View File
@@ -0,0 +1,10 @@
# Readme
The word test should be replaced by a glossary link.
```
But the word test should not be replaced in code blocks
```
The two words test long should be replaced by the correct glossary links.
+3
View File
@@ -0,0 +1,3 @@
# Summary
* [Page](folder/PAGE.md)
+3
View File
@@ -0,0 +1,3 @@
# Page
Just a page in a sub-directory to test relative link to glossary.
+3
View File
@@ -0,0 +1,3 @@
# Hello World
## Hello {#hello-custom}
+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
+18
View File
@@ -0,0 +1,18 @@
# Tests for Images
# SVG relative image
### Convert SVG to PNG
![test.svg](test.svg)
### Should only convert it once
![test.svg](test.svg)
### Download remote images
![remote image](http://upload.wikimedia.org/wikipedia/commons/b/b0/NewTux.svg)
### Remote images with same filename
![youtube1](http://img.youtube.com/vi/9bZkp7q19f0/0.jpg)
![youtube2](http://img.youtube.com/vi/IkV2HQLAKHY/0.jpg)
+3
View File
@@ -0,0 +1,3 @@
# Summary
* [Page](./folder/PAGE.md)
+3
View File
@@ -0,0 +1,3 @@
### Images from other folder
![test.svg](../test.svg)
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
<svg version="1.1" baseProfile="tiny" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="svg-root" width="100%" height="100%" viewBox="0 0 480 360">
<title id="test-title">basic SVG tiny doc</title>
<g id="test-body-content">
<text font-family="Arial" font-size="14" text-anchor="middle" x="225" y="25">hello world</text>
</g>
<text id="revision" x="10" y="340" font-size="40" stroke="none" fill="black">Revision: 1.1</text>
<rect id="test-frame" x="1" y="1" width="478" height="358" fill="none" stroke="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 730 B

+3
View File
@@ -0,0 +1,3 @@
*
!SUMMARY.md
!.gitignore
+9
View File
@@ -0,0 +1,9 @@
# Summary
* [Hello](hello.md)
* [Hello 2](hello2.md)
* Hello 3
* [Hello 4](hello3/hello4.md)
* Hello 5
* [Hello 6](hello3/hello5/hello6.md)
+4
View File
@@ -0,0 +1,4 @@
# Language
* [English](en)
* [French](fr)
+1
View File
@@ -0,0 +1 @@
# Readme
+1
View File
@@ -0,0 +1 @@
Hello
+1
View File
@@ -0,0 +1 @@
# Summary
+1
View File
@@ -0,0 +1 @@
Bonjour
+1
View File
@@ -0,0 +1 @@
# Summary
+1
View File
@@ -0,0 +1 @@
# Readme
+4
View File
@@ -0,0 +1,4 @@
# Summary
* [Folder1](folder1/README.md)
* [Folder2](folder2/README.md)
+3
View File
@@ -0,0 +1,3 @@
# Folder 1
[Link to folder2](../folder2/README.md)
+1
View File
@@ -0,0 +1 @@
# Folder 2
+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
+3
View File
@@ -0,0 +1,3 @@
# Readme
Default description for the book.
+1
View File
@@ -0,0 +1 @@
# Summary
+3
View File
@@ -0,0 +1,3 @@
body {
color: red;
}
+1
View File
@@ -0,0 +1 @@
# Page 1
+1
View File
@@ -0,0 +1 @@
# Readme
+5
View File
@@ -0,0 +1,5 @@
# Summary
* [Page 1](./PAGE1.md)
* [Page 2](./folder/PAGE2.md)
* [Don't exists](./NOTFOUND.md)
+1
View File
@@ -0,0 +1 @@
# Page 2
+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'
}
});
});
});
+33 -10
View File
@@ -1,14 +1,37 @@
var path = require('path');
var assert = require('assert');
var Book = require('../').Book;
describe('Configuration parsing', function () {
it('should correctly load from json', function() {
assert(books[0].options.title == "Test");
describe('Configuration', function () {
it('should extract default title from README', function() {
return books.parse('basic')
.then(function(book) {
book.options.title.should.be.equal('Readme');
});
});
it('should correctly load from javascript', function() {
assert(books[4].options.title == "Test 2");
it('should extract default description from README', function() {
return books.parse('basic')
.then(function(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')
.then(function(book) {
book.options.title.should.be.equal('json-config');
});
});
it('should correctly load from JavaScript (book.js)', function() {
return books.parse('config-js')
.then(function(book) {
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.');
});
});
});
+68
View File
@@ -0,0 +1,68 @@
var fs = require('fs');
var path = require('path');
describe('ConRefs', function () {
var book, readme;
before(function() {
return books.generate('conrefs', 'website')
.then(function(_book) {
book = _book;
readme = fs.readFileSync(
path.join(book.options.output, 'index.html'),
{ encoding: 'utf-8' }
);
});
});
it('should handle local references', function() {
readme.should.be.html({
'.page-inner p#t1': {
count: 1,
text: 'Hello World',
trim: true
}
});
});
it('should handle local references with absolute paths', function() {
readme.should.be.html({
'.page-inner p#t2': {
count: 1,
text: 'Hello World',
trim: true
}
});
});
it('should correctly include file from git reference', function() {
readme.should.be.html({
'.page-inner p#t3': {
count: 1,
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
}
});
});
});
+60 -26
View File
@@ -1,36 +1,70 @@
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var assert = require('assert');
var cheerio = require('cheerio');
var fs = require("fs");
var fsUtil = require("../lib/utils/fs");
describe('eBook generator', function () {
describe('Basic Book', function() {
var book;
before(function() {
return books.generate('basic', 'ebook')
.then(function(_book) {
book = _book;
});
});
describe('eBook Generator', function () {
it('should correctly generate ebook pages', function(done) {
testGeneration(books[1], "ebook", function(output) {
assert(fs.existsSync(path.join(output, "SUMMARY.html")));
}, done);
it('should correctly output a SUMMARY.html', function() {
book.should.have.file('SUMMARY.html');
});
it('should correctly copy assets', function() {
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');
});
});
it('should correctly convert svg images to png', function(done) {
testGeneration(books[4], "ebook", function(output) {
// Check that all images exists
_.each([
"index.html",
"sub/PAGE.html"
], function(pageName) {
var pageFile = path.join(output, pageName);
var pageFolder = path.dirname(pageFile);
var pageContent = fs.readFileSync(pageFile, {encoding: "utf8"});
var $ = cheerio.load(pageContent);
describe('Custom styles', function() {
var book;
$("img").each(function() {
var src = $(this).attr("src");
assert(fs.existsSync(path.resolve(pageFolder, src)), src+" not found for page "+pageName);
})
before(function() {
return books.generate('style-print', 'ebook')
.then(function(_book) {
book = _book;
});
});
it('should remove default print.css', function() {
var PAGE = fs.readFileSync(
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: 2
}
});
}, done);
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 copy print.css', function() {
book.should.have.file('styles');
book.should.have.file('styles/print.css');
});
});
});
-6
View File
@@ -1,6 +0,0 @@
# Glossary
## Description
This is the dexcription of a description.
-3
View File
@@ -1,3 +0,0 @@
# Other Title
This is a description, it's simple, no ?
-1
View File
@@ -1 +0,0 @@
# Summary

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