| 'use strict' | 
| /** | 
|  * # API | 
|  * @author Ivan Voischev (@voischev), | 
|  *         Anton Winogradov (@awinogradov), | 
|  *         Alexej Yaroshevich (@zxqfox), | 
|  *         Vasiliy (@Yeti-or) | 
|  * @module API | 
|  * @namespace tree | 
|  */ | 
| module.exports = { | 
|   /** | 
|    * walk the tree and pass all nodes to callback | 
|    * | 
|    * @memberof tree | 
|    * @param  {Function} cb - Callback | 
|    * @return {Function}    - Node in callback | 
|    * | 
|    ***Usage** | 
|    * ```js | 
|    * export const walk = (tree) => { | 
|    *   tree.walk((node) => { | 
|    *     let classes = node.attrs && node.attrs.class.split(' ') || [] | 
|    * | 
|    *     if (classes.includes(className)) return cb(node) | 
|    *       return node | 
|    *   }) | 
|    * } | 
|    * ``` | 
|    */ | 
|   walk: function (cb) { | 
|     return traverse(this, cb) | 
|   }, | 
|   /** | 
|    * match expression to search nodes in the tree | 
|    * | 
|    * @memberof tree | 
|    * @param  {String|RegExp|Object|Array} expression - Matcher(s) to search | 
|    * @param  {Function} cb - Callback | 
|    * @return {Function}    - Node in callback | 
|    * | 
|    ***Usage** | 
|    * ```js | 
|    * export const match = (tree) => { | 
|    *   // Single matcher | 
|    *   tree.match({ tag: 'custom-tag' }, (node) => { | 
|    *     let tag = node.tag | 
|    * | 
|    *     Object.assign(node, { tag: 'div', attrs: {class: tag} }) | 
|    * | 
|    *     return node | 
|    *   }) | 
|    *   // Multiple matchers | 
|    *   tree.match([{ tag: 'b' }, { tag: 'strong' }], (node) => { | 
|    *     let style = 'font-weight: bold;' | 
|    * | 
|    *     node.tag = 'span' | 
|    * | 
|    *     node.attrs | 
|    *       ? ( node.attrs.style | 
|    *         ? ( node.attrs.style += style ) | 
|    *         : node.attrs.style = style | 
|    *       ) | 
|    *       : node.attrs = { style: style } | 
|    * | 
|    *     return node | 
|    *   }) | 
|    * } | 
|    * ``` | 
|    */ | 
|   match: function (expression, cb) { | 
|     return Array.isArray(expression) | 
|       ? traverse(this, function (node) { | 
|         for (var i = 0; i < expression.length; i++) { | 
|           if (compare(expression[i], node)) return cb(node) | 
|         } | 
|   | 
|         return node | 
|       }) | 
|       : traverse(this, function (node) { | 
|         if (compare(expression, node)) return cb(node) | 
|   | 
|         return node | 
|       }) | 
|   } | 
| } | 
|   | 
| /** @private */ | 
| function traverse (tree, cb) { | 
|   if (Array.isArray(tree)) { | 
|     for (var i = 0; i < tree.length; i++) { | 
|       tree[i] = traverse(cb(tree[i]), cb) | 
|     } | 
|   } else if ( | 
|       tree && | 
|       typeof tree === 'object' && | 
|       tree.hasOwnProperty('content') | 
|   ) traverse(tree.content, cb) | 
|   | 
|   return tree | 
| } | 
|   | 
| /** @private */ | 
| function compare (expected, actual) { | 
|   if (expected instanceof RegExp) { | 
|     if (typeof actual === 'object') return false | 
|     if (typeof actual === 'string') return expected.test(actual) | 
|   } | 
|   | 
|   if (typeof expected !== typeof actual) return false | 
|   if (typeof expected !== 'object' || expected === null) { | 
|     return expected === actual | 
|   } | 
|   | 
|   if (Array.isArray(expected)) { | 
|     return expected.every(function (exp) { | 
|       return [].some.call(actual, function (act) { | 
|         return compare(exp, act) | 
|       }) | 
|     }) | 
|   } | 
|   | 
|   return Object.keys(expected).every(function (key) { | 
|     var ao = actual[key] | 
|     var eo = expected[key] | 
|   | 
|     if (typeof eo === 'object' && eo !== null && ao !== null) { | 
|       return compare(eo, ao) | 
|     } | 
|     if (typeof eo === 'boolean') { | 
|       return eo !== (ao == null) | 
|     } | 
|   | 
|     return ao === eo | 
|   }) | 
| } |