refactor: renderer.js拆分为6个模块(core/tabs/editor/search/sidebar/markdown)
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
// MarkLite — Search & Replace Module
|
||||
(function () {
|
||||
'use strict';
|
||||
var ML = window.MarkLite;
|
||||
|
||||
var searchMatches = [];
|
||||
var searchIndex = -1;
|
||||
var searchCaseSensitive = false;
|
||||
var searchUseRegex = false;
|
||||
|
||||
ML.openSearch = function (showReplace) {
|
||||
ML.searchBar.classList.remove('hidden');
|
||||
ML.replaceRow.classList.toggle('hidden', !showReplace);
|
||||
ML.searchInput.focus();
|
||||
var selected = ML.editor.value.substring(ML.editor.selectionStart, ML.editor.selectionEnd);
|
||||
if (selected && !selected.includes('\n')) ML.searchInput.value = selected;
|
||||
doSearch();
|
||||
};
|
||||
|
||||
ML.closeSearch = function () {
|
||||
ML.searchBar.classList.add('hidden');
|
||||
clearHighlights();
|
||||
searchMatches = []; searchIndex = -1;
|
||||
ML.searchCount.textContent = '';
|
||||
ML.editor.focus();
|
||||
};
|
||||
|
||||
ML.toggleSearchOption = function (type) {
|
||||
if (type === 'case') { searchCaseSensitive = !searchCaseSensitive; ML.btnCase.classList.toggle('active', searchCaseSensitive); }
|
||||
else { searchUseRegex = !searchUseRegex; ML.btnRegex.classList.toggle('active', searchUseRegex); }
|
||||
doSearch();
|
||||
};
|
||||
|
||||
function doSearch() {
|
||||
clearHighlights();
|
||||
var text = ML.searchInput.value;
|
||||
if (!text) { searchMatches = []; searchIndex = -1; ML.searchCount.textContent = ''; return; }
|
||||
searchMatches = findMatches(text);
|
||||
if (searchMatches.length > 0) {
|
||||
var cursor = ML.editor.selectionStart;
|
||||
searchIndex = searchMatches.findIndex(function (m) { return m.start >= cursor; });
|
||||
if (searchIndex === -1) searchIndex = 0;
|
||||
scrollToMatch(searchIndex);
|
||||
} else { searchIndex = -1; }
|
||||
ML.searchCount.textContent = searchMatches.length > 0 ? (searchIndex + 1) + '/' + searchMatches.length : '无结果';
|
||||
highlightMatches();
|
||||
}
|
||||
|
||||
function findMatches(text) {
|
||||
var content = ML.editor.value;
|
||||
var matches = [];
|
||||
if (searchUseRegex) {
|
||||
try {
|
||||
var flags = searchCaseSensitive ? 'g' : 'gi';
|
||||
var regex = new RegExp(text, flags);
|
||||
var m;
|
||||
while ((m = regex.exec(content)) !== null) {
|
||||
matches.push({ start: m.index, end: m.index + m[0].length });
|
||||
if (m[0].length === 0) regex.lastIndex++;
|
||||
}
|
||||
} catch (e) { /* invalid regex */ }
|
||||
} else {
|
||||
var haystack = searchCaseSensitive ? content : content.toLowerCase();
|
||||
var needle = searchCaseSensitive ? text : text.toLowerCase();
|
||||
var idx = 0;
|
||||
while ((idx = haystack.indexOf(needle, idx)) !== -1) {
|
||||
matches.push({ start: idx, end: idx + needle.length });
|
||||
idx += needle.length;
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
function highlightMatches() {
|
||||
clearHighlights();
|
||||
if (searchMatches.length === 0) return;
|
||||
var content = ML.editor.value;
|
||||
var lineHeight = parseFloat(getComputedStyle(ML.editor).lineHeight) || 20.8;
|
||||
var editorStyle = getComputedStyle(ML.editor);
|
||||
var paddingTop = parseFloat(editorStyle.paddingTop);
|
||||
var paddingLeft = parseFloat(editorStyle.paddingLeft);
|
||||
var lineStarts = [0];
|
||||
for (var i = 0; i < content.length; i++) { if (content.charCodeAt(i) === 10) lineStarts.push(i + 1); }
|
||||
function posToXY(pos) {
|
||||
var line = 0;
|
||||
for (var _i = 0; _i < lineStarts.length; _i++) { if (lineStarts[_i] > pos) break; line = _i; }
|
||||
return { top: line * lineHeight + paddingTop, left: (pos - lineStarts[line]) * ML.getCharWidth() + paddingLeft };
|
||||
}
|
||||
var overlay = document.createElement('div');
|
||||
overlay.id = 'search-highlights';
|
||||
overlay.style.cssText = 'position:absolute;top:0;left:0;right:0;bottom:0;pointer-events:none;overflow:hidden;z-index:1;';
|
||||
var wrapper = document.getElementById('editor-wrapper');
|
||||
wrapper.style.position = 'relative';
|
||||
wrapper.appendChild(overlay);
|
||||
for (var _i2 = 0; _i2 < searchMatches.length; _i2++) {
|
||||
var m = searchMatches[_i2];
|
||||
var startPos = posToXY(m.start);
|
||||
var endPos = posToXY(m.end);
|
||||
var isCurrent = _i2 === searchIndex;
|
||||
var div = document.createElement('div');
|
||||
div.className = 'search-hl' + (isCurrent ? ' current' : '');
|
||||
div.style.cssText = 'position:absolute;top:' + startPos.top + 'px;left:' + startPos.left + 'px;height:' + lineHeight + 'px;width:' + Math.max(endPos.left - startPos.left, 8) + 'px;';
|
||||
overlay.appendChild(div);
|
||||
}
|
||||
}
|
||||
|
||||
function clearHighlights() { var old = document.getElementById('search-highlights'); if (old) old.remove(); }
|
||||
|
||||
function scrollToMatch(index) {
|
||||
if (index < 0 || index >= searchMatches.length) return;
|
||||
searchIndex = index;
|
||||
var m = searchMatches[index];
|
||||
ML.editor.setSelectionRange(m.start, m.end);
|
||||
var lineHeight = parseFloat(getComputedStyle(ML.editor).lineHeight) || 20.8;
|
||||
var content = ML.editor.value;
|
||||
var line = 0;
|
||||
for (var i = 0; i < m.start; i++) { if (content.charCodeAt(i) === 10) line++; }
|
||||
var matchTop = line * lineHeight;
|
||||
var viewTop = ML.editor.scrollTop;
|
||||
var viewBottom = viewTop + ML.editor.clientHeight;
|
||||
if (matchTop < viewTop + 40 || matchTop > viewBottom - 40) ML.editor.scrollTop = matchTop - ML.editor.clientHeight / 2;
|
||||
ML.searchCount.textContent = (index + 1) + '/' + searchMatches.length;
|
||||
highlightMatches();
|
||||
}
|
||||
|
||||
ML.findNext = function () { if (searchMatches.length === 0) return; scrollToMatch((searchIndex + 1) % searchMatches.length); };
|
||||
ML.findPrev = function () { if (searchMatches.length === 0) return; scrollToMatch((searchIndex - 1 + searchMatches.length) % searchMatches.length); };
|
||||
|
||||
ML.replaceCurrent = function () {
|
||||
if (searchMatches.length === 0 || searchIndex < 0) return;
|
||||
var m = searchMatches[searchIndex];
|
||||
ML.editor.setSelectionRange(m.start, m.end);
|
||||
document.execCommand('insertText', false, ML.replaceInput.value);
|
||||
doSearch();
|
||||
};
|
||||
|
||||
ML.replaceAll = function () {
|
||||
if (searchMatches.length === 0) return;
|
||||
var replacement = ML.replaceInput.value;
|
||||
for (var i = searchMatches.length - 1; i >= 0; i--) {
|
||||
var m = searchMatches[i];
|
||||
ML.editor.setSelectionRange(m.start, m.end);
|
||||
document.execCommand('insertText', false, replacement);
|
||||
}
|
||||
doSearch();
|
||||
};
|
||||
|
||||
ML.initSearchBar = function () {
|
||||
ML.searchInput.addEventListener('input', doSearch);
|
||||
ML.btnCase.addEventListener('click', function () { ML.toggleSearchOption('case'); });
|
||||
ML.btnRegex.addEventListener('click', function () { ML.toggleSearchOption('regex'); });
|
||||
ML.btnNext.addEventListener('click', ML.findNext);
|
||||
ML.btnPrev.addEventListener('click', ML.findPrev);
|
||||
ML.btnSearchClose.addEventListener('click', ML.closeSearch);
|
||||
ML.btnReplace.addEventListener('click', ML.replaceCurrent);
|
||||
ML.btnReplaceAll.addEventListener('click', ML.replaceAll);
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user