diff options
Diffstat (limited to 'lib/api')
-rw-r--r-- | lib/api/decodeConfig.js | 19 | ||||
-rw-r--r-- | lib/api/decodeGlobal.js | 22 | ||||
-rw-r--r-- | lib/api/decodePage.js | 44 | ||||
-rw-r--r-- | lib/api/deprecate.js | 104 | ||||
-rw-r--r-- | lib/api/encodeConfig.js | 36 | ||||
-rw-r--r-- | lib/api/encodeGlobal.js | 125 | ||||
-rw-r--r-- | lib/api/encodeNavigation.js | 64 | ||||
-rw-r--r-- | lib/api/encodePage.js | 39 | ||||
-rw-r--r-- | lib/api/encodeProgress.js | 63 | ||||
-rw-r--r-- | lib/api/index.js | 8 |
10 files changed, 524 insertions, 0 deletions
diff --git a/lib/api/decodeConfig.js b/lib/api/decodeConfig.js new file mode 100644 index 0000000..351ed05 --- /dev/null +++ b/lib/api/decodeConfig.js @@ -0,0 +1,19 @@ +var Config = require('../models/config'); + +/** + Decode changes from a JS API to a config object + + @param {Config} config + @param {Object} result: result from API + @return {Config} +*/ +function decodeGlobal(config, result) { + var values = result.values; + + delete values.generator; + delete values.output; + + return Config.updateValues(config, values); +} + +module.exports = decodeGlobal; diff --git a/lib/api/decodeGlobal.js b/lib/api/decodeGlobal.js new file mode 100644 index 0000000..118afb2 --- /dev/null +++ b/lib/api/decodeGlobal.js @@ -0,0 +1,22 @@ +var decodeConfig = require('./decodeConfig'); + +/** + Decode changes from a JS API to a output object. + Only the configuration can be edited by plugin's hooks + + @param {Output} output + @param {Object} result: result from API + @return {Output} +*/ +function decodeGlobal(output, result) { + var book = output.getBook(); + var config = book.getConfig(); + + // Update config + config = decodeConfig(config, result.config); + book = book.set('config', config); + + return output.set('book', book); +} + +module.exports = decodeGlobal; diff --git a/lib/api/decodePage.js b/lib/api/decodePage.js new file mode 100644 index 0000000..c85dd1b --- /dev/null +++ b/lib/api/decodePage.js @@ -0,0 +1,44 @@ +var deprecate = require('./deprecate'); + +/** + Decode changes from a JS API to a page object. + Only the content can be edited by plugin's hooks. + + @param {Output} output + @param {Page} page: page instance to edit + @param {Object} result: result from API + @return {Page} +*/ +function decodePage(output, page, result) { + var originalContent = page.getContent(); + + // No returned value + // Existing content will be used + if (!result) { + return page; + } + + deprecate.disable('page.sections'); + + // GitBook 3 + // Use returned page.content if different from original content + if (result.content != originalContent) { + page = page.set('content', result.content); + } + + // GitBook 2 compatibility + // Finally, use page.sections + else if (result.sections) { + page = page.set('content', + result.sections.map(function(section) { + return section.content; + }).join('\n') + ); + } + + deprecate.enable('page.sections'); + + return page; +} + +module.exports = decodePage; diff --git a/lib/api/deprecate.js b/lib/api/deprecate.js new file mode 100644 index 0000000..d8d6ac1 --- /dev/null +++ b/lib/api/deprecate.js @@ -0,0 +1,104 @@ +var is = require('is'); + +var logged = {}; +var disabled = {}; + +/** + Log a deprecated notice + + @param {Book|Output} book + @param {String} key + @param {String} message +*/ +function logNotice(book, key, message) { + if (logged[key] || disabled[key]) return; + + logged[key] = true; + + var logger = book.getLogger(); + logger.warn.ln(message); +} + +/** + Deprecate a function + + @param {Book|Output} book + @param {String} key: unique identitifer for the deprecated + @param {Function} fn + @param {String} msg: message to print when called + @return {Function} +*/ +function deprecateMethod(book, key, fn, msg) { + return function() { + logNotice(book, key, msg); + + return fn.apply(this, arguments); + }; +} + +/** + Deprecate a property of an object + + @param {Book|Output} book + @param {String} key: unique identitifer for the deprecated + @param {Object} instance + @param {String|Function} property + @param {String} msg: message to print when called + @return {Function} +*/ +function deprecateField(book, key, instance, property, value, msg) { + var store = undefined; + + var prepare = function() { + if (!is.undefined(store)) return; + + if (is.fn(value)) store = value(); + else store = value; + }; + + var getter = function(){ + prepare(); + + logNotice(book, key, msg); + return store; + }; + var setter = function(v) { + prepare(); + + logNotice(book, key, msg); + store = v; + return store; + }; + + Object.defineProperty(instance, property, { + get: getter, + set: setter, + enumerable: true, + configurable: true + }); +} + +/** + Enable a deprecation + + @param {String} key: unique identitifer +*/ +function enableDeprecation(key) { + disabled[key] = false; +} + +/** + Disable a deprecation + + @param {String} key: unique identitifer +*/ +function disableDeprecation(key) { + disabled[key] = true; +} + +module.exports = { + method: deprecateMethod, + field: deprecateField, + enable: enableDeprecation, + disable: disableDeprecation +}; diff --git a/lib/api/encodeConfig.js b/lib/api/encodeConfig.js new file mode 100644 index 0000000..2a05528 --- /dev/null +++ b/lib/api/encodeConfig.js @@ -0,0 +1,36 @@ +var objectPath = require('object-path'); +var deprecate = require('./deprecate'); + +/** + Encode a config object into a JS config api + + @param {Output} output + @param {Config} config + @return {Object} +*/ +function encodeConfig(output, config) { + var result = { + values: config.getValues().toJS(), + + get: function(key, defaultValue) { + return objectPath.get(result.values, key, defaultValue); + }, + + set: function(key, value) { + return objectPath.set(result.values, key, value); + } + }; + + deprecate.field(output, 'config.options', result, 'options', + result.values, '"config.options" property is deprecated, use "config.get(key)" instead'); + + deprecate.field(output, 'config.options.generator', result.values, 'generator', + output.getGenerator(), '"options.generator" property is deprecated, use "output.name" instead'); + + deprecate.field(output, 'config.options.generator', result.values, 'output', + output.getRoot(), '"options.output" property is deprecated, use "output.root()" instead'); + + return result; +} + +module.exports = encodeConfig; diff --git a/lib/api/encodeGlobal.js b/lib/api/encodeGlobal.js new file mode 100644 index 0000000..4688cca --- /dev/null +++ b/lib/api/encodeGlobal.js @@ -0,0 +1,125 @@ +var Promise = require('../utils/promise'); +var PathUtils = require('../utils/path'); +var fs = require('../utils/fs'); + +var deprecate = require('./deprecate'); +var encodeConfig = require('./encodeConfig'); +var encodeNavigation = require('./encodeNavigation'); +var fileToURL = require('../output/helper/fileToURL'); + +/** + Encode a global context into a JS object + It's the context for page's hook, etc + + @param {Output} output + @return {Object} +*/ +function encodeGlobal(output) { + var book = output.getBook(); + var bookFS = book.getContentFS(); + var logger = output.getLogger(); + var outputFolder = output.getRoot(); + + var result = { + log: logger, + config: encodeConfig(output, book.getConfig()), + + isMultilingual: function() { + return book.isMultilingual(); + }, + + isLanguageBook: function() { + return book.isLanguageBook(); + }, + + isSubBook: deprecate.method(output, 'this.isSubBook', function() { + return book.isLanguageBook(); + }, '"isSubBook" is deprecated, use "isLanguageBook()" instead'), + + /** + Read a file from the book + + @param {String} fileName + @return {Promise<Buffer>} + */ + readFile: function(fileName) { + return bookFS.read(fileName); + }, + + /** + Read a file from the book as a string + + @param {String} fileName + @return {Promise<String>} + */ + readFileAsString: function(fileName) { + return bookFS.readAsString(fileName); + }, + + output: { + /** + Name of the generator being used + {String} + */ + name: output.getGenerator(), + + /** + Return absolute path to the root folder of output + @return {String} + */ + root: function() { + return outputFolder; + }, + + /** + Convert a filepath into an url + @return {String} + */ + toURL: function(filePath) { + return fileToURL(output, filePath); + }, + + /** + Write a file to the output folder, + It creates the required folder + + @param {String} fileName + @param {Buffer} content + @return {Promise} + */ + writeFile: function(fileName, content) { + return Promise() + .then(function() { + var filePath = PathUtils.resolveInRoot(outputFolder, fileName); + + return fs.ensureFile(filePath) + .then(function() { + return fs.writeFile(filePath, content); + }); + }); + } + } + }; + + // todo + // template.applyBlock + + // Deprecated properties + + deprecate.field(output, 'this.generator', result, 'generator', + output.getGenerator(), '"this.generator" property is deprecated, use "this.output.name" instead'); + + deprecate.field(output, 'this.navigation', result, 'navigation', function() { + return encodeNavigation(output); + }, '"navigation" property is deprecated'); + + deprecate.field(output, 'this.book', result, 'book', + result, '"book" property is deprecated, use "this" directly instead'); + + deprecate.field(output, 'this.options', result, 'options', + result.config.values, '"options" property is deprecated, use config.get(key) instead'); + + return result; +} + +module.exports = encodeGlobal; diff --git a/lib/api/encodeNavigation.js b/lib/api/encodeNavigation.js new file mode 100644 index 0000000..8e329a1 --- /dev/null +++ b/lib/api/encodeNavigation.js @@ -0,0 +1,64 @@ +var Immutable = require('immutable'); + +/** + Encode an article for next/prev + + @param {Map<String:Page>} + @param {Article} + @return {Object} +*/ +function encodeArticle(pages, article) { + var articlePath = article.getPath(); + + return { + path: articlePath, + title: article.getTitle(), + level: article.getLevel(), + exists: (articlePath && pages.has(articlePath)), + external: article.isExternal() + }; +} + +/** + this.navigation is a deprecated property from GitBook v2 + + @param {Output} + @return {Object} +*/ +function encodeNavigation(output) { + var book = output.getBook(); + var pages = output.getPages(); + var summary = book.getSummary(); + var articles = summary.getArticlesAsList(); + + + var navigation = articles + .map(function(article, i) { + var ref = article.getRef(); + if (!ref) { + return undefined; + } + + var prev = articles.get(i - 1); + var next = articles.get(i + 1); + + return [ + ref, + { + index: i, + title: article.getTitle(), + introduction: (i === 0), + prev: prev? encodeArticle(pages, prev) : undefined, + next: next? encodeArticle(pages, next) : undefined, + level: article.getLevel() + } + ]; + }) + .filter(function(e) { + return Boolean(e); + }); + + return Immutable.Map(navigation).toJS(); +} + +module.exports = encodeNavigation; diff --git a/lib/api/encodePage.js b/lib/api/encodePage.js new file mode 100644 index 0000000..379d3d5 --- /dev/null +++ b/lib/api/encodePage.js @@ -0,0 +1,39 @@ +var JSONUtils = require('../json'); +var deprecate = require('./deprecate'); +var encodeProgress = require('./encodeProgress'); + +/** + Encode a page in a context to a JS API + + @param {Output} output + @param {Page} page + @return {Object} +*/ +function encodePage(output, page) { + var book = output.getBook(); + var summary = book.getSummary(); + var fs = book.getContentFS(); + var file = page.getFile(); + + // JS Page is based on the JSON output + var result = JSONUtils.encodePage(page, summary); + + result.type = file.getType(); + result.path = file.getPath(); + result.rawPath = fs.resolve(result.path); + + deprecate.field(output, 'page.progress', result, 'progress', function() { + return encodeProgress(output, page); + }, '"page.progress" property is deprecated'); + + deprecate.field(output, 'page.sections', result, 'sections', [ + { + content: result.content, + type: 'normal' + } + ], '"sections" property is deprecated, use page.content instead'); + + return result; +} + +module.exports = encodePage; diff --git a/lib/api/encodeProgress.js b/lib/api/encodeProgress.js new file mode 100644 index 0000000..afa0341 --- /dev/null +++ b/lib/api/encodeProgress.js @@ -0,0 +1,63 @@ +var Immutable = require('immutable'); +var encodeNavigation = require('./encodeNavigation'); + +/** + page.progress is a deprecated property from GitBook v2 + + @param {Output} + @param {Page} + @return {Object} +*/ +function encodeProgress(output, page) { + var current = page.getPath(); + var navigation = encodeNavigation(output); + navigation = Immutable.Map(navigation); + + var n = navigation.size; + var percent = 0, prevPercent = 0, currentChapter = null; + var done = true; + + var chapters = navigation + .map(function(nav, chapterPath) { + nav.path = chapterPath; + return nav; + }) + .valueSeq() + .sortBy(function(nav) { + return nav.index; + }) + .map(function(nav, i) { + // Calcul percent + nav.percent = (i * 100) / Math.max((n - 1), 1); + + // Is it done + nav.done = done; + if (nav.path == current) { + currentChapter = nav; + percent = nav.percent; + done = false; + } else if (done) { + prevPercent = nav.percent; + } + + return nav; + }) + .toJS(); + + return { + // Previous percent + prevPercent: prevPercent, + + // Current percent + percent: percent, + + // List of chapter with progress + chapters: chapters, + + // Current chapter + current: currentChapter + }; +} + +module.exports = encodeProgress; + diff --git a/lib/api/index.js b/lib/api/index.js new file mode 100644 index 0000000..5e67525 --- /dev/null +++ b/lib/api/index.js @@ -0,0 +1,8 @@ + +module.exports = { + encodePage: require('./encodePage'), + decodePage: require('./decodePage'), + + encodeGlobal: require('./encodeGlobal'), + decodeGlobal: require('./decodeGlobal') +}; |