Skip to content

Instantly share code, notes, and snippets.

@jharding
Last active December 19, 2015 03:59
Show Gist options
  • Save jharding/5893957 to your computer and use it in GitHub Desktop.
Save jharding/5893957 to your computer and use it in GitHub Desktop.
(function(doc) {
var defaults = {
node: null,
pattern: null,
depth: -1,
tagName: 'strong',
className: 'jake',
wordsOnly: false,
caseSensitive: false
};
this.bearhug = function bearhug(o) {
var regex, textNodes, textNode, newNode;
o = mixin({}, defaults, o);
if (!o.node || !o.pattern) {
throw new Error('both node and pattern must be set');
}
// support wrapping multiple patterns
o.pattern = isArray(o.pattern) ? o.pattern : [o.pattern];
regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly);
textNodes = traverse(o.node, whatevs, o.depth);
function whatevs(textNode) {
if (regex.test(textNode.data)) {
newNode = strToNode(textNode.data);
textNode.parentNode.replaceChild(newNode, textNode);
}
}
function strToNode(str) {
var fragment = doc.createDocumentFragment(), nodes = getNodes(str);
for (var i = 0; i < nodes.length; i++) {
fragment.appendChild(nodes[i]);
}
return fragment;
}
function getNodes(str) {
var match = regex.exec(str), before, pattern, after, nodes = [];
if (match) {
before = match[1];
pattern = match[2];
after = match[3];
before && nodes.push.apply(nodes, getNodes(before));
nodes.push(wrapPattern(pattern));
after && nodes.push.apply(nodes, getNodes(after));
}
else {
nodes.push(doc.createTextNode(str));
}
return nodes;
}
function wrapPattern(str) {
var wrapperNode = doc.createElement(o.tagName);
o.className && (wrapperNode.className = o.className);
wrapperNode.appendChild(doc.createTextNode(str));
return wrapperNode;
}
};
function getRegex(patterns, caseSensitive, wordsOnly) {
var escapedPatterns = [], escapedPattern, regexStr;
for (var i = 0; i < patterns.length; i++) {
escapedPattern = wordsOnly ?
'\\b' + escapeRegexChars(patterns[i]) + '\\b' :
escapeRegexChars(patterns[i]);
escapedPatterns.push(escapedPattern);
}
regexStr = '^(.*)(' + escapedPatterns.join('|') + ')(.*)$';
return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, 'i');
}
function traverse(el, fn, depth) {
var numOfChildren = el.childNodes.length,
childNode,
textNodes = [],
TEXT_NODE_TYPE = 3;
for (var i = 0; i < numOfChildren; i++) {
childNode = el.childNodes[i];
if (childNode.nodeType === TEXT_NODE_TYPE) {
fn(childNode);
}
else if (childNode.className === 'jake') {
console.log('wat?');
}
else if (childNode.nodeType === 1 && depth !== 0) {
traverse(childNode, fn, depth - 1);
}
}
}
function mixin(target) {
var objs = [].slice.call(arguments, 1), obj;
for (var i = 0; i < objs.length; i++) {
obj = objs[i];
for (var key in obj) {
obj.hasOwnProperty(key) && (target[key] = obj[key]);
}
}
return target;
}
// http://stackoverflow.com/a/6969486
function escapeRegexChars(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
}
function isArray(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}
})(window.document);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment