Sub LatterDayFetch() ' Paul Beverley - Version 25.01.25 ' Launches selected text on ChurchofJesusChrist.org alsoCopySubject = False mySite = "https://www.churchofjesuschrist.org/search?facet=scriptures&lang=eng&query=" ' To search not just scriptures, open the below line of code and comment out the previous line with an apostrophe ' The below line removes "facet=scriptures" from the URL query ' mySite = "https://www.churchofjesuschrist.org/search&lang=eng&query=" If Selection.Start = Selection.End Then Selection.Expand wdWord If Len(Selection) < 3 Then Selection.Collapse wdCollapseStart Selection.MoveLeft , 1 Selection.Expand wdWord End If Do While InStr(ChrW(8217) & "' ", Right(Selection.text, 1)) > 0 Selection.MoveEnd , -1 DoEvents Loop Else endNow = Selection.End Selection.MoveLeft wdWord, 1 startNow = Selection.Start Selection.End = endNow Selection.Expand wdWord Do While InStr(ChrW(8217) & "' ", Right(Selection.text, 1)) > 0 Selection.MoveEnd , -1 DoEvents Loop Selection.Start = startNow End If If alsoCopySubject = True Then Selection.Copy mySubject = Trim(Selection) mySubject = """" & mySubject & """" ' Add quotation marks around the selected text mySubject = Replace(mySubject, " ", "+") mySubject = Replace(mySubject, "&", "%26") mySubject = Replace(mySubject, ":", "%3A") mySubject = Replace(mySubject, ChrW(8217), "'") Debug.Print mySite & mySubject ActiveDocument.FollowHyperlink Address:=mySite & mySubject Selection.Collapse wdCollapseEnd End Sub Sub LatterDayFetchURL() ' Liz Kazandzhy - Version 22.03.25 ' Looks up a specific scripture reference on ChurchofJesusChrist.org ' The reference must be selected or bound between parentheses/semicolons Dim selectedText As String selectedText = GetScriptureReference() ' Extracts the reference If selectedText <> "" Then OpenLatterDayURL selectedText ' Processes and opens the URL End Sub Sub LatterDayFetchNextURL() ' Liz Kazandzhy - Version 22.03.25 ' Finds the next scripture reference (searching the next 500 words) and looks it up on ChurchofJesusChrist.org ' If text is selected, it copies that text for easy searching on the web page Dim selectedText As String If Selection.Type = wdSelectionNormal Then Selection.Copy ' ? Copy if text is already selected Else SelectTextBetweenMarkers ' ? Otherwise, auto-select text End If selectedText = GetNextScriptureReference() ' Extracts the reference If selectedText <> "" Then OpenLatterDayURL selectedText ' Processes and opens the URL End Sub Sub LatterDayFetchPrevURL() ' Liz Kazandzhy - Version 22.03.25 ' Finds the previous scripture reference (within the paragraph) and looks it up on ChurchofJesusChrist.org ' If text is selected, it copies that text for easy searching on the web page Dim selectedText As String If Selection.Type = wdSelectionNormal Then Selection.Copy ' ? Copy if text is already selected Else SelectTextBetweenMarkers ' ? Otherwise, auto-select text End If selectedText = GetPrevScriptureReference() ' Extracts the reference If selectedText <> "" Then OpenLatterDayURL selectedText ' Processes and opens the URL End Sub Function GetScriptureReference() As String ' Liz Kazandzhy - Version 22.03.25 ' Finds a scripture reference (selected or near the cursor) and moves the cursor after it. Dim refText As String Dim paraRange As Range Dim cursorPos As Integer Dim leftPos As Integer Dim rightPos As Integer Dim paraText As String ' Initialize return value refText = "" ' Use selected text if something is selected If Selection.Type = wdSelectionNormal Then refText = Trim(Selection.text) Selection.Collapse wdCollapseEnd ' Move cursor to the end of selection Else ' No text selected, so find reference based on cursor position Set paraRange = Selection.Paragraphs(1).Range paraText = paraRange.text cursorPos = Selection.Start - paraRange.Start ' Get cursor position in paragraph ' Find the left boundary (look for ( or ;) leftPos = cursorPos Do While leftPos > 0 If Mid(paraText, leftPos, 1) = "(" Or Mid(paraText, leftPos, 1) = ";" Then Exit Do leftPos = leftPos - 1 Loop ' Find the right boundary (look for ; or )) rightPos = cursorPos Do While rightPos <= Len(paraText) If Mid(paraText, rightPos, 1) = ";" Or Mid(paraText, rightPos, 1) = ")" Then Exit Do rightPos = rightPos + 1 Loop ' Extract the reference, trimming extra characters refText = Trim(Mid(paraText, leftPos + 1, rightPos - leftPos - 1)) ' Remove "see " if present If LCase(Left(refText, 4)) = "see " Then refText = Trim(Mid(refText, 5)) End If ' Move cursor to the end of the reference Selection.Start = paraRange.Start + rightPos - 1 Selection.End = Selection.Start End If ' Debugging output Debug.Print "Extracted Reference: [" & refText & "]" ' Return extracted reference (always as a string) GetScriptureReference = refText End Function Function GetNextScriptureReference() As String ' Liz Kazandzhy - Version 22.03.25 ' Called by LatterDayFetchNextURL ' Finds the next scripture reference after the cursor, searching up to 500 words ahead, and moves the cursor to the end of the reference Dim refText As String Dim searchRange As Range Dim searchText As String Dim cursorPos As Long Dim regex As Object Dim matches As Object Dim match As Object Dim matchPos As Long Dim matchEnd As Long Dim newCursorPos As Long ' Get regex pattern for scripture references Set regex = GetScriptureRegex() ' Search up to 500 words ahead Set searchRange = Selection.Range searchRange.End = searchRange.Start ' Start at cursor position searchRange.MoveEnd Unit:=wdWord, count:=500 ' Expand range by 500 words ' Get text from expanded range searchText = searchRange.text cursorPos = 0 ' Always start from the beginning of the expanded range ' Execute regex on expanded text Set matches = regex.Execute(searchText) ' Look for the first match For Each match In matches matchPos = match.FirstIndex matchEnd = matchPos + Len(match.Value) If matchPos >= cursorPos Then refText = Trim(match.Value) ' Get the matched reference ' ? Prevent overflow by using "Long" for calculations newCursorPos = CLng(searchRange.Start) + CLng(matchEnd) ' ? Ensure cursor does NOT exceed document length If newCursorPos > ActiveDocument.Content.End Then newCursorPos = ActiveDocument.Content.End End If ' Move the cursor to the end of the found reference Selection.Start = newCursorPos Selection.End = newCursorPos GetNextScriptureReference = refText ' Return found reference Exit Function End If Next match ' ? No match found MsgBox "No scripture references found in the next 500 words.", vbExclamation, "Error" GetNextScriptureReference = "" End Function Function GetPrevScriptureReference() As String ' Liz Kazandzhy - Version 22.03.25 ' Called by LatterDayFetchPrevURL ' Finds the previous scripture reference before the cursor position in the paragraph Dim refText As String Dim paraRange As Range Dim paraText As String Dim cursorPos As Integer Dim regex As Object Dim matches As Object Dim match As Object Dim matchPos As Integer Dim lastMatch As Object Dim lastMatchPos As Integer ' Get the text of the current paragraph Set paraRange = Selection.Paragraphs(1).Range paraText = paraRange.text cursorPos = Selection.Start - paraRange.Start ' Get cursor position in paragraph ' Get the regex from the list of scripture books Set regex = GetScriptureRegex() ' Execute regex on paragraph text Set matches = regex.Execute(paraText) ' Look for the last match that occurs **before** the cursor position lastMatchPos = -1 For Each match In matches matchPos = match.FirstIndex If matchPos < cursorPos Then ' Keep track of the last valid match before cursor Set lastMatch = match lastMatchPos = matchPos Else Exit For ' Stop once we find a match *after* the cursor End If Next ' If a valid match is found before the cursor, return it If lastMatchPos <> -1 Then refText = Trim(lastMatch.Value) ' Get the matched reference ' Move the cursor to the **start** of the found reference Selection.Start = paraRange.Start + lastMatchPos Selection.End = Selection.Start GetPrevScriptureReference = refText ' Return found reference Exit Function End If ' If no match is found before the cursor, return an error message MsgBox "No scripture reference found before this point in the paragraph.", vbExclamation, "Error" GetPrevScriptureReference = "" End Function Function GenerateScriptureURL(selectedText As String) As String ' Liz Kazandzhy - Version 22.03.25 ' Converts a scripture reference into a URL and returns it Dim parts() As String Dim book As String, chapterAndVerse As String, chapter As String, verse As String Dim bookUrlFragment As String Dim baseUrl As String Dim constructedUrl As String Dim bookMapping As Object Dim i As Integer Dim startVerse As String, endVerse As String ' Initialize base URL baseUrl = "https://www.churchofjesuschrist.org/study/scriptures/" ' Get book mapping Set bookMapping = GetBookMapping() ' Remove any parentheses from the extracted reference selectedText = Trim(Replace(Replace(selectedText, "(", ""), ")", "")) ' Now split the cleaned-up reference parts = Split(selectedText, " ") ' Reconstruct book name book = "" For i = 0 To UBound(parts) - 1 book = book & parts(i) & " " Next i book = Trim(book) ' Find the book URL fragment If Not bookMapping.Exists(book) Then Exit Function bookUrlFragment = bookMapping(book) ' Extract chapter and verse(s) If UBound(parts) < 1 Then Exit Function chapterAndVerse = parts(UBound(parts)) ' Construct URL (check if chapter-only or has verses) If InStr(chapterAndVerse, ":") = 0 Then constructedUrl = baseUrl & bookUrlFragment & chapterAndVerse & "?lang=eng" Else chapter = Split(chapterAndVerse, ":")(0) verse = Split(chapterAndVerse, ":")(1) ' Handle verse ranges If InStr(verse, "-") > 0 Or InStr(verse, "–") > 0 Then verse = Replace(verse, "–", "-") Dim verseParts() As String verseParts = Split(verse, "-") startVerse = Trim(verseParts(0)) endVerse = Trim(verseParts(1)) constructedUrl = baseUrl & bookUrlFragment & chapter & "?lang=eng&id=p" & startVerse & "-p" & endVerse & "#p" & startVerse Else constructedUrl = baseUrl & bookUrlFragment & chapter & "?lang=eng&id=p" & verse & "#p" & verse End If End If ' Return the constructed URL GenerateScriptureURL = constructedUrl End Function Function OpenLatterDayURL(selectedText As String) ' Liz Kazandzhy - Version 22.03.25 ' Calls GenerateScriptureURL and opens the scripture reference in the browser Dim url As String url = GenerateScriptureURL(selectedText) ' Open the URL if valid If url <> "" Then ActiveDocument.FollowHyperlink url End Function Function GetBookMapping() As Object ' Liz Kazandzhy - Version 22.03.25 ' Returns a dictionary with scripture book mappings Dim bookMapping As Object Set bookMapping = CreateObject("Scripting.Dictionary") ' Old Testament bookMapping.Add "Genesis", "ot/gen/" bookMapping.Add "Exodus", "ot/ex/" bookMapping.Add "Leviticus", "ot/lev/" bookMapping.Add "Numbers", "ot/num/" bookMapping.Add "Deuteronomy", "ot/deut/" bookMapping.Add "Joshua", "ot/josh/" bookMapping.Add "Judges", "ot/judg/" bookMapping.Add "Ruth", "ot/ruth/" bookMapping.Add "1 Samuel", "ot/1-sam/" bookMapping.Add "2 Samuel", "ot/2-sam/" bookMapping.Add "1 Kings", "ot/1-kgs/" bookMapping.Add "2 Kings", "ot/2-kgs/" bookMapping.Add "1 Chronicles", "ot/1-chr/" bookMapping.Add "2 Chronicles", "ot/2-chr/" bookMapping.Add "Ezra", "ot/ezra/" bookMapping.Add "Nehemiah", "ot/neh/" bookMapping.Add "Esther", "ot/esth/" bookMapping.Add "Job", "ot/job/" bookMapping.Add "Psalms", "ot/ps/" bookMapping.Add "Psalm", "ot/ps/" bookMapping.Add "Proverbs", "ot/prov/" bookMapping.Add "Ecclesiastes", "ot/eccl/" bookMapping.Add "Song of Solomon", "ot/song/" bookMapping.Add "Isaiah", "ot/isa/" bookMapping.Add "Jeremiah", "ot/jer/" bookMapping.Add "Lamentations", "ot/lam/" bookMapping.Add "Ezekiel", "ot/ezek/" bookMapping.Add "Daniel", "ot/dan/" bookMapping.Add "Hosea", "ot/hosea/" bookMapping.Add "Joel", "ot/joel/" bookMapping.Add "Amos", "ot/amos/" bookMapping.Add "Obadiah", "ot/obad/" bookMapping.Add "Jonah", "ot/jonah/" bookMapping.Add "Micah", "ot/micah/" bookMapping.Add "Nahum", "ot/nahum/" bookMapping.Add "Habakkuk", "ot/hab/" bookMapping.Add "Zephaniah", "ot/zeph/" bookMapping.Add "Haggai", "ot/hag/" bookMapping.Add "Zechariah", "ot/zech/" bookMapping.Add "Malachi", "ot/mal/" ' New Testament bookMapping.Add "Matthew", "nt/matt/" bookMapping.Add "Mark", "nt/mark/" bookMapping.Add "Luke", "nt/luke/" bookMapping.Add "John", "nt/john/" bookMapping.Add "Acts", "nt/acts/" bookMapping.Add "Romans", "nt/rom/" bookMapping.Add "1 Corinthians", "nt/1-cor/" bookMapping.Add "2 Corinthians", "nt/2-cor/" bookMapping.Add "Galatians", "nt/gal/" bookMapping.Add "Ephesians", "nt/eph/" bookMapping.Add "Philippians", "nt/philip/" bookMapping.Add "Colossians", "nt/col/" bookMapping.Add "1 Thessalonians", "nt/1-thes/" bookMapping.Add "2 Thessalonians", "nt/2-thes/" bookMapping.Add "1 Timothy", "nt/1-tim/" bookMapping.Add "2 Timothy", "nt/2-tim/" bookMapping.Add "Titus", "nt/titus/" bookMapping.Add "Philemon", "nt/philem/" bookMapping.Add "Hebrews", "nt/heb/" bookMapping.Add "James", "nt/james/" bookMapping.Add "1 Peter", "nt/1-pet/" bookMapping.Add "2 Peter", "nt/2-pet/" bookMapping.Add "1 John", "nt/1-jn/" bookMapping.Add "2 John", "nt/2-jn/" bookMapping.Add "3 John", "nt/3-jn/" bookMapping.Add "Jude", "nt/jude/" bookMapping.Add "Revelation", "nt/rev/" ' Book of Mormon bookMapping.Add "1 Nephi", "bofm/1-ne/" bookMapping.Add "2 Nephi", "bofm/2-ne/" bookMapping.Add "Jacob", "bofm/jacob/" bookMapping.Add "Enos", "bofm/enos/" bookMapping.Add "Jarom", "bofm/jarom/" bookMapping.Add "Omni", "bofm/omni/" bookMapping.Add "Words of Mormon", "bofm/w-of-m/" bookMapping.Add "Mosiah", "bofm/mosiah/" bookMapping.Add "Alma", "bofm/alma/" bookMapping.Add "Helaman", "bofm/hel/" bookMapping.Add "3 Nephi", "bofm/3-ne/" bookMapping.Add "4 Nephi", "bofm/4-ne/" bookMapping.Add "Mormon", "bofm/morm/" bookMapping.Add "Ether", "bofm/ether/" bookMapping.Add "Moroni", "bofm/moro/" ' Doctrine and Covenants / Pearl of Great Price bookMapping.Add "Doctrine and Covenants", "dc-testament/dc/" bookMapping.Add "D&C", "dc-testament/dc/" bookMapping.Add "Moses", "pgp/moses/" bookMapping.Add "Abraham", "pgp/abr/" bookMapping.Add "Joseph Smith—Matthew", "pgp/js-m/" bookMapping.Add "Joseph Smith—History", "pgp/js-h/" bookMapping.Add "Articles of Faith", "pgp/a-of-f/" ' Non-breaking space versions bookMapping.Add "1 Samuel", "ot/1-sam/" bookMapping.Add "2 Samuel", "ot/2-sam/" bookMapping.Add "1 Kings", "ot/1-kgs/" bookMapping.Add "2 Kings", "ot/2-kgs/" bookMapping.Add "1 Chronicles", "ot/1-chr/" bookMapping.Add "2 Chronicles", "ot/2-chr/" bookMapping.Add "1 Corinthians", "nt/1-cor/" bookMapping.Add "2 Corinthians", "nt/2-cor/" bookMapping.Add "1 Thessalonians", "nt/1-thes/" bookMapping.Add "2 Thessalonians", "nt/2-thes/" bookMapping.Add "1 Timothy", "nt/1-tim/" bookMapping.Add "2 Timothy", "nt/2-tim/" bookMapping.Add "1 Peter", "nt/1-pet/" bookMapping.Add "2 Peter", "nt/2-pet/" bookMapping.Add "1 John", "nt/1-jn/" bookMapping.Add "2 John", "nt/2-jn/" bookMapping.Add "3 John", "nt/3-jn/" bookMapping.Add "1 Nephi", "bofm/1-ne/" bookMapping.Add "2 Nephi", "bofm/2-ne/" bookMapping.Add "3 Nephi", "bofm/3-ne/" bookMapping.Add "4 Nephi", "bofm/4-ne/" ' Return dictionary Set GetBookMapping = bookMapping End Function Function GetScriptureRegex() ' Liz Kazandzhy - Version 22.03.25 ' Returns a regex to be used to search for scripture references ' Get book mapping Set bookMapping = GetBookMapping() ' Convert book names into regex format (separated by `|` for OR matching) Dim bookList As String Dim book As Variant For Each book In bookMapping.Keys If bookList <> "" Then bookList = bookList & "|" bookList = bookList & Replace(book, " ", "\s") ' Escape spaces for regex Next book ' Define regex pattern for scripture references Set regex = CreateObject("VBScript.RegExp") regex.Pattern = "(" & bookList & ")\s(\d+:\d+[-–]?\d*)" ' Match book + chapter:verse (and ranges) regex.Global = True ' Return the regex object Set GetScriptureRegex = regex End Function Function FetchScriptureTextSelenium(selectedReference) As String ' Liz Kazandzhy - Version 22.03.25 ' Fetches the scripture text in the background to compare against the quoted text Dim driver As New Selenium.ChromeDriver Dim url As String Dim scriptureText As String Dim startTime As Double Dim loadTime As Double Dim paragraphIDs As String Dim paragraphParts() As String Dim combinedText As String Dim i As Integer If selectedReference = "" Or IsNull(selectedReference) Then Debug.Print "!! No valid scripture reference provided." FetchScriptureTextSelenium = "" Exit Function End If If selectedReference = "" Then FetchScriptureTextSelenium = "" Exit Function End If ' Generate the scripture URL url = GenerateScriptureURL(CStr(selectedReference)) If url = "" Then MsgBox "Invalid scripture reference. Could not generate URL.", vbExclamation, "Error" FetchScriptureTextSelenium = "" Exit Function End If ' OPTIONAL: Open the URL for manual checking ' OpenLatterDayURL (selectedReference) ' Extract paragraph IDs from the URL (i.e., p1 or p1-p5) paragraphIDs = Split(url, "id=")(1) ' Extracts "p1" or "p1-p5" paragraphIDs = Split(paragraphIDs, "#")(0) ' Removes fragment identifier ' Start Chrome in headless mode driver.SetCapability "chromedriver", "C:\Users\lizmo\AppData\Local\SeleniumBasic\chromedriver.exe" driver.AddArgument "--headless" driver.AddArgument "--disable-gpu" driver.AddArgument "--log-level=3" driver.AddArgument "--no-sandbox" driver.Start "chrome" driver.Get url ' Start the timer startTime = Timer ' Wait dynamically for scripture content to load (up to 10 seconds) Do On Error Resume Next driver.FindElementByCss("#p1").text ' Check if at least one paragraph exists On Error GoTo 0 If Err.Number = 0 Then Exit Do ' Exit if content is found If Timer - startTime > 2 Then ' Wait up to 2 seconds Debug.Print "Scripture content did not load in time." driver.Quit FetchScriptureTextSelenium = "" ' Return empty string if failed Exit Function End If DoEvents Loop ' Check if it’s a single verse or a range If InStr(paragraphIDs, "-") > 0 Then ' Verse range detected paragraphParts = Split(paragraphIDs, "-") Dim startVerse As Integer, endVerse As Integer startVerse = CInt(Mid(paragraphParts(0), 2)) ' Extract number from p1, p2, etc. endVerse = CInt(Mid(paragraphParts(1), 2)) ' Extract last verse ' Loop through and concatenate all paragraphs combinedText = "" For i = startVerse To endVerse On Error Resume Next scriptureText = driver.FindElementByCss("#p" & i).text On Error GoTo 0 If scriptureText <> "" Then combinedText = combinedText & scriptureText & " " End If Next i scriptureText = FullTrim(combinedText) ' Trim final concatenated text Else ' Single verse detected, fetch only one paragraph On Error Resume Next scriptureText = driver.FindElementByCss("#" & paragraphIDs).text On Error GoTo 0 End If ' Calculate and display load time loadTime = Timer - startTime ' Close the browser driver.Quit ' Replace non-breaking spaces with regular spaces scriptureText = Replace(scriptureText, Chr(160), Chr(32)) ' Return the fetched scripture text FetchScriptureTextSelenium = scriptureText End Function Sub CheckVerseWithRefAfter() ' Liz Kazandzhy - Version 22.03.25 ' Compares the text (either selected or between quotation marks) with the official scripture text ' Used when the scripture reference is AFTER the quoted verse ' Highlights in green if it's a perfect match, pink if not CheckVerseAccuracy (True) End Sub Sub CheckVerseWithRefBefore() ' Liz Kazandzhy - Version 22.03.25 ' Compares the text (either selected or between quotation marks) with the official scripture text ' Used when the scripture reference is BEFORE the quoted verse ' Highlights in green if it's a perfect match, pink if not CheckVerseAccuracy (False) End Sub Function CheckVerseAccuracy(isRefAfter) ' Liz Kazandzhy - Version 22.03.25 ' Compares the text (either selected or between quotation marks) with the official scripture text Dim scriptureText As String Dim selectedText As String Dim selectedReference As String Dim sentences As Variant Dim i As Integer Dim allSentencesMatch As Boolean Dim selectionEndPos As Long allSentencesMatch = True ' Default to true ' Check if there is selected text; if so, use it instead of auto-selecting If Selection.Type = wdSelectionNormal Then selectedText = FullTrim(Selection.text) selectionEndPos = Selection.End ' <== Store the endpoint here Else ' Automatically select text from cursor position selectedText = SelectTextBetweenMarkers() selectionEndPos = Selection.End ' <== Store the endpoint here End If ' Exit if no valid text is found If selectedText = "" Then Debug.Print "No text found. Exiting." Exit Function End If ' Normalize spaces in selected text selectedText = Replace(selectedText, Chr(160), Chr(32)) ' Trim the first character to account for capitalization being different selectedText = Mid(selectedText, 2) ' Fetch the scripture reference If isRefAfter Then selectedReference = GetNextScriptureReference() Else selectedReference = GetPrevScriptureReference() End If ' Restore the cursor to the end of selectedText Selection.Start = selectionEndPos Selection.End = selectionEndPos ' Ensure the reference is valid before proceeding If selectedReference = "" Or IsNull(selectedReference) Then Debug.Print "!! No scripture reference found. Exiting." Exit Function End If ' Fetch scripture text scriptureText = FetchScriptureTextSelenium(CStr(selectedReference)) ' Ensure scripture text is valid If scriptureText = "" Then Debug.Print "Failed to fetch scripture text. Exiting." Exit Function End If ' Split text into sentences sentences = SplitTextIntoSentences(selectedText) ' Ensure we have valid sentences before proceeding If Not IsArray(sentences) Then Exit Function End If ' Loop through each sentence and check if it's in the scripture text For i = LBound(sentences) To UBound(sentences) If Not CheckSentenceMatch(sentences(i), scriptureText) Then allSentencesMatch = False ' At least one error was found End If Next i ' OPTIONAL: If at least one sentence is incorrect, open DiffChecker ' If Not allSentencesMatch Then ' Shell "cmd /c start https://www.diffchecker.com/", vbNormalFocus ' OpenLatterDayURL selectedReference ' End If End Function Function SelectTextBetweenMarkers() As String ' Liz Kazandzhy - Version 22.03.25 ' Selects text between quotes (excluding them) if cursor is inside them ' If no quotes exist, selects from the beginning of the paragraph until "(" ' Automatically copies the selected text Dim cursorRange As Range Dim paraRange As Range Dim quoteChars As String Dim foundOpeningQuote As Boolean Dim foundClosingQuote As Boolean Dim foundParen As Boolean Dim selectedText As String Dim wordCount As Integer ' Define possible quote characters (straight and curly quotes) quoteChars = """“”" ' Set the cursor position as a range Set cursorRange = Selection.Range cursorRange.Collapse wdCollapseStart ' Ensure we're at the cursor position ' Define the current paragraph range Set paraRange = Selection.Paragraphs(1).Range ' If the cursor is at the beginning of the paragraph, force selection from there If Selection.Start = paraRange.Start Then cursorRange.Start = paraRange.Start End If ' Try finding the opening quote by searching backward within the paragraph Do While cursorRange.Start > paraRange.Start cursorRange.MoveStart wdCharacter, -1 If InStr(quoteChars, cursorRange.Characters.First.text) > 0 Then foundOpeningQuote = True cursorRange.MoveStart wdCharacter, 1 ' Move past the opening quote Exit Do End If Loop ' If an opening quote was found, search forward for a closing quote If foundOpeningQuote Then cursorRange.End = Selection.Start ' Reset to cursor start before moving forward Do While cursorRange.End < paraRange.End cursorRange.MoveEnd wdCharacter, 1 If InStr(quoteChars, cursorRange.Characters.Last.text) > 0 Then foundClosingQuote = True cursorRange.MoveEnd wdCharacter, -1 ' Exclude the closing quote Exit Do End If Loop End If ' If no quotes were found, search for the next "(" marker If Not foundOpeningQuote Or Not foundClosingQuote Then ' Start **from the beginning of the paragraph** cursorRange.Start = paraRange.Start ' Expand forward until next "(" or 500 words max wordCount = 0 Do While cursorRange.End < ActiveDocument.Content.End And wordCount < 500 cursorRange.MoveEnd wdWord, 1 wordCount = wordCount + 1 If cursorRange.Characters.Last.text = "(" Then foundParen = True cursorRange.MoveEnd wdCharacter, -1 ' Exclude the parenthesis Exit Do End If Loop End If ' If no valid selection was made, exit If Not foundOpeningQuote And Not foundClosingQuote And Not foundParen Then Debug.Print "!! No valid selection found, exiting..." Exit Function End If ' Apply the selection Selection.Start = cursorRange.Start Selection.End = cursorRange.End ' Trim any trailing white space selectedText = FullTrim(Selection.text) ' Automatically copy the selection to clipboard Selection.Copy ' Return selected text SelectTextBetweenMarkers = selectedText End Function ' ============================== ' STEP 1: FUNCTION TO CHECK ONE SENTENCE ' ============================== Function CheckSentenceMatch(ByVal sentence As String, ByVal scriptureText As String) As Boolean ' Liz Kazandzhy - Version 22.03.25 Dim sentenceMatchesExactly As Boolean sentenceMatchesExactly = True ' Default to true ' Trim sentence and remove extra spaces or hidden characters sentence = FullTrim(sentence) ' If the sentence is found in scripture text, highlight green, otherwise pink If InStrB(1, scriptureText, sentence) > 0 Then HighlightText sentence, wdBrightGreen ' Green for found Else HighlightText sentence, wdPink ' Pink for not found sentenceMatchesExactly = False ' Mark as incorrect End If ' Return whether the sentence matches exactly CheckSentenceMatch = sentenceMatchesExactly End Function ' ============================== ' STEP 2: FUNCTION TO SPLIT TEXT INTO SENTENCES ' ============================== Function SplitTextIntoSentences(ByVal text As String) As Variant ' Liz Kazandzhy - Version 22.03.25 Dim delimiters As String delimiters = ".!?;:[]" ' Sentence-ending punctuation Dim tempText As String Dim i As Integer Dim sentences() As String ' Ensure text is not empty If Len(text) = 0 Then SplitTextIntoSentences = Array("") Exit Function End If ' Replace punctuation with a special marker tempText = text For i = 1 To Len(delimiters) tempText = Replace(tempText, Mid(delimiters, i, 1), "|") Next i ' Replace Word ellipsis character (Unicode U+2026) with "|" tempText = Replace(tempText, ChrW(&H2026), "|") ' If no delimiters are found, return text as single-item array If InStr(tempText, "|") = 0 Then SplitTextIntoSentences = Array(FullTrim(text)) Exit Function End If ' Split text using "|" sentences = Split(tempText, "|") ' Trim each sentence using FullTrim to remove all unwanted characters For i = LBound(sentences) To UBound(sentences) sentences(i) = FullTrim(sentences(i)) Next i SplitTextIntoSentences = sentences End Function Function FullTrim(ByVal text As String) As String ' Liz Kazandzhy - Version 22.03.25 ' Removes all leading and trailing spaces, tabs, newlines, and non-breaking spaces text = Replace(text, Chr(9), "") ' Remove tabs text = Replace(text, Chr(160), " ") ' Convert non-breaking spaces to normal spaces text = Replace(text, vbCr, "") ' Remove carriage returns text = Replace(text, vbLf, "") ' Remove line feeds FullTrim = Trim(text) ' Finally, remove leading/trailing spaces End Function ' ============================== ' STEP 3: FUNCTION TO HIGHLIGHT TEXT ' ============================== Function HighlightText(ByVal textToFind As String, ByVal highlightColor As Long) ' Liz Kazandzhy - Version 22.03.25 Dim rng As Range Dim textChunks As Variant Dim i As Integer Dim startPos As Long ' Split long text into smaller chunks textChunks = SplitLongText(textToFind) ' Store the original selection position startPos = Selection.Start ' Search and highlight each chunk separately (looping backward) For i = UBound(textChunks) To LBound(textChunks) Step -1 ' Reset range to original position before each search Set rng = ActiveDocument.Range(startPos, Selection.End) With rng.Find .ClearFormatting .text = textChunks(i) .Forward = False ' ? Search backward .Wrap = wdFindStop ' ? Stop at beginning of document .MatchWholeWord = True ' Look for exact matches .Execute End With If rng.Find.found Then rng.HighlightColorIndex = highlightColor ' Highlight green or pink End If Next i End Function ' ============================== ' FUNCTION: Splitting Long Text ' ============================== Function SplitLongText(ByVal textToFind As String, Optional chunkSize As Integer = 250) As Variant ' Liz Kazandzhy - Version 22.03.25 Dim numChunks As Integer, i As Integer Dim parts() As String ' Calculate number of chunks in the selection numChunks = (Len(textToFind) \ chunkSize) If (Len(textToFind) Mod chunkSize) > 0 Then numChunks = numChunks + 1 ' Prevent ReDim error If numChunks < 1 Then numChunks = 1 ReDim parts(numChunks - 1) ' Split text into chunks For i = 0 To numChunks - 1 parts(i) = Mid(textToFind, (i * chunkSize) + 1, chunkSize) Next i SplitLongText = parts End Function