2014-11-14 44 views
4

我正在使用codemirror,配置爲顯示javascript。codemirror - 在編輯器中檢測並創建鏈接

我有這樣的代碼:

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

當顯示這將是巨大的,如果我可以點擊可能出現在編輯器中的鏈接編輯。鏈接顯然會在不同的選項卡上打開頁面。

有沒有簡單的方法來實現這一目標?

回答

5

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

  • 寫識別等各個環節的覆蓋模式。基本上,這是一種模式,當它找到看起來像鏈接的東西時會吐出自定義標記類型,否則爲空。您可以使用simple mode addon來簡化操作。您可以使用此令牌類型的CSS類(例如"link"變爲cm-link)來設置鏈接的樣式。

  • 通過調用addOverlay方法使編輯器使用覆蓋圖。

  • 在您的編輯器(instance.getWrapperElement().addEventListener(...))上註冊mousedown事件處理程序。

  • 在此處理程序中,檢查事件的target是否具有鏈接CSS類。如果是,則用戶點擊一個鏈接。

  • 如果是這樣,請使用coordsChar方法,使用鼠標事件中的座標來查找文檔中被點擊的位置。從該位置周圍的文檔文本中提取實際鏈接,然後按照它進行操作。 (或者,甚至更好的是,不管直接干擾點擊(可能意圖將光標置於鏈接或選擇它),而是顯示包含常規鏈接的小部件,只要光標位於鏈接文本內部即可。 )

4

這裏是我想出了一個解決方案:

演示在這裏: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*)[email protected])?(?:(?!(?: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>