codemirror - 在编辑器中检测和创建链接

时间:2014-11-14 15:26:45

标签: javascript codemirror

我正在使用codemirror,配置为显示javascript。

我有这样的代码:

...
var ref =  'http://www.example.com/test.html';
var ref2 = 'http://www.example.com/test2.html';
...

当显示编辑器时,如果我可以点击编辑器中可能存在的链接,那将是很棒的。该链接显然会在不同的选项卡上打开页面。

有一种简单的方法可以达到这个目的吗?

3 个答案:

答案 0 :(得分:12)

不是很容易,但你要做的是:

  • 编写可识别此类链接的叠加模式。基本上,这是一种模式,当它找到看起来像链接的东西时会吐出自定义标记类型,否则为null。您可以使用simple mode addon来简化此操作。您可以使用此令牌类型的CSS类(例如"link"变为cm-link)来设置链接的样式。

  • 通过调用addOverlay方法让编辑器使用叠加层。

  • 在您的编辑器上注册mousedown事件处理程序(instance.getWrapperElement().addEventListener(...))。

  • 在此处理程序中,检查事件' target是否具有链接CSS类。如果是,则用户点击链接。

  • 如果是这样,请使用coordsChar方法,使用鼠标事件中的坐标,找到单击文档中的位置。从该位置周围的文档文本中提取实际链接,然后按照该链接进行操作。

  • (或者,更好的是,不是直接干扰点击,而是可能将光标放在链接中或选择它,只要光标位于链接文本内,就会显示包含常规链接的窗口小部件。)

答案 1 :(得分:7)

这是我提出的解决方案:

演示:plunkr

代码:

<!DOCTYPE html>
<html>

<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.17.0/codemirror.js"></script>
<link rel="stylesheet" type="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);

function hoverWidgetOnOverlay(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 widget
            var 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();

        } else if ((e.target===widget || widget.contains(e.target))) { // entered widget, call widget.onEntered
            if (widget.dataset.entered==='true' && typeof widget.onEntered==='function')  widget.onEntered();
            widget.dataset.entered='true';

        } else if (!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();
        }

        return true;
    });
}

function hyperlinkOverlay(cm) {
    if (!cm) return;

    const rx_word = "\" "; // Define what separates a word

    function isUrl(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)) return false;
        for (let i=0; i<isUrl.prefixes.length; i++) if (s.startsWith(isUrl.prefixes[i])) return true;
        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]+'?')) return true;
        return false;
    }

    cm.addOverlay({
        token: function(stream) {
            let ch = stream.peek();
            let word = "";

            if (rx_word.includes(ch) || ch==='\uE000' || ch==='\uE001') {
                stream.next();
                return null;
            }

            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='&rarr;'
    widget.onclick=function(e) { 
        if (!widget.dataset.token) return;
        let link=widget.dataset.token;
        if (!(new RegExp('^(?:(?:https?|ftp):\/\/)', 'i')).test(link)) link="http:\/\/"+link;
        window.open(link, '_blank'); 
        return true;
    };
    hoverWidgetOnOverlay(cm, 'url', widget);
}

</script>
</body>

</html>

答案 2 :(得分:1)

这是一个起点,但有待改进。
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>"
      }
    }
  }