Codemirror - Detect And Create Links Inside Editor
Solution 1:
Not really easy, but what you'd do is:
Write an overlay mode that recognizes such links. Basically, this is a mode that spits out a custom token type when it finds something that looks like a link, and null otherwise. You can use the simple mode addon to make this easier. You can use this token type's CSS class (for example
"link"
becomescm-link
) to style your links.Make your editor use your overlay by calling the
addOverlay
method.Register a
mousedown
event handler on your editor (instance.getWrapperElement().addEventListener(...)
).In this handler, check whether the event's
target
has the link CSS class. If it does, the user is clicking a link.If so, use the
coordsChar
method, using the coordinates from your mouse event, to find the position in the document that was clicked. Extract the actual link from the document text around that position, and follow it.(Or, even better, instead of directly interfering with the click, which might be intended to put the cursor in the link or select it, show a widget containing a regular link whenever the cursor is inside of link text.)
Solution 2:
Here is a solution I came up with:
demo here: plunkr
code:
<!DOCTYPE html><html><head><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.17.0/codemirror.js"></script><linktype="text/css"href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.17.0/codemirror.css"/><style>html, body { height:100%; }
.CodeMirror.cm-url { color: blue; }
</style></head><body><script>var cm = CodeMirror(document.body);
cm.setValue('hover over the links below\nlink1 https://plnkr.co/edit/5m31E14HUEhSXrXtOkNJ some text\nlink2 google.com\n');
hyperlinkOverlay(cm);
functionhoverWidgetOnOverlay(cm, overlayClass, widget) {
cm.addWidget({line:0, ch:0}, widget, true);
widget.style.position = 'fixed';
widget.style.zIndex=100000;
widget.style.top=widget.style.left='-1000px'; // hide it
widget.dataset.token=null;
cm.getWrapperElement().addEventListener('mousemove', e => {
let onToken=e.target.classList.contains("cm-"+overlayClass), onWidget=(e.target===widget || widget.contains(e.target));
if (onToken && e.target.innerText!==widget.dataset.token) { // entered token, show widgetvar rect = e.target.getBoundingClientRect();
widget.style.left=rect.left+'px';
widget.style.top=rect.bottom+'px';
//let charCoords=cm.charCoords(cm.coordsChar({ left: e.pageX, top:e.pageY }));//widget.style.left=(e.pageX-5)+'px'; //widget.style.top=(cm.charCoords(cm.coordsChar({ left: e.pageX, top:e.pageY })).bottom-1)+'px';
widget.dataset.token=e.target.innerText;
if (typeof widget.onShown==='function') widget.onShown();
} elseif ((e.target===widget || widget.contains(e.target))) { // entered widget, call widget.onEnteredif (widget.dataset.entered==='true' && typeof widget.onEntered==='function') widget.onEntered();
widget.dataset.entered='true';
} elseif (!onToken && widget.style.left!=='-1000px') { // we stepped outside
widget.style.top=widget.style.left='-1000px'; // hide it delete widget.dataset.token;
widget.dataset.entered='false';
if (typeof widget.onHidden==='function') widget.onHidden();
}
returntrue;
});
}
functionhyperlinkOverlay(cm) {
if (!cm) return;
const rx_word = "\" "; // Define what separates a wordfunctionisUrl(s) {
if (!isUrl.rx_url) {
// taken from https://gist.github.com/dperini/729294
isUrl.rx_url=/^(?:(?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i;
// valid prefixes
isUrl.prefixes=['http:\/\/', 'https:\/\/', 'ftp:\/\/', 'www.'];
// taken from https://w3techs.com/technologies/overview/top_level_domain/all
isUrl.domains=['com','ru','net','org','de','jp','uk','br','pl','in','it','fr','au','info','nl','ir','cn','es','cz','kr','ua','ca','eu','biz','za','gr','co','ro','se','tw','mx','vn','tr','ch','hu','at','be','dk','tv','me','ar','no','us','sk','xyz','fi','id','cl','by','nz','il','ie','pt','kz','io','my','lt','hk','cc','sg','edu','pk','su','bg','th','top','lv','hr','pe','club','rs','ae','az','si','ph','pro','ng','tk','ee','asia','mobi'];
}
if (!isUrl.rx_url.test(s)) returnfalse;
for (let i=0; i<isUrl.prefixes.length; i++) if (s.startsWith(isUrl.prefixes[i])) returntrue;
for (let i=0; i<isUrl.domains.length; i++) if (s.endsWith('.'+isUrl.domains[i]) || s.includes('.'+isUrl.domains[i]+'\/') ||s.includes('.'+isUrl.domains[i]+'?')) returntrue;
returnfalse;
}
cm.addOverlay({
token: function(stream) {
let ch = stream.peek();
let word = "";
if (rx_word.includes(ch) || ch==='\uE000' || ch==='\uE001') {
stream.next();
returnnull;
}
while ((ch = stream.peek()) && !rx_word.includes(ch)) {
word += ch;
stream.next();
}
if (isUrl(word)) return"url"; // CSS class: cm-url
}},
{ opaque : true } // opaque will remove any spelling overlay etc
);
let widget=document.createElement('button');
widget.innerHTML='→'
widget.onclick=function(e) {
if (!widget.dataset.token) return;
let link=widget.dataset.token;
if (!(newRegExp('^(?:(?:https?|ftp):\/\/)', 'i')).test(link)) link="http:\/\/"+link;
window.open(link, '_blank');
returntrue;
};
hoverWidgetOnOverlay(cm, 'url', widget);
}
</script></body></html>
Solution 3:
Here is a starting point, but it need to be improved. LIVE DEMO
function makeHyperLink(innerTextInside)
{
var all = document.getElementsByTagName("*");
for (var i=0, max=all.length; i < max; i++) {
if(all[i].innerText == innerTextInside)
{
all[i].innerHTML="<a target='_blank' href='https://google.com'>THIS IS A LINK TO GOOGLE</a>"
}
}
}
Post a Comment for "Codemirror - Detect And Create Links Inside Editor"