diff --git a/src/findAndReplaceDOMText.js b/src/findAndReplaceDOMText.js index 359eef1..62f99bb 100644 --- a/src/findAndReplaceDOMText.js +++ b/src/findAndReplaceDOMText.js @@ -29,32 +29,51 @@ window.findAndReplaceDOMText = (function() { * @param {String|Element|Function} replacementNode A NodeName, * Node to clone, or a function which returns a node to use * as the replacement node. + * @param {Number} captureGroup A number specifiying which capture + * group to use in the match. (optional) */ - function findAndReplaceDOMText(regex, node, replacementNode) { + function findAndReplaceDOMText(regex, node, replacementNode, captureGroup) { - var m, index, matches = [], text = _getText(node); + var m, matches = [], text = _getText(node); var replaceFn = _genReplacer(replacementNode); if (!text) { return; } if (regex.global) { while (m = regex.exec(text)) { - if (!m[0]) throw 'findAndReplaceDOMText cannot handle zero-length matches'; - matches.push([regex.lastIndex - m[0].length, regex.lastIndex, m]); + matches.push(_getMatchIndexes(m, captureGroup)); } } else { m = text.match(regex); - index = text.indexOf(m[0]); - if (!m[0]) throw 'findAndReplaceDOMText cannot handle zero-length matches'; - matches.push([index, index + m[0].length, m]); + matches.push(_getMatchIndexes(m, captureGroup)); } if (matches.length) { _stepThroughMatches(node, matches, replaceFn); } - } + /** + * Gets the start and end indexes of a match + */ + function _getMatchIndexes(m, captureGroup) { + + captureGroup = captureGroup || 0; + + if (!m[0]) throw 'findAndReplaceDOMText cannot handle zero-length matches'; + + var index = m.index; + + if (captureGroup > 0) { + var cg = m[captureGroup]; + if (!cg) throw 'Invalid capture group'; + index += m[0].indexOf(cg); + m[0] = cg; + } + + return [ index, index + m[0].length, [ m[0] ] ]; + }; + /** * Gets aggregate text of a node without resorting * to broken innerText/textContent diff --git a/test/test.js b/test/test.js index 22bb616..3f3247b 100644 --- a/test/test.js +++ b/test/test.js @@ -106,4 +106,36 @@ test('Custom replacement function - correct ordering', function() { return document.createTextNode(fill); }); equal(nCalled, 3); +}); + +test('Capture groups', function() { + + var text = 'TEST TESThello TESThello TESThello'; + var d = document.createElement('div'); + + d.innerHTML = text; + findAndReplaceDOMText(/(TEST)hello/, d, 'x', 1); + htmlEqual(d.innerHTML, 'TEST TESThello TESThello TESTHello'); + + d.innerHTML = text; + findAndReplaceDOMText(/(TEST)hello/g, d, 'x', 1); + htmlEqual(d.innerHTML, 'TEST TESThello TESThello TESThello'); + + d.innerHTML = text; + findAndReplaceDOMText(/\s(TEST)(hello)/g, d, 'x', 2); + htmlEqual(d.innerHTML, 'TEST TESThello TESThello TESThello'); +}); + +test('Word boundaries', function() { + + var text = 'a go matching at test wordat at'; + var d = document.createElement('div'); + + d.innerHTML = text; + findAndReplaceDOMText(/\bat\b/, d, 'x'); + htmlEqual(d.innerHTML, 'a go matching at test wordat at'); + + d.innerHTML = text; + findAndReplaceDOMText(/\bat\b/g, d, 'x'); + htmlEqual(d.innerHTML, 'a go matching at test wordat at'); }); \ No newline at end of file