我正在使用codemirror,配置爲顯示javascript。codemirror - 在編輯器中檢測並創建鏈接
我有這樣的代碼:
...
var ref = 'http://www.example.com/test.html';
var ref2 = 'http://www.example.com/test2.html';
...
當顯示這將是巨大的,如果我可以點擊可能出現在編輯器中的鏈接編輯。鏈接顯然會在不同的選項卡上打開頁面。
有沒有簡單的方法來實現這一目標?
我正在使用codemirror,配置爲顯示javascript。codemirror - 在編輯器中檢測並創建鏈接
我有這樣的代碼:
...
var ref = 'http://www.example.com/test.html';
var ref2 = 'http://www.example.com/test2.html';
...
當顯示這將是巨大的,如果我可以點擊可能出現在編輯器中的鏈接編輯。鏈接顯然會在不同的選項卡上打開頁面。
有沒有簡單的方法來實現這一目標?
不是很容易,但你會做的是:
寫識別等各個環節的覆蓋模式。基本上,這是一種模式,當它找到看起來像鏈接的東西時會吐出自定義標記類型,否則爲空。您可以使用simple mode addon來簡化操作。您可以使用此令牌類型的CSS類(例如"link"
變爲cm-link
)來設置鏈接的樣式。
通過調用addOverlay
方法使編輯器使用覆蓋圖。
在您的編輯器(instance.getWrapperElement().addEventListener(...)
)上註冊mousedown
事件處理程序。
在此處理程序中,檢查事件的target
是否具有鏈接CSS類。如果是,則用戶點擊一個鏈接。
如果是這樣,請使用coordsChar
方法,使用鼠標事件中的座標來查找文檔中被點擊的位置。從該位置周圍的文檔文本中提取實際鏈接,然後按照它進行操作。 (或者,甚至更好的是,不管直接干擾點擊(可能意圖將光標置於鏈接或選擇它),而是顯示包含常規鏈接的小部件,只要光標位於鏈接文本內部即可。 )
這裏是我想出了一個解決方案:
演示在這裏: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='→'
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>