diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/mail/components/compose/content/dialogs/EdReplace.js | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | comm/mail/components/compose/content/dialogs/EdReplace.js | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/comm/mail/components/compose/content/dialogs/EdReplace.js b/comm/mail/components/compose/content/dialogs/EdReplace.js new file mode 100644 index 0000000000..c937702416 --- /dev/null +++ b/comm/mail/components/compose/content/dialogs/EdReplace.js @@ -0,0 +1,380 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* import-globals-from ../editorUtilities.js */ +/* import-globals-from EdDialogCommon.js */ + +var gReplaceDialog; // Quick access to document/form elements. +var gFindInst; // nsIWebBrowserFind that we're going to use +var gFindService; // Global service which remembers find params +var gEditor; // the editor we're using + +document.addEventListener("dialogaccept", event => { + onFindNext(); + event.preventDefault(); +}); + +function initDialogObject() { + // Create gReplaceDialog object and initialize. + gReplaceDialog = {}; + gReplaceDialog.findInput = document.getElementById("dialog.findInput"); + gReplaceDialog.replaceInput = document.getElementById("dialog.replaceInput"); + gReplaceDialog.caseSensitive = document.getElementById( + "dialog.caseSensitive" + ); + gReplaceDialog.wrap = document.getElementById("dialog.wrap"); + gReplaceDialog.searchBackwards = document.getElementById( + "dialog.searchBackwards" + ); + gReplaceDialog.findNext = document.getElementById("findNext"); + gReplaceDialog.replace = document.getElementById("replace"); + gReplaceDialog.replaceAndFind = document.getElementById("replaceAndFind"); + gReplaceDialog.replaceAll = document.getElementById("replaceAll"); +} + +function loadDialog() { + // Set initial dialog field contents. + // Set initial dialog field contents. Use the gFindInst attributes first, + // this is necessary for window.find() + gReplaceDialog.findInput.value = gFindInst.searchString + ? gFindInst.searchString + : gFindService.searchString; + gReplaceDialog.replaceInput.value = gFindService.replaceString; + gReplaceDialog.caseSensitive.checked = gFindInst.matchCase + ? gFindInst.matchCase + : gFindService.matchCase; + gReplaceDialog.wrap.checked = gFindInst.wrapFind + ? gFindInst.wrapFind + : gFindService.wrapFind; + gReplaceDialog.searchBackwards.checked = gFindInst.findBackwards + ? gFindInst.findBackwards + : gFindService.findBackwards; + + doEnabling(); +} + +function onLoad() { + // Get the xul <editor> element: + var editorElement = window.arguments[0]; + + // If we don't get the editor, then we won't allow replacing. + gEditor = editorElement.getEditor(editorElement.contentWindow); + if (!gEditor) { + window.close(); + return; + } + + // Get the nsIWebBrowserFind service: + gFindInst = editorElement.webBrowserFind; + + try { + // get the find service, which stores global find state + gFindService = Cc["@mozilla.org/find/find_service;1"].getService( + Ci.nsIFindService + ); + } catch (e) { + dump("No find service!\n"); + gFindService = 0; + } + + // Init gReplaceDialog. + initDialogObject(); + + // Change "OK" to "Find". + // dialog.find.label = document.getElementById("fBLT").getAttribute("label"); + + // Fill dialog. + loadDialog(); + + if (gReplaceDialog.findInput.value) { + gReplaceDialog.findInput.select(); + } else { + gReplaceDialog.findInput.focus(); + } +} + +function saveFindData() { + // Set data attributes per user input. + if (gFindService) { + gFindService.searchString = gReplaceDialog.findInput.value; + gFindService.matchCase = gReplaceDialog.caseSensitive.checked; + gFindService.wrapFind = gReplaceDialog.wrap.checked; + gFindService.findBackwards = gReplaceDialog.searchBackwards.checked; + } +} + +function setUpFindInst() { + gFindInst.searchString = gReplaceDialog.findInput.value; + gFindInst.matchCase = gReplaceDialog.caseSensitive.checked; + gFindInst.wrapFind = gReplaceDialog.wrap.checked; + gFindInst.findBackwards = gReplaceDialog.searchBackwards.checked; +} + +function onFindNext() { + // Transfer dialog contents to the find service. + saveFindData(); + // set up the find instance + setUpFindInst(); + + // Search. + var result = gFindInst.findNext(); + + if (!result) { + var bundle = document.getElementById("findBundle"); + Services.prompt.alert( + window, + GetString("Alert"), + bundle.getString("notFoundWarning") + ); + SetTextboxFocus(gReplaceDialog.findInput); + gReplaceDialog.findInput.select(); + gReplaceDialog.findInput.focus(); + return false; + } + return true; +} + +function onReplace() { + if (!gEditor) { + return false; + } + + // Does the current selection match the find string? + var selection = gEditor.selection; + + var selStr = selection.toString(); + var specStr = gReplaceDialog.findInput.value; + if (!gReplaceDialog.caseSensitive.checked) { + selStr = selStr.toLowerCase(); + specStr = specStr.toLowerCase(); + } + // Unfortunately, because of whitespace we can't just check + // whether (selStr == specStr), but have to loop ourselves. + // N chars of whitespace in specStr can match any M >= N in selStr. + var matches = true; + var specLen = specStr.length; + var selLen = selStr.length; + if (selLen < specLen) { + matches = false; + } else { + var specArray = specStr.match(/\S+|\s+/g); + var selArray = selStr.match(/\S+|\s+/g); + if (specArray.length != selArray.length) { + matches = false; + } else { + for (var i = 0; i < selArray.length; i++) { + if (selArray[i] != specArray[i]) { + if (/\S/.test(selArray[i][0]) || /\S/.test(specArray[i][0])) { + // not a space chunk -- match fails + matches = false; + break; + } else if (selArray[i].length < specArray[i].length) { + // if it's a space chunk then we only care that sel be + // at least as long as spec + matches = false; + break; + } + } + } + } + } + + // If the current selection doesn't match the pattern, + // then we want to find the next match, but not do the replace. + // That's what most other apps seem to do. + // So here, just return. + if (!matches) { + return false; + } + + // Transfer dialog contents to the find service. + saveFindData(); + + // For reverse finds, need to remember the caret position + // before current selection + var newRange; + if (gReplaceDialog.searchBackwards.checked && selection.rangeCount > 0) { + newRange = selection.getRangeAt(0).cloneRange(); + newRange.collapse(true); + } + + // nsPlaintextEditor::InsertText fails if the string is empty, + // so make that a special case: + var replStr = gReplaceDialog.replaceInput.value; + if (replStr == "") { + gEditor.deleteSelection(gEditor.eNone, gEditor.eStrip); + } else { + gEditor.insertText(replStr); + } + + // For reverse finds, need to move caret just before the replaced text + if (gReplaceDialog.searchBackwards.checked && newRange) { + gEditor.selection.removeAllRanges(); + gEditor.selection.addRange(newRange); + } + + return true; +} + +function onReplaceAll() { + if (!gEditor) { + return; + } + + var findStr = gReplaceDialog.findInput.value; + var repStr = gReplaceDialog.replaceInput.value; + + // Transfer dialog contents to the find service. + saveFindData(); + + var finder = Cc["@mozilla.org/embedcomp/rangefind;1"] + .createInstance() + .QueryInterface(Ci.nsIFind); + + finder.caseSensitive = gReplaceDialog.caseSensitive.checked; + finder.findBackwards = gReplaceDialog.searchBackwards.checked; + + // We want the whole operation to be undoable in one swell foop, + // so start a transaction: + gEditor.beginTransaction(); + + // and to make sure we close the transaction, guard against exceptions: + try { + // Make a range containing the current selection, + // so we don't go past it when we wrap. + var selection = gEditor.selection; + var selecRange; + if (selection.rangeCount > 0) { + selecRange = selection.getRangeAt(0); + } + var origRange = selecRange.cloneRange(); + + // We'll need a range for the whole document: + var wholeDocRange = gEditor.document.createRange(); + var rootNode = gEditor.rootElement; + wholeDocRange.selectNodeContents(rootNode); + + // And start and end points: + var endPt = gEditor.document.createRange(); + + if (gReplaceDialog.searchBackwards.checked) { + endPt.setStart(wholeDocRange.startContainer, wholeDocRange.startOffset); + endPt.setEnd(wholeDocRange.startContainer, wholeDocRange.startOffset); + } else { + endPt.setStart(wholeDocRange.endContainer, wholeDocRange.endOffset); + endPt.setEnd(wholeDocRange.endContainer, wholeDocRange.endOffset); + } + + // Find and replace from here to end (start) of document: + var foundRange; + var searchRange = wholeDocRange.cloneRange(); + while ( + (foundRange = finder.Find(findStr, searchRange, selecRange, endPt)) != + null + ) { + gEditor.selection.removeAllRanges(); + gEditor.selection.addRange(foundRange); + + // The editor will leave the caret at the end of the replaced text. + // For reverse finds, we need it at the beginning, + // so save the next position now. + if (gReplaceDialog.searchBackwards.checked) { + selecRange = foundRange.cloneRange(); + selecRange.setEnd(selecRange.startContainer, selecRange.startOffset); + } + + // nsPlaintextEditor::InsertText fails if the string is empty, + // so make that a special case: + if (repStr == "") { + gEditor.deleteSelection(gEditor.eNone, gEditor.eStrip); + } else { + gEditor.insertText(repStr); + } + + // If we're going forward, we didn't save selecRange before, so do it now: + if (!gReplaceDialog.searchBackwards.checked) { + selection = gEditor.selection; + if (selection.rangeCount <= 0) { + gEditor.endTransaction(); + return; + } + selecRange = selection.getRangeAt(0).cloneRange(); + } + } + + // If no wrapping, then we're done + if (!gReplaceDialog.wrap.checked) { + gEditor.endTransaction(); + return; + } + + // If wrapping, find from start/end of document back to start point. + if (gReplaceDialog.searchBackwards.checked) { + // Collapse origRange to end + origRange.setStart(origRange.endContainer, origRange.endOffset); + // Set current position to document end + selecRange.setEnd(wholeDocRange.endContainer, wholeDocRange.endOffset); + selecRange.setStart(wholeDocRange.endContainer, wholeDocRange.endOffset); + } else { + // Collapse origRange to start + origRange.setEnd(origRange.startContainer, origRange.startOffset); + // Set current position to document start + selecRange.setStart( + wholeDocRange.startContainer, + wholeDocRange.startOffset + ); + selecRange.setEnd( + wholeDocRange.startContainer, + wholeDocRange.startOffset + ); + } + + while ( + (foundRange = finder.Find( + findStr, + wholeDocRange, + selecRange, + origRange + )) != null + ) { + gEditor.selection.removeAllRanges(); + gEditor.selection.addRange(foundRange); + + // Save insert point for backward case + if (gReplaceDialog.searchBackwards.checked) { + selecRange = foundRange.cloneRange(); + selecRange.setEnd(selecRange.startContainer, selecRange.startOffset); + } + + // nsPlaintextEditor::InsertText fails if the string is empty, + // so make that a special case: + if (repStr == "") { + gEditor.deleteSelection(gEditor.eNone, gEditor.eStrip); + } else { + gEditor.insertText(repStr); + } + + // Get insert point for forward case + if (!gReplaceDialog.searchBackwards.checked) { + selection = gEditor.selection; + if (selection.rangeCount <= 0) { + gEditor.endTransaction(); + return; + } + selecRange = selection.getRangeAt(0); + } + } + } catch (e) {} + + gEditor.endTransaction(); +} + +function doEnabling() { + var findStr = gReplaceDialog.findInput.value; + gReplaceDialog.enabled = findStr; + gReplaceDialog.findNext.disabled = !findStr; + gReplaceDialog.replace.disabled = !findStr; + gReplaceDialog.replaceAndFind.disabled = !findStr; + gReplaceDialog.replaceAll.disabled = !findStr; +} |