'use strict' module.exports = findAndReplace var visit = require('unist-util-visit-parents') var convert = require('unist-util-is/convert') var escape = require('escape-string-regexp') var splice = [].splice function findAndReplace(tree, find, replace, options) { var settings var schema if (typeof find === 'string' || (find && typeof find.exec === 'function')) { schema = [[find, replace]] } else { schema = find options = replace } settings = options || {} search(tree, settings, handlerFactory(toPairs(schema))) return tree function handlerFactory(pairs) { var pair = pairs[0] return handler function handler(node, parent) { var find = pair[0] var replace = pair[1] var nodes = [] var start = 0 var index = parent.children.indexOf(node) var position var match var subhandler var value find.lastIndex = 0 match = find.exec(node.value) while (match) { position = match.index value = replace.apply( null, [].concat(match, {index: match.index, input: match.input}) ) if (value !== false) { if (start !== position) { nodes.push({type: 'text', value: node.value.slice(start, position)}) } if (typeof value === 'string' && value.length > 0) { value = {type: 'text', value: value} } if (value) { nodes = [].concat(nodes, value) } start = position + match[0].length } if (!find.global) { break } match = find.exec(node.value) } if (position === undefined) { nodes = [node] index-- } else { if (start < node.value.length) { nodes.push({type: 'text', value: node.value.slice(start)}) } nodes.unshift(index, 1) splice.apply(parent.children, nodes) } if (pairs.length > 1) { subhandler = handlerFactory(pairs.slice(1)) position = -1 while (++position < nodes.length) { node = nodes[position] if (node.type === 'text') { subhandler(node, parent) } else { search(node, settings, subhandler) } } } return index + nodes.length + 1 } } } function search(tree, settings, handler) { var ignored = convert(settings.ignore || []) var result = [] visit(tree, 'text', visitor) return result function visitor(node, parents) { var index = -1 var parent var grandparent while (++index < parents.length) { parent = parents[index] if ( ignored( parent, grandparent ? grandparent.children.indexOf(parent) : undefined, grandparent ) ) { return } grandparent = parent } return handler(node, grandparent) } } function toPairs(schema) { var result = [] var key var index if (typeof schema !== 'object') { throw new Error('Expected array or object as schema') } if ('length' in schema) { index = -1 while (++index < schema.length) { result.push([ toExpression(schema[index][0]), toFunction(schema[index][1]) ]) } } else { for (key in schema) { result.push([toExpression(key), toFunction(schema[key])]) } } return result } function toExpression(find) { return typeof find === 'string' ? new RegExp(escape(find), 'g') : find } function toFunction(replace) { return typeof replace === 'function' ? replace : returner function returner() { return replace } }