// 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); }; })();