summaryrefslogtreecommitdiffstats
path: root/lib/api
diff options
context:
space:
mode:
authorSamy Pesse <samypesse@gmail.com>2016-04-30 20:15:08 +0200
committerSamy Pesse <samypesse@gmail.com>2016-04-30 20:15:08 +0200
commit36b49c66c6b75515bc84dd678fd52121a313e8d2 (patch)
treebc7e0f703d4557869943ec7f9495cac7a5027d4f /lib/api
parent87db7cf1d412fa6fbd18e9a7e4f4755f2c0c5547 (diff)
parent80b8e340dadc54377ff40500f86b1de631395806 (diff)
downloadgitbook-36b49c66c6b75515bc84dd678fd52121a313e8d2.zip
gitbook-36b49c66c6b75515bc84dd678fd52121a313e8d2.tar.gz
gitbook-36b49c66c6b75515bc84dd678fd52121a313e8d2.tar.bz2
Merge branch 'fixes'
Diffstat (limited to 'lib/api')
-rw-r--r--lib/api/decodeConfig.js19
-rw-r--r--lib/api/decodeGlobal.js22
-rw-r--r--lib/api/decodePage.js44
-rw-r--r--lib/api/deprecate.js104
-rw-r--r--lib/api/encodeConfig.js36
-rw-r--r--lib/api/encodeGlobal.js125
-rw-r--r--lib/api/encodeNavigation.js64
-rw-r--r--lib/api/encodePage.js39
-rw-r--r--lib/api/encodeProgress.js63
-rw-r--r--lib/api/index.js8
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')
+};