Created
August 12, 2021 15:44
-
-
Save egm0121/5877dc10b6fc005ee1123a2930dbf05d to your computer and use it in GitHub Desktop.
linkify-content-script for chrome extensions: wraps a page phone numbers into clickable links ( uses XPATH and event delegation for performance)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var linkifyExtension = (function(){ | |
var LINKIFY_EXTENSION_CLASS = 'linkify-phone-match'; | |
var EXCLUDE_NODES = ['script', 'style', 'input', 'select', 'textarea', 'button', 'a', 'code', 'head', 'noscript']; | |
var PHONE_MATCHERS = { | |
US : { | |
reg : /(^|\s)((\+1\d{10})|((\+1[ \.])?\(?\d{3}\)?[ \-\.\/]{1,3}\d{3}[ \-\.]{1,2}\d{4}))(\s|$)|(^|\s)\+(?:[0-9] ?){6,14}[0-9](\s|$)/gm, | |
match : 2 | |
} , | |
PERMISSIVE : { | |
reg : /(^|\s)(?:\+?(\d{1,3}))?([-. (]*(\d{3})[-. )]*)?((\d{3})[-. ]*(\d{2,4})(?:[-.x ]*(\d+))?)(\s|$)/gm, | |
match : 0 | |
} | |
}; | |
var XPATH_TEXT_MATCHER = '//*[not('+EXCLUDE_NODES.map(function(i){ return 'ancestor-or-self::'+i; }).join(' or ')+')]/text()[normalize-space()]'; | |
// http://james.padolsey.com/javascript/replacing-text-in-the-dom-its-not-that-simple/ | |
var generateLinkable = (function(){ | |
var uuid = 0; | |
return function(nbr){ | |
uuid++; | |
return ["<a href='javascript:void()' class='"+LINKIFY_EXTENSION_CLASS+"' id='linkify-phone-id-"+uuid+"'>"+nbr+"</a>",uuid]; | |
}; | |
})(); | |
var wrapMatch = function(textNode,nbrArr) { | |
var temp = document.createElement('div'); | |
var linkables = nbrArr.map(generateLinkable); | |
var tempHTML = textNode.data; | |
linkables.map(function(linkable,i){ | |
tempHTML = tempHTML.replace(nbrArr[i],linkable[0]); | |
}); | |
temp.innerHTML = tempHTML; | |
while (temp.firstChild) { | |
textNode.parentNode.insertBefore( temp.firstChild, textNode ); | |
} | |
// Remove original text-node: | |
textNode.parentNode.removeChild(textNode); | |
}; | |
var handleClickToCall = function(e){ | |
console.log('send event to bg page'); | |
chrome.runtime.sendMessage({e:'contentScript.request.call',data:e.target.innerText}); | |
}; | |
var mapTextNodes = function(rootEl,callback){ | |
var textNodes = document.evaluate(XPATH_TEXT_MATCHER, rootEl, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE); | |
var l = textNodes.snapshotLength; | |
for(var i = 0; i < l; i++){ | |
var currItem = textNodes.snapshotItem(i); | |
if(typeof callback === "function")callback(currItem); | |
}; | |
}; | |
return { | |
lastLinkRef : false, | |
isParsing:false, | |
linkify : function() { | |
console.time("linkify took"); | |
this.isParsing = true; | |
console.log('linkify!'); | |
mapTextNodes(document,function(currItem){ | |
if(currItem.parentNode && currItem.parentNode.classList.contains(LINKIFY_EXTENSION_CLASS) ){ | |
console.log('skip it!'); | |
return; | |
} | |
var currText = currItem.textContent , matches = false, nbrArr = []; | |
while(matches = PHONE_MATCHERS.PERMISSIVE.reg.exec(currText)){ | |
var match = matches[PHONE_MATCHERS.PERMISSIVE.match]; | |
digitLength = match.replace(/\D/g, ''); | |
if(digitLength.length > 6){ | |
nbrArr.push( match ); | |
} | |
} | |
if(nbrArr.length && nbrArr.filter(function(i){return i;}).length){ | |
console.log('FOUND :',nbrArr); | |
wrapMatch(currItem,nbrArr); | |
} | |
}); | |
this.isParsing = false; | |
console.timeEnd("linkify took"); | |
}, | |
bindDomEvents : function(){ | |
var that = this; | |
console.log("XPATH using : "+XPATH_TEXT_MATCHER); | |
document.addEventListener('DOMNodeInserted', function(){ | |
if(!that.isParsing){ | |
if(that.lastLinkRef)clearTimeout(that.lastLinkRef); | |
that.lastLinkRef = setTimeout(function(){that.linkify();},10); | |
} | |
}); | |
document.querySelector('body').addEventListener("click",function(e){ | |
if(e.target && e.target.classList && | |
e.target.classList.contains(LINKIFY_EXTENSION_CLASS)){ | |
handleClickToCall(e); | |
} | |
}); | |
}, | |
}; | |
})(); | |
setTimeout(function(){ | |
linkifyExtension.linkify(); | |
},10); | |
linkifyExtension.bindDomEvents(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment