| var pkg = require('../package.json') | 
| var api = require('./api.js') | 
|   | 
| var parser = require('posthtml-parser') | 
| var render = require('posthtml-render') | 
|   | 
| /** | 
|  * @author Ivan Voischev (@voischev), | 
|  *         Anton Winogradov (@awinogradov), | 
|  *         Alexej Yaroshevich (@zxqfox), | 
|  *         Vasiliy (@Yeti-or) | 
|  * | 
|  * @requires api | 
|  * @requires posthtml-parser | 
|  * @requires posthtml-render | 
|  * | 
|  * @constructor PostHTML | 
|  * @param {Array} plugins - An array of PostHTML plugins | 
|  */ | 
| function PostHTML (plugins) { | 
| /** | 
|  * PostHTML Instance | 
|  * | 
|  * @prop plugins | 
|  * @prop options | 
|  */ | 
|   this.version = pkg.version | 
|   this.name = pkg.name | 
|   this.plugins = typeof plugins === 'function' ? [plugins] : plugins || [] | 
| } | 
|   | 
| /** | 
|  * @requires posthtml-parser | 
|  * | 
|  * @param   {String} html - Input (HTML) | 
|  * @returns {Array}  tree - PostHTMLTree (JSON) | 
|  */ | 
| PostHTML.parser = parser | 
| /** | 
|  * @requires posthtml-render | 
|  * | 
|  * @param   {Array}  tree - PostHTMLTree (JSON) | 
|  * @returns {String} html - HTML | 
|  */ | 
| PostHTML.render = render | 
|   | 
| /** | 
| * @this posthtml | 
| * @param   {Function} plugin - A PostHTML plugin | 
| * @returns {Constructor} - this(PostHTML) | 
| * | 
| * **Usage** | 
| * ```js | 
| * ph.use((tree) => { tag: 'div', content: tree }) | 
| *   .process('<html>..</html>', {}) | 
| *   .then((result) => result)) | 
| * ``` | 
| */ | 
| PostHTML.prototype.use = function () { | 
|   [].push.apply(this.plugins, arguments) | 
|   return this | 
| } | 
|   | 
| /** | 
|  * @param   {String} html - Input (HTML) | 
|  * @param   {?Object} options - PostHTML Options | 
|  * @returns {Object<{html: String, tree: PostHTMLTree}>} - Sync Mode | 
|  * @returns {Promise<{html: String, tree: PostHTMLTree}>} - Async Mode (default) | 
|  * | 
|  * **Usage** | 
|  * | 
|  * **Sync** | 
|  * ```js | 
|  * ph.process('<html>..</html>', { sync: true }).html | 
|  * ``` | 
|  * | 
|  * **Async** | 
|  * ```js | 
|  * ph.process('<html>..</html>', {}).then((result) => result)) | 
|  * ``` | 
|  */ | 
| PostHTML.prototype.process = function (tree, options) { | 
|   /** | 
|    * ## PostHTML Options | 
|    * | 
|    * @type {Object} | 
|    * @prop {?Boolean} options.sync - enables sync mode, plugins will run synchronously, throws an error when used with async plugins | 
|    * @prop {?Function} options.parser - use custom parser, replaces default (posthtml-parser) | 
|    * @prop {?Function} options.render - use custom render, replaces default (posthtml-render) | 
|    * @prop {?Boolean} options.skipParse - disable parsing | 
|    */ | 
|   options = options || {} | 
|   | 
|   if (options.parser) parser = options.parser | 
|   if (options.render) render = options.render | 
|   | 
|   tree = options.skipParse ? tree : parser(tree) | 
|   | 
|   tree.options = options | 
|   tree.processor = this | 
|   | 
|   // sync mode | 
|   if (options.sync === true) { | 
|     this.plugins.forEach(function (plugin) { | 
|       apiExtend(tree) | 
|   | 
|       var result | 
|   | 
|       if (plugin.length === 2 || isPromise(result = plugin(tree))) { | 
|         throw new Error( | 
|           'Can’t process contents in sync mode because of async plugin: ' + plugin.name | 
|         ) | 
|       } | 
|       // return the previous tree unless result is fulfilled | 
|       tree = result || tree | 
|     }) | 
|   | 
|     return lazyResult(render, tree) | 
|   } | 
|   | 
|   // async mode | 
|   var i = 0 | 
|   | 
|   var next = function (result, cb) { | 
|     // all plugins called | 
|     if (this.plugins.length <= i) { | 
|       cb(null, result) | 
|       return | 
|     } | 
|   | 
|     // little helper to go to the next iteration | 
|     function _next (res) { | 
|       return next(res || result, cb) | 
|     } | 
|   | 
|     // (re)extend the object | 
|     apiExtend(result) | 
|   | 
|     // call next | 
|     var plugin = this.plugins[i++] | 
|   | 
|     if (plugin.length === 2) { | 
|       plugin(result, function (err, res) { | 
|         if (err) return cb(err) | 
|         _next(res) | 
|       }) | 
|       return | 
|     } | 
|   | 
|     // sync and promised plugins | 
|     var err = null | 
|   | 
|     var res = tryCatch(function () { | 
|       return plugin(result) | 
|     }, function (err) { | 
|       return err | 
|     }) | 
|   | 
|     if (err) { | 
|       cb(err) | 
|       return | 
|     } | 
|   | 
|     if (isPromise(res)) { | 
|       res.then(_next).catch(cb) | 
|       return | 
|     } | 
|   | 
|     _next(res) | 
|   }.bind(this) | 
|   | 
|   return new Promise(function (resolve, reject) { | 
|     next(tree, function (err, tree) { | 
|       if (err) reject(err) | 
|       else resolve(lazyResult(render, tree)) | 
|     }) | 
|   }) | 
| } | 
|   | 
| /** | 
|  * @exports posthtml | 
|  * | 
|  * @param  {Array} plugins | 
|  * @return {Function} posthtml | 
|  * | 
|  * **Usage** | 
|  * ```js | 
|  * import posthtml from 'posthtml' | 
|  * import plugin from 'posthtml-plugin' | 
|  * | 
|  * const ph = posthtml([ plugin() ]) | 
|  * ``` | 
|  */ | 
| module.exports = function (plugins) { | 
|   return new PostHTML(plugins) | 
| } | 
|   | 
| /** | 
|  * Checks if parameter is a Promise (or thenable) object. | 
|  * | 
|  * @private | 
|  * | 
|  * @param   {*} promise - Target `{}` to test | 
|  * @returns {Boolean} | 
|  */ | 
| function isPromise (promise) { | 
|   return !!promise && typeof promise.then === 'function' | 
| } | 
|   | 
| /** | 
|  * Simple try/catch helper, if exists, returns result | 
|  * | 
|  * @private | 
|  * | 
|  * @param   {Function} tryFn - try block | 
|  * @param   {Function} catchFn - catch block | 
|  * @returns {?*} | 
|  */ | 
| function tryCatch (tryFn, catchFn) { | 
|   try { | 
|     return tryFn() | 
|   } catch (err) { | 
|     catchFn(err) | 
|   } | 
| } | 
|   | 
| /** | 
|  * Extends the PostHTMLTree with the Tree API | 
|  * | 
|  * @private | 
|  * | 
|  * @param   {Array} tree - PostHTMLTree | 
|  * @returns {Array} tree - PostHTMLTree with API | 
|  */ | 
| function apiExtend (tree) { | 
|   tree.walk = api.walk | 
|   tree.match = api.match | 
| } | 
|   | 
| /** | 
|  * Wraps the PostHTMLTree within an object using a getter to render HTML on demand. | 
|  * | 
|  * @private | 
|  * | 
|  * @param   {Function} render | 
|  * @param   {Array}    tree | 
|  * @returns {Object<{html: String, tree: Array}>} | 
|  */ | 
| function lazyResult (render, tree) { | 
|   return { | 
|     get html () { | 
|       return render(tree, tree.options) | 
|     }, | 
|     tree: tree | 
|   } | 
| } |