Files
Misspelled/Misspelled.lua
T
Mr_Rosh 1bb5cf3b13 Improve compatibility with legacy WoW clients
Fall back to older secure-call APIs, guard WIM registration when the addon is unavailable, and remove the obsolete .gitattributes file.
2026-10-27 12:15:28 +02:00

1816 lines
68 KiB
Lua

--[[--------------------------------------------------------------------------
Developed by Nathan Pieper - nrpieper (@) gmail (dot) com
Misspelled is an interactive chat text spell checker for World of Warcraft
This code freely distributed for your use in any GPL compliant project.
Portions of this code are covered by the Gnu Public License (GPL)
Dictionaries use the OpenOffice/HunSpell/ASpell dictionary format
(http://wiki.services.openoffice.org/wiki/Dictionaries)
Phonetic cache information created via the NetSpell dictionary util.
References & Credits:
Paul Welter & NetSpell: http://www.loresoft.com/projects/netspell
Metaphone Algorithm http://aspell.net/metaphone/
Edit Distance Algorithm http://en.wikipedia.org/wiki/Levenshtein_distance
--------------------------------------------------------------------------]]--
--[[
Challenges:
Issue #1
(http://www.wowwiki.com/ItemLink)
Color tags can be destroyed or mangled as you perform edit near the start
and end of the color tag.
If you press delete with the cursor positioned, just before, a color tag,
the start of the tag (|cffffffff) and the next character are removed. The
closing color tag (|r) is left at the end.
If you press backspace with the cursor positioned just past the (|r) end
of a color tag, the (|r) is removed along with the character just before it.
If you press delete with the cursor positioned, just before the last char,
in a color tagged string, the character and the closing (|r) are both removed.
If you press backspace with the cursor positioned, just after the first char,
in a color tagged string, the first char and the beginning color tag are both removed.
In the case of colored item links, Wow just deletes the entire item, if you
hit a delete or backspace.
Solution:
First parse the line to flag any valid item links or textures so they don't get
destroyed by the next step.
Then if there are any remaining start or end color tag, remove them.
Issue #2
The Chat Edit box's edit cursor position is relative to characters that don't display in the
chat edit text, i.e. item link tags, color tags, and texture tags.
When we add, or remove, a color tag, to highlight a misspelled word, we should adjust the
cursor position so it acts natural.
Note: An OnCursorChanged event doesn't exist or doesn't fire for the ChatEditBox.
Solution:
Methods that insert or delete text from the ChatEditBox need to properly adjust the cursor
position as needed when, visible or hidden, characters are inserted or deleted to the left of the cursor.
Two techniques could be used.
#1: Track the number of, printable and non-printable characters inserted or deleted
to the left of the cursor, and adjust the cursor position to compensate.
#2: Insert a tracking char byte (example: \1), at the cursor position. After making
inserts and deletes, find the tracking char byte, remove it and reposition the cursor
at its location.
I have implemented technique #1.
Issue #3 WIM Integration
Wim allows multiple chat windows at once. So we need to track WordLocations per editbox.
Wim breaks long chat messages into multiple messages.
Wim has a EditBox right click handler to insert emoticons and previous chat messages.
WIM sends chat messages using ChatThrottleLib:SendChatMessage. This can hot SendChatMessage before Misspelled and bypasses the step that cleans the highlighting.
(10/10/2009) Updated hooks, from WIM, will be provided via WIM.RegisterPreSendFilterText(func()), to remove the need to manually hook ChatThrottleLib
User Dictionary Editor Added
(12/6/2009) - Used AceGUI to add the ability to remove words from the user dictionary.
(1/12/2010) - Added enGB UK English dictionary.
(5/12/2010) - Added support for PTR Patch 3.5. Multiple ChatEditBoxes get created. Hook into the activate routine.
(5/23/2010) - Fixed enGB dictionary load issue.
(6/24/2010) - Added itIT Italian dictionary
(7/22/2010) - The Addon Gryphonheart Items (GHI) begins it's item color tags with a (|C) capitol C character. I added that possibility to the chat text parsing.
(9/14/2010) - Client 4 has some issues with trying to set the owner of the popmenu items. Looks like it's not needed.
(9/7/2019) - Wow Classic: When starting you can't have friends and the global function: GetNumFriends() is nil. Detect and skip to eliminate the error.
(4/30/2025) - Changes added to RemoveHighlighting to parse new Item Quality # colors and Global Colors UI escape sequences.
(12/18/2025) - Wow Retail 12.2.7 changes added to hook chat frames.
--]]--
local _G = _G
Misspelled = LibStub("AceAddon-3.0"):NewAddon("Misspelled", "AceEvent-3.0", "AceHook-3.0")
local Misspelled = _G.Misspelled
Misspelled.Version = GetAddOnMetadata("Misspelled", "Version")
local L = LibStub("AceLocale-3.0"):GetLocale("Misspelled", true)
local table_insert = table.insert
local string_byte = string.byte
local string_find = string.find
local string_format = string.format
local string_gsub = string.gsub
local string_len = string.len
local string_lower = string.lower
local string_match = string.match
local string_rep = string.rep
local string_sub = string.sub
local string_upper = string.upper
local tostring = tostring
local pairs = pairs
local ipairs = ipairs
--local SPELLED_WRONG_HIGHLIGHT = "|cffff91c8" --The color misspelled words will get changed into.
--local SPELLED_WRONG_HIGHLIGHT = "|cffdea1d3" --The color misspelled words will get changed into.
--local SPELLED_WRONG_HIGHLIGHT = "|cfffefe80" --The color misspelled words will get changed into.
local SPELLED_WRONG_HIGHLIGHT_HEX_COLOR_CODE = "ff7dc6fb" --The color misspelled words will get changed into. (Medium-Cyan-ish)
local SPELLED_WRONG_HIGHLIGHT = "|c"..SPELLED_WRONG_HIGHLIGHT_HEX_COLOR_CODE --The color misspelled words will get changed into. (Medium-Cyan-ish)
local WordCache = {} --Stores a cache of every word checked, along with the suggestions for words misspelled
local WordCacheCount = 0 --Counter used to track when we should clean the WordCache table to save memory
local WordCacheCountMax = 7000 --Number of entries tha can live in the WordCache before we clean the cache
local WordLocations = {} --Lookup table used by multiple functions to determine where each work starts and ends
--There will be a sub-table for each EditBox:GetName() so we can store multiple sets of info at once.
local SkipOnTextChanged = false -- use to avoid OnTextChanged event firing after spell checking highlights chat text.
local RightClickedWord = nil --The current word under the CursorPosition that was right-clicked
local RightClickedWordStartPos --Where that word starts
local RightClickedWordEndPos --and where that word ends
local RightClickedEditBox --and what EditBox was right clicked
local OldLineLength --Tracks the previous length of the ChatEditBox.text
local GuildRosterCalled = false
local MaxColorCodes = 12 --The max amount of color codes we will add to the editbox text.
local Misspelled_Saved_CTL_SendChatMessage
local Misspelled_CTL_hookedversion=0
--Output debug messages to the DevTool addon (https://github.com/brittyazel/DevTool)
function Misspelled:AddToInspector(data, strName)
if DevTool and self.DEBUG then
local valType = type(data)
if valType == "string" then
-- from: https://github.com/Gethe/wow-ui-source/blob/live/Interface/AddOns/Blizzard_SharedXML/Dump.lua
-- %q "quotes" the string and escapes any special characters within it (like double quotes, newlines, etc.)
-- replacing | with || outputs UI escaped sequence strings displaying all color and link details
data = string_gsub(string_format("%q", data), "[|]", "||")
DevTool:AddData(data, "Misspelled: " .. strName)
else
DevTool:AddData(data, "Misspelled: " .. strName)
end
end
end
function Misspelled:OnInitialize()
--Enable to output debug messages created with calls to: AddToInspector(data, strName), to the addon: DevTool
--self.DEBUG = true
if Misspelled_DB == nil then
Misspelled_DB = {}
end
--Check if the user has chosen to override the auto dictionary selection
if Misspelled_DB.LoadDictionary == nil then
Misspelled_DB.LoadDictionary = "enUS"
end
if Misspelled_DB.AutoSelectDictionary == nil then
Misspelled_DB.AutoSelectDictionary = true
end
local dict = "Auto"
if Misspelled_DB.AutoSelectDictionary == true then
dict = "Auto"
else
dict = Misspelled_DB.LoadDictionary or "Auto"
end
--Load the Dictionary
local dictLoaded
if dict == "Auto" then
dictLoaded = WordDict:Init()
else
dictLoaded = WordDict:Init(dict)
end
Misspelled:print("Misspelled: " .. L["Dictionary Loaded"] .." - " .. dictLoaded)
--Load user dict
Misspelled:LoadUserDict()
Misspelled:print("Misspelled: " .. L["User Dictionary Loaded"])
-- Build Interface Options window
self:CreateInterfaceOptions()
--Watch for other chat addons: Wim, to load and then integrate.
Misspelled:RegisterEvent("ADDON_LOADED")
--Guild members and Friends are valid words.
--Wait for the GUILD_ROSTER_UPDATE event to load
--the guild members & friends into the dictionary as valid words.
--Register the GUILD_ROSTER_UPDATE event, so we know when we can load the
--Guild member names into the database
Misspelled:RegisterEvent("GUILD_ROSTER_UPDATE")
--GuildRoster can only be called every so often.
--If another addon triggered it first, we might miss notification.
--Moved to OnTextChanged
--GuildRoster()
--Patch 3.5 has multiple ChatEditBoxes. We need to hook in differently.
--Updated for WoW 11.2.7: ChatEdit_ActivateChat exists but is never called.
--Force the new timer-based approach.
-- Use timer-based approach for 11.2.7+
local self = Misspelled
local timer = CreateFrame("Frame")
local elapsed = 0
timer:SetScript("OnUpdate", function(frame, delta)
elapsed = elapsed + delta
if elapsed < 0.1 then return end
frame:SetScript("OnUpdate", nil)
local n = _G.NUM_CHAT_WINDOWS or 10
for i = 1, n do
local editbox = _G["ChatFrame" .. i .. "EditBox"]
if editbox then
local hooked = self:IsHooked(editbox, "OnTextChanged")
if not hooked then
self:WireUpEditBox(editbox)
end
end
end
end)
if ChatEdit_ActivateChat ~= nil then
Misspelled:SecureHook("ChatEdit_ActivateChat")
elseif ChatFrameEditBox ~= nil then
Misspelled:WireUpEditBox(ChatFrameEditBox)
end
-- hooks for removing any misspelled word highlighting in the text before the chat message is sent
-- The Wow client will disconnect if you attempt to send a color tags in a chat message.
Misspelled:RawHook("SendChatMessage", Misspelled.SendChatMessage, true)
end
function Misspelled:WireUpEditBox(editbox)
Misspelled:SecureHookScript(editbox, "OnEscapePressed", Misspelled.EditBox_OnEscapePressed)
Misspelled:SecureHookScript(editbox, "OnEnterPressed", Misspelled.EditBox_OnEnterPressed)
Misspelled:SecureHookScript(editbox, "OnTextChanged", Misspelled.EditBox_OnTextChanged)
Misspelled:HookScript(editbox, "OnMouseUp", Misspelled.EditBox_OnMouseUp) -- Used to hook mouse right-clicks to show suggestions frame
end
function Misspelled:GUILD_ROSTER_UPDATE()
Misspelled:LoadGuildAndFriendRoster()
end
function Misspelled:ADDON_LOADED(event, addonName)
if event == "ADDON_LOADED" and addonName == "WIM" and WIM then
WIM.RegisterWidgetTrigger("msg_box", "whisper,chat,w2w", "OnEscapePressed", Misspelled.EditBox_OnEscapePressed)
WIM.RegisterWidgetTrigger("msg_box", "whisper,chat,w2w", "OnEnterPressed", Misspelled.EditBox_OnEnterPressed)
WIM.RegisterWidgetTrigger("msg_box", "whisper,chat,w2w", "OnTextChanged", Misspelled.EditBox_OnTextChanged)
--WIM.RegisterWidgetTrigger("msg_box", "whisper,chat,w2w", "OnMouseUp", Misspelled.EditBox_OnMouseUp)
--Before a chat message is sent, remove any highlighting that Misspelled might have added.
--The Wow client will disconnect if you attempt to send a colored chat message.
--If available use the WIM API: Wim.RegisterPreSendFilterText
--WIM sends its chat messages via the API ChatThrottleLib,
--ChatThrottleLib hooks the default SendChatMessage api, many times, before Misspelled can.
--ChatThrottleLib is used in many addons, that potentially load before Misspelled.
--So we have to hook ChatThrottleLib just to be safe.
if(WIM.RegisterPreSendFilterText) then -- avoid error if WIM not up to date.
WIM.RegisterPreSendFilterText(function(text)
return Misspelled:RemoveHighlighting(text)
end)
else
if(ChatThrottleLib and Misspelled_CTL_hookedversion < ChatThrottleLib.version) then
Misspelled_Saved_CTL_SendChatMessage=ChatThrottleLib.SendChatMessage
function ChatThrottleLib:SendChatMessage(prio, prefix, text, ...)
text = Misspelled:RemoveHighlighting(text)
--print("Misspelled Hooked ChatThrottleLib_SendChatMessaged called")
return Misspelled_Saved_CTL_SendChatMessage(ChatThrottleLib, prio, prefix, text, ...)
end
Misspelled_CTL_hookedversion=ChatThrottleLib.version
end
end
end
end
--Patch 3.5 Hook ChatFrame.lua ChatEdit_ActivateChat(editBox)
function Misspelled:ChatEdit_ActivateChat(editBox)
--Make sure this editbox is hooked
--print("EditBox to hook: " .. editBox:GetName())
local hooked, hookHandler = Misspelled:IsHooked(editBox, "OnTextChanged")
if hooked == false then
Misspelled:WireUpEditBox(editBox)
end
self.hooks["ChatEdit_ActivateChat"](editBox)
end
--Before a chat message is sent, remove any highlighting that Misspelled might have added.
--The Wow client will disconnect if you attempt to send Hex code colored text in a chat message.
function Misspelled.SendChatMessage(message, chatType, languageID, target, ...)
local cleanedMessage = Misspelled:RemoveHighlighting(message)
--On DEBUG only
--Misspelled:AddToInspector(cleanedMessage, "Misspelled:SendChatMessage - gotMessage")
--self.hooks[C_ChatInfo]["SendChatMessage"](cleanedMessage, chatType, languageID, target, ...)
local gameType = "Unknown"
Misspelled.hooks["SendChatMessage"](cleanedMessage, chatType, languageID, target, ...)
end
--Possible changes:
--A single new character was inserted at the end of the line (line length grew by 1)
-- If this char is a word separator, then spell check the line
--
--Any other change in the line, requires we recheck the entire line.
--More than a single character was pasted or linked into the line.
-- remove any highlighting and recheck the entire line.
--
--A single character was removed from the line
function Misspelled.EditBox_OnTextChanged(editbox)
if SkipOnTextChanged == true then
SkipOnTextChanged = false
return
end
--print("Editbox name:", editbox:GetName())
--Load the guild roster if needed
if GuildRosterCalled == false then
if IsInGuild() then
GuildRoster() --Request an updated guild roster from the server.
else
Misspelled:LoadGuildAndFriendRoster()
end
GuildRosterCalled = true
end
local text = editbox:GetText()
local pos = editbox:GetCursorPosition()
--print ("TextChaged:", editbox:GetCursorPosition(), string.gsub(editbox:GetText(), "\124", "\124\124"))
local newLineLength = #text
--Check if we should clear the WordCache table to save memory, if it's gotten very large
if newLineLength == 0 then
if WordCacheCount > WordCacheCountMax then
WordCache = {}
WordCacheCount = 0
end
end
--if the first char is a /, indicating some slash command, skip spellchecking the text.
if (string_sub(text, 1, 1) == "/" ) then
local cleanedChatMessage = Misspelled:RemoveHighlighting(text)
if text ~= cleanedChatMessage then
editbox:SetText(cleanedChatMessage)
if pos == 1 or pos== 0 then
editbox:SetCursorPosition(pos)
end
OldLineLength = #text
end
RightClickedWord = nil
WordLocations[editbox:GetName()] = {}
return
end
--If we currently just added one new char to the end of the line, see if it's a word boundary char
--CursorPosition (pos) must be at the end of the line, and the line size must have had grown by
--one character.
if pos == #text and newLineLength -1 == OldLineLength then
local lastChar = string_sub(text, -1, -1)
if string_match(lastChar, "[ %(%);,%.!%?:\"]") ~= nil then
Misspelled:SpellCheckChat(editbox)
end
elseif pos ~= #text then
----We must be making some other kind of edit someplace other than at the end of the line.
----Recheck the entire line
Misspelled:SpellCheckChat(editbox)
end
--Save the new line length for use with the next round of OnTextChanged processing.
OldLineLength = #editbox:GetText()
end
function Misspelled.EditBox_OnEscapePressed(editbox)
RightClickedWord = nil
WordLocations[editbox:GetName()] = {}
end
function Misspelled.EditBox_OnEnterPressed(editbox)
RightClickedWord = nil
WordLocations[editbox:GetName()] = {}
--before message is sent, remove and misspelled highlighting
local cleanedChatMessage = Misspelled:RemoveHighlighting(editbox:GetText())
WordLocations[editbox:GetName()] = {}
editbox:SetText(cleanedChatMessage)
end
--Return a string where misspelled words are highlighted
--If this string is a slash command "/", don't perform any highlighting
--We should keep track of the current edit cursor position,
--so we can report it's new position after highlighting.
function Misspelled:SpellCheckChat(editbox)
local editboxText = editbox:GetText()
if #editboxText < 2 then return end
--Ensure we have hooked the MouseUp event
--Should fix Chatter changing the OnMouseUp script to nil. Bad Chatter
local hooked, hookHandler = Misspelled:IsHooked(editbox, "OnMouseUp")
if hooked == false or (hooked == true and hookHandler ~= self.OnMouseUp) then
Misspelled:Unhook(editbox, "OnMouseUp")
Misspelled:RawHookScript(editbox, "OnMouseUp", Misspelled.EditBox_OnMouseUp)
end
local newText = editboxText
--Watch how many characters we insert or remove from the left side of the cursor position.
--We'll adjust the cursor position to compensate for any misspelled word Hex code colored text added by Misspelled.
local newCPos
--remove any previous misspelling highlighting before checking the editboxText for misspellings.
newText, newCPos = Misspelled:RemoveHighlighting(editboxText, editbox:GetCursorPosition())
WordLocations[editbox:GetName()] = {}
--If this is a command, don't spellcheck or highlight
if string_sub(editboxText, 1, 1) == "/" then
if newText ~= editboxText then
editbox:SetText(newText)
editbox:SetCursorPosition(newCPos)
end
end
--Find misspelled words & populate the WordLocations info.
Misspelled:CheckLine(newText, editbox)
local colorCodesAdded = 0
--Use the WordLocation info to march backwards through the input text,
--highlighting misspellings
--tprint(WordLocations)
local w
for x = #WordLocations[editbox:GetName()], 1, -1 do
w = WordLocations[editbox:GetName()][x]
if WordCache[w.Word].Correct == false then
--Insert highlighting
newText = string_sub(newText, 1, w.StartPos -1) .. SPELLED_WRONG_HIGHLIGHT .. string_sub(newText, w.StartPos, w.EndPos) .. FONT_COLOR_CODE_CLOSE .. string_sub(newText, w.EndPos + 1)
--Adjust cursor position if the cursor was to the right of the first char in the word we're highlighting.
if newCPos >= w.EndPos then
newCPos = newCPos + #SPELLED_WRONG_HIGHLIGHT + #FONT_COLOR_CODE_CLOSE
elseif newCPos >= w.StartPos then
newCPos = newCPos + #SPELLED_WRONG_HIGHLIGHT
end
colorCodesAdded = colorCodesAdded + 1
if colorCodesAdded >= MaxColorCodes then
--Truncate WordLocations to here, because there are too many misspelled words.
--The WoW chatbox won't let us add more than so many color coded sections.
--Delete all entries before x
WordLocations[editbox:GetName()] = { unpack( WordLocations[editbox:GetName()], x ) }
break
end
end
end
--Adjust the word's WordLocation StartPos and EndPos, wherever we added highlighting.
--The right click handler uses this position info. to detect the misspelled word that was right clicked.
--March forward this time
local n = 0 --NewCharsAddedCounter
for x = 1, #WordLocations[editbox:GetName()] do
w = WordLocations[editbox:GetName()][x]
if WordCache[w.Word].Correct == false then
w.StartPos = w.StartPos + n + #SPELLED_WRONG_HIGHLIGHT
n = n + #SPELLED_WRONG_HIGHLIGHT
w.EndPos = w.EndPos + n
n = n + #FONT_COLOR_CODE_CLOSE
end
end
if newText ~= editboxText then
--When we call settext, an OnSetText event will fire.
--Execution of this event should be skipped to avoid SpellCheckChat from running twice.
--Use a local toggle to skip this second firing
SkipOnTextChanged = true
editbox:SetText(newText)
editbox:SetCursorPosition(newCPos)
end
end
--Finds all words in the input text string, ignoring any Wow hyperlinks or textures.
--These links or textures will be replaced with a sequence of # characters, the same length of the color+link,
--so the contents of these are not spellchecked.
--
--Next populate the table, WordLocations, storing the following info. on each word:
-- WordLocations[x] = {["word"] = word, ["StartPos"] = matchPosStart, ["EndPos"] = matchPosEnd}
-- x == Ordinal position of the word in the text string
--
--Next check each word to see we need to cache spell check info for the word.
--Words with numbers or words in all upper case are ignored.
--Store in the WordCache table whether the word is spell correctly, along with the
--any misspelled replacement suggestions.
--
--Trim the WordCache table if it's grown very large
function Misspelled:CheckLine(text, editbox)
--Reset the info on where each word is located
WordLocations[editbox:GetName()] = {}
if text == nil then return end
if #text == 0 then return end
--Find if there are any WoW UI escape sequences on this line, and replace them with # chars,
--so they don't match as words in the next stage of parsing and get ignored for spellchecking.
--ref: https://warcraft.wiki.gg/wiki/UI_escape_sequences
-- |cn[^:]+:.*|r -- Global Colors text (new in 11.1.5 - |cncolorname:text|r)
-- |cnIQ%d:.*|r -- Item Quality Colors (new in 11.1.5 - |cnIQn:text|r
-- |[Cc]%x-|H.+|h.+|h|r -- Hex color coded text with links (format: |cffxxxxxx|Htype:payload|h[text]|h|r ) https://warcraft.wiki.gg/wiki/Hyperlinks
-- |cn[^:]+:|H.+|h.+h|r -- Global Colors / Custom item color links (new in 11.1.5 - |cncolorname:text|r) https://warcraft.wiki.gg/wiki/UI_escape_sequences#:~:text=back%20to%20white-,Global%20Colors,-%7Ccncolorname%3A
-- |H.*|h -- Links
-- |T.*|t -- Textures
-- |A.*|a -- Texture Atlas
-- {.-} -- Raid target icons
-- |n -- newline character
local newText = text
-- newText = string_gsub(newText, "(|[Cc]%x-|H.-|h.-|h|r)", function(x) return string_rep("#", #x) end)
-- newText = string_gsub(newText, "(|H.*|h)", function(x) return string_rep("#", #x) end)
-- newText = string_gsub(newText, "(|T.*|t)", function(x) return string_rep("#", #x) end)
-- newText = string_gsub(newText, "({.-})", function(x) return string_rep("#", #x) end)
-- newText = string_gsub(newText, "(|n)", function(x) return string_rep("#", #x) end)
local WowTextMarkupEscapes = {
["(|cn[^:]+:.-|r)"] = "#", -- Global Colors text
["(|cnIQ%d:.-|r)"] = "#", -- Item Quality Colors text
["(|[Cc]%x-|H.-|h.-|h|r)"] = "#", -- Hex color coded text with optional colored links
["(|H.-|h)"] = "#", -- Links
["(|T.-|t)"] = "#", -- Textures
["(|A.-|a)"] = "#", -- Texture Atlas
["({.-})"] = "#", -- Raid target icons
["(|n)"] = "#" -- Newline character
}
for k, v in pairs(WowTextMarkupEscapes) do
newText = string_gsub(newText, k, function(x) return string_rep(v, #x) end)
end
--March through the text, finding the words, record there start & end positions, and spell check status
local patt = "[A-Za-z0-9_'À-ÿœæŒÆ]+"
local matchPosStart
local matchPosEnd
matchPosStart, matchPosEnd = string_find(newText, patt)
local x = 0
local word
local correct
while matchPosStart ~= nil do
word = string_sub(newText, matchPosStart, matchPosEnd)
--ignore all uppercase words
if word ~= string_upper(word) then
--ignore words with numbers in them
if string_match(word, "[%d]") == nil then
x = x + 1
WordLocations[editbox:GetName()][x] = {["Word"] = word, ["StartPos"] = matchPosStart, ["EndPos"] = matchPosEnd}
if WordCache[word] == nil then
correct = false
--Ignore words in all upper case
if word == string_upper(word) then
correct = true
end
--See if the dictionary contains the word
if correct == false then
correct = WordDict:Contains(word)
end
--Try the lower case version of the word
if correct == false then
correct = WordDict:Contains(string_lower(word))
end
--Cache the results
WordCache[word] = {["Correct"] = correct} --, ["Suggestions"] = {}}
WordCacheCount = WordCacheCount + 1
--Changed to delay searching for suggestions until someone right-clicks on a misspelled word.
--Adding UTF8 support slows the suggestion generation.
--~ if correct == false then
--~ local suggestions = {}
--~ suggestions = WordDict:Suggest(word)
--~ if #suggestions > 0 then
--~ WordCache[word].Suggestions = suggestions
--~ end
--~ end
end
end
end
matchPosStart, matchPosEnd = string_find(newText, patt, matchPosEnd+1)
end
end
--Return a string where the highlighting has been removed from any misspelled words,
--with the goal to not change or destroy any other UI escaped sequences present in the chat message,
--such as colored text, Wow itemLinks or textures.
--(https://warcraft.wiki.gg/wiki/UI_escape_sequences & http://www.wowwiki.com/ItemLink)
--
--Misspelled word highlighting is added with a Hex color coded UI escape sequence: |cff7dc6fbMisspelledtext|r
--The chat message text may contain other UI escaped sequences such at item links.
--The chat message text could also contain one or more pipe characters ||, complicating parsing.
--
-- Note: Lua's standard regular expression library has limitations compared to some other regex engines.
-- It does not support features like negative lookahead assertions ((?!...)),
-- which are typically used to assert that a sequence is not present.
-- Therefore, a single Lua regular expression cannot directly say "match everything until |r, but fail if |h is encountered before that".
--
--Potential refactor:
-- 1) Use a regular expression to match any block starting with |cff7dc6fb and ending with |r, capturing everything in between.
-- regex capture: |c%x-(.-)|r
-- 2) Check the captures text to ensure it does not contain the sequence |h.
--
--It's possible that a recent edit has started to destroy the color tags, either at the
--beginning or end of a highlighted misspelled word.
--Attempt to detect this and remove any dangling colored text tags.
function Misspelled:RemoveHighlighting(text, ...)
-- \124 is the ASCII code for the pipe '|' character.
--Misspelled:AddToInspector(string_gsub(text, "\124", "\124\124"), "RemoveHighlighting-input")
--Blizzard uses string.gsub(textString, "[|]", "||"), in the /dump source code
local cleanedChatMessage
local newText = text
--Track the number of characters removed from the left side of the current cursor position.
local cPos
if ... ~= nil then
cPos = ...
else
cPos = 0
end
local itemLinks = {}
local itemLink
local itemLinkNum = 1
local patt, matchPosStart, matchPosEnd
local tokenSize
local tempToken
local tempText
local charsRemoved --Tracks the chars removed from the left of the cursor
--Try and match Global Colors text. (|cncolorname:text|r)
--Use a non-greedy match character (-) in the match pattern rather than a greed match character (*)
patt = "(|cn[^:]+:.-|r)"
matchPosStart, matchPosEnd = string_find(newText, patt)
while matchPosStart ~= nil do
--Store the itemlink and it's relative position so it can be replaced latter
itemLink = string_sub(newText, matchPosStart, matchPosEnd)
itemLinks[itemLinkNum] = itemLink
tokenSize = #itemLink
tempToken = "{<<" .. tostring(itemLinkNum)
tempToken = tempToken .. string_rep(">", tokenSize - #tempToken - 1) .. "}"
--Replace this itemlink with a temporary placeholder code
newText = string_gsub(newText, patt, tempToken, 1)
itemLinkNum = itemLinkNum + 1
matchPosStart, matchPosEnd = string_find(newText, patt)
end
--Try and match (IQn) Item Quality Colors text. (|cnIQn:text|r)
--Use a non-greedy match character (-) in the match pattern rather than a greed match character (*)
patt = "(|cnIQ%d:.-|r)"
matchPosStart, matchPosEnd = string_find(newText, patt)
while matchPosStart ~= nil do
--Store the itemlink and it's relative position so it can be replaced latter
itemLink = string_sub(newText, matchPosStart, matchPosEnd)
itemLinks[itemLinkNum] = itemLink
tokenSize = #itemLink
tempToken = "{<<" .. tostring(itemLinkNum)
tempToken = tempToken .. string_rep(">", tokenSize - #tempToken - 1) .. "}"
--Replace this itemlink with a temporary placeholder code
newText = string_gsub(newText, patt, tempToken, 1)
itemLinkNum = itemLinkNum + 1
matchPosStart, matchPosEnd = string_find(newText, patt)
end
--Try and match Hex code colored Item links. The Addon GHI (Gryphonheart Items) colors links with a capitol C, non-standard.
patt = "|[Cc]%x+|H.-|h.-|h|r"
matchPosStart, matchPosEnd = string_find(newText, patt)
while matchPosStart ~= nil do
--Store the itemlink and it's relative position so it can be replaced latter
itemLink = string_sub(newText, matchPosStart, matchPosEnd)
itemLinks[itemLinkNum] = itemLink
tokenSize = #itemLink
tempToken = "{<<" .. tostring(itemLinkNum)
tempToken = tempToken .. string_rep(">", tokenSize - #tempToken - 1) .. "}"
--Replace this itemlink with a temporary placeholder code
newText = string_gsub(newText, patt, tempToken, 1)
itemLinkNum = itemLinkNum + 1
matchPosStart, matchPosEnd = string_find(newText, patt)
end
--Try to match, non-colored Item Links
patt = "|H.-|h"
matchPosStart, matchPosEnd = string_find(newText, patt)
while matchPosStart ~= nil do
--Store the itemlink and it's relative position so it can be replaced latter
itemLink = string_sub(newText, matchPosStart, matchPosEnd)
itemLinks[itemLinkNum] = itemLink
tokenSize = #itemLink
tempToken = "{<<" .. tostring(itemLinkNum)
tempToken = tempToken .. string_rep(">", tokenSize - #tempToken - 1) .. "}"
--Replace this itemlink with a temporary placeholder code
newText = string_gsub(newText, patt, tempToken, 1)
itemLinkNum = itemLinkNum + 1
matchPosStart, matchPosEnd = string_find(newText, patt)
end
--Try to match and textures links. (i.e. Raid targets and there used when chatting with a GM)
patt = "|T.-|t"
matchPosStart, matchPosEnd = string_find(newText, patt)
while matchPosStart ~= nil do
--Store the itemlink and it's relative position so it can be replaced latter
itemLink = string_sub(newText, matchPosStart, matchPosEnd)
itemLinks[itemLinkNum] = itemLink
tokenSize = #itemLink
tempToken = "{<<" .. tostring(itemLinkNum)
tempToken = tempToken .. string_rep(">", tokenSize - #tempToken - 1) .. "}"
--Replace this itemlink with a temporary placeholder code
newText = string_gsub(newText, patt, tempToken, 1)
itemLinkNum = itemLinkNum + 1
matchPosStart, matchPosEnd = string_find(newText, patt)
end
--Remove the highlighting from any misspelled words.
--i.e. When the beginning SPELLED_WRONG_HIGHLIGHT Hex coded color tag and ending |r tag wrap text.
--Adjust the cursor position as needed.
patt = SPELLED_WRONG_HIGHLIGHT .. "(.-)|r"
matchPosStart, matchPosEnd = string_find(newText, patt)
Misspelled:AddToInspector({_patt=string_gsub(patt,"[|]","||"),_newText=string_gsub(newText,"[|]","||"),_matchPosStart=matchPosStart,_matchPosEnd=matchPosEnd},"RemoveHighlighting string.find misspelled highlighting")
while matchPosStart ~= nil do
--strings.gsub(input, pattern, replaceText, n=limit the number of substations to be made)
tempText = string_gsub(newText, patt, "%1", 1)
--tempText = string_gsub(newText, patt, function(x) return x end, 1)
if #newText - #tempText ~= 0 then
charsRemoved = 0
--If the cursor was to the right of the start, subtract the num of deleted chars from the cursor position
if cPos >= matchPosStart then
charsRemoved = (#newText - #tempText)
end
--If the cursor was to the left of the end color tag and to the right of the start, add 2
if cPos >= matchPosStart and cPos < matchPosEnd then
charsRemoved = charsRemoved - 2
end
cPos = cPos - charsRemoved
end
newText = tempText
matchPosStart, matchPosEnd = string_find(newText, patt)
end
--Remove any remaining orphaned beginning color tags.
patt = "|[Cc]%x%x%x%x%x%x%x%x"
matchPosStart, matchPosEnd = string_find(newText, patt)
while matchPosStart ~= nil do
tempText = string_gsub(newText, patt, "", 1)
if #newText - #tempText ~= 0 then
--If the cursor was to the right of the start, subtract the num of deleted chars from the cursor position
if cPos >= matchPosStart then
cPos = cPos - (#newText - #tempText)
end
end
newText = tempText
matchPosStart, matchPosEnd = string_find(newText, patt)
end
--Remove any remaining orphaned ending color tags.
patt = "|r"
matchPosStart, matchPosEnd = string_find(newText, patt)
while matchPosStart ~= nil do
tempText = string_gsub(newText, patt, "", 1)
if #newText - #tempText ~= 0 then
--If the cursor was to the right of the start, subtract the num of deleted chars from the cursor position
if cPos >= matchPosStart then
cPos = cPos - (#newText - #tempText)
end
end
newText = tempText
matchPosStart, matchPosEnd = string_find(newText, patt)
end
--Replace back the escape sequences extracted
if #itemLinks > 0 then
for i,val in ipairs(itemLinks) do
newText = string_gsub(newText, "{<<" .. tostring(i) .. ">-}", val)
end
end
--If by chance the tracked cursor position went negative, set it to 0
if cPos < 0 then
cPos = 0
end
cleanedChatMessage = newText
--cPos should never be > #newText, unless there's some unfound error above
return cleanedChatMessage, cPos
end
function Misspelled:TestRemoveHighlighting()
local testMessage
local checkMessage
local cleanedMessage
local newCPos
local testResult
--Test 1
testID = "1"
testMessage = "Apple"
checkMessage= "Apple"
cleanedMessage, newCPos = Misspelled:RemoveHighlighting(testMessage, string_len(testMessage))
local testResults_table = {
_testMessage = string_gsub(testMessage,"[|]","||"),
_checkMessage = string_gsub(checkMessage,"[|]","||"),
_cleanedMessage = string_gsub(cleanedMessage,"[|]","||"),
_CPos = string_len(testMessage),
_newCPos = newCPos
}
if cleanedMessage == checkMessage then testResult = "passed" else testResult = "failed" end
Misspelled:AddToInspector(testResults_table,"Test ".. testResult .. ": RemoveHighlighting "..testID)
--Test2 - Misspelled highlighted word: Applez
testID = "2"
testMessage = "|cff7dc6fbApplez|r"
checkMessage= "Applez"
cleanedMessage, newCPos = Misspelled:RemoveHighlighting(testMessage, string_len(testMessage))
local testResults_table = {
_testMessage = string_gsub(testMessage,"[|]","||"),
_checkMessage = string_gsub(checkMessage,"[|]","||"),
_cleanedMessage = string_gsub(cleanedMessage,"[|]","||"),
_CPos = string_len(testMessage),
_newCPos = newCPos
}
if cleanedMessage == checkMessage then testResult = "passed" else testResult = "failed" end
Misspelled:AddToInspector(testResults_table,"Test ".. testResult .. ": RemoveHighlighting "..testID)
--Test3 - Misspelled highlighted word: Applez good.
testID = "3"
testMessage = "|cff7dc6fbApplez|r good."
checkMessage= "Applez good."
cleanedMessage, newCPos = Misspelled:RemoveHighlighting(testMessage, string_len(testMessage))
local testResults_table = {
_testMessage = string_gsub(testMessage,"[|]","||"),
_checkMessage = string_gsub(checkMessage,"[|]","||"),
_cleanedMessage = string_gsub(cleanedMessage,"[|]","||"),
_CPos = string_len(testMessage),
_newCPos = newCPos
}
if cleanedMessage == checkMessage then testResult = "passed" else testResult = "failed" end
Misspelled:AddToInspector(testResults_table,"Test ".. testResult .. ": RemoveHighlighting "..testID)
--Test4 - Correctly spelled word [Link] correctly spelled word
testID = "4"
testMessage = "Test |cff71d5ff|Hspell:2061:0|h[Flash Heal]|h|r good."
checkMessage= "Test |cff71d5ff|Hspell:2061:0|h[Flash Heal]|h|r good."
cleanedMessage, newCPos = Misspelled:RemoveHighlighting(testMessage, string_len(testMessage))
local testResults_table = {
_testMessage = string_gsub(testMessage,"[|]","||"),
_checkMessage = string_gsub(checkMessage,"[|]","||"),
_cleanedMessage = string_gsub(cleanedMessage,"[|]","||"),
_CPos = string_len(testMessage),
_newCPos = newCPos
}
if cleanedMessage == checkMessage then testResult = "passed" else testResult = "failed" end
Misspelled:AddToInspector(testResults_table,"Test ".. testResult .. ": RemoveHighlighting "..testID)
--Test5 - Correctly spelled word [Spell Link] incorrectly spelled word
testID = "5"
testMessage = "Test |cff71d5ff|Hspell:2061:0|h[Flash Heal]|h|r |cff7dc6fbbadd|r."
checkMessage= "Test |cff71d5ff|Hspell:2061:0|h[Flash Heal]|h|r badd."
cleanedMessage, newCPos = Misspelled:RemoveHighlighting(testMessage, string_len(testMessage))
local testResults_table = {
_testMessage = string_gsub(testMessage,"[|]","||"),
_checkMessage = string_gsub(checkMessage,"[|]","||"),
_cleanedMessage = string_gsub(cleanedMessage,"[|]","||"),
_CPos = string_len(testMessage),
_newCPos = newCPos
}
if cleanedMessage == checkMessage then testResult = "passed" else testResult = "failed" end
Misspelled:AddToInspector(testResults_table,"Test ".. testResult .. ": RemoveHighlighting "..testID)
--Test6 - Correctly spelled word [Item link]
testID = "6"
testMessage = "Off-hand: |cffa335ee|Hitem:222566::::::::80:258::13:1:3524:6:40:2249:38:8:45:211296:46:226024:47:222584:48:224072:::::|h[Vagabond's Torch |A:Professions-ChatIcon-Quality-Tier5:17:17::1|a]|h|r"
checkMessage= "Off-hand: |cffa335ee|Hitem:222566::::::::80:258::13:1:3524:6:40:2249:38:8:45:211296:46:226024:47:222584:48:224072:::::|h[Vagabond's Torch |A:Professions-ChatIcon-Quality-Tier5:17:17::1|a]|h|r"
cleanedMessage, newCPos = Misspelled:RemoveHighlighting(testMessage, string_len(testMessage))
local testResults_table = {
_testMessage = string_gsub(testMessage,"[|]","||"),
_checkMessage = string_gsub(checkMessage,"[|]","||"),
_cleanedMessage = string_gsub(cleanedMessage,"[|]","||"),
_CPos = string_len(testMessage),
_newCPos = newCPos
}
if cleanedMessage == checkMessage then testResult = "passed" else testResult = "failed" end
Misspelled:AddToInspector(testResults_table,"Test ".. testResult .. ": RemoveHighlighting "..testID)
--Test7 - Correctly spelled word [Hex colored Item link] incorrectly spelled word.
testID = "7"
testMessage = "Off-hand: |cffa335ee|Hitem:222566::::::::80:258::13:1:3524:6:40:2249:38:8:45:211296:46:226024:47:222584:48:224072:::::|h[Vagabond's Torch |A:Professions-ChatIcon-Quality-Tier5:17:17::1|a]|h|r |cff7dc6fbbadd|r."
checkMessage= "Off-hand: |cffa335ee|Hitem:222566::::::::80:258::13:1:3524:6:40:2249:38:8:45:211296:46:226024:47:222584:48:224072:::::|h[Vagabond's Torch |A:Professions-ChatIcon-Quality-Tier5:17:17::1|a]|h|r badd."
cleanedMessage, newCPos = Misspelled:RemoveHighlighting(testMessage, string_len(testMessage))
local testResults_table = {
_testMessage = string_gsub(testMessage,"[|]","||"),
_checkMessage = string_gsub(checkMessage,"[|]","||"),
_cleanedMessage = string_gsub(cleanedMessage,"[|]","||"),
_CPos = string_len(testMessage),
_newCPos = newCPos
}
if cleanedMessage == checkMessage then testResult = "passed" else testResult = "failed" end
Misspelled:AddToInspector(testResults_table,"Test ".. testResult .. ": RemoveHighlighting "..testID)
--Test8 - Correctly spelled word [cnIQ#: colored Item link]
testID = "8"
testMessage = "test: |cnIQ2:|Hitem:225566::::::::80:258:::::::::|h[Warped Wing]|h|r"
checkMessage = "test: |cnIQ2:|Hitem:225566::::::::80:258:::::::::|h[Warped Wing]|h|r"
cleanedMessage, newCPos = Misspelled:RemoveHighlighting(testMessage, string_len(testMessage))
local testResults_table = {
_testMessage = string_gsub(testMessage,"[|]","||"),
_checkMessage = string_gsub(checkMessage,"[|]","||"),
_cleanedMessage = string_gsub(cleanedMessage,"[|]","||"),
_CPos = string_len(testMessage),
_newCPos = newCPos
}
if cleanedMessage == checkMessage then testResult = "passed" else testResult = "failed" end
Misspelled:AddToInspector(testResults_table,"Test ".. testResult .. ": RemoveHighlighting "..testID)
--Test9 - Correctly spelled word [cnIQ#: colored Item link] incorrectly spelled word.
testID = "9"
testMessage = "test: |cnIQ2:|Hitem:225566::::::::80:258:::::::::|h[Warped Wing]|h|r |cff7dc6fbbadd|r."
checkMessage = "test: |cnIQ2:|Hitem:225566::::::::80:258:::::::::|h[Warped Wing]|h|r badd."
cleanedMessage, newCPos = Misspelled:RemoveHighlighting(testMessage, string_len(testMessage))
local testResults_table = {
_testMessage = string_gsub(testMessage,"[|]","||"),
_checkMessage = string_gsub(checkMessage,"[|]","||"),
_cleanedMessage = string_gsub(cleanedMessage,"[|]","||"),
_CPos = string_len(testMessage),
_newCPos = newCPos
}
if cleanedMessage == checkMessage then testResult = "passed" else testResult = "failed" end
Misspelled:AddToInspector(testResults_table,"Test ".. testResult .. ": RemoveHighlighting "..testID)
end
-------------------------------------------------------------------------
--
-- Routines for the right click misspelled suggestions popup.
--
-------------------------------------------------------------------------
function Misspelled:OnMouseUp(editbox, button)
if button == "RightButton" then
local badWordFound = false
CloseDropDownMenus()
--check if we are positioned on a misspelled word
local pos = editbox:GetCursorPosition()
if WordLocations[editbox:GetName()] ~= nil then
for i, w in ipairs(WordLocations[editbox:GetName()]) do
if pos >= w.StartPos and pos <= w.EndPos then
if WordCache[w.Word].Correct == false then
--If not cached, lookup Suggestions for the misspelled word
if WordCache[w.Word].Suggestions == nil then
WordCache[w.Word].Suggestions = WordDict:Suggest(w.Word)
end
RightClickedWord = w.Word
RightClickedWordStartPos = w.StartPos
RightClickedWordEndPos = w.EndPos
RightClickedEditBox = editbox
badWordFound = true
ToggleDropDownMenu(1, nil, MisspelledSuggestions_DropDown, "cursor")
else
RightClickedWord = nil
end
break
end
end
end
--Show where the user right clicked.
--print("cursor position:" , ChatFrameEditBox:GetCursorPosition())
--If we are not positioned on a bad, word call and MouseUp handler that was hooked to the box.
if badWordFound == false then
--print("Trigger native OnMouseUp call")
self.hooks[editbox]["OnMouseUp"](editbox, button)
end
end
end
function MisspelledSuggestions_InitializeDropDown(level)
if RightClickedWord == nil then return end
if #RightClickedWord == 0 then return end
do
local info = UIDropDownMenu_CreateInfo()
info.text = L["Suggestions for:"] .. " " .. RightClickedWord
info.isTitle = 1
info.notClickable = 1
info.notCheckable = true
UIDropDownMenu_AddButton(info)
end
--Add suggestions to the DropDown
for i, s in ipairs(WordCache[RightClickedWord].Suggestions) do
do
local info = UIDropDownMenu_CreateInfo()
--Line below causes a this == nil error in 4.0. Looks like it's not needed.
--info.owner = this:GetParent()
--If the misspelled word's first
info.text = s.Word
--If this suggestion is either a guild member or friend append a note.
if Misspelled:IsGuildMember(s.Word) == true then
info.text = info.text .. " " .. L["(Guild)"]
else
if Misspelled:IsFriend(s.Word) == true then
info.text = info.text .. " " .. L["(Friend)"]
end
end
info.isTitle = nil
info.notCheckable = true
info.value = s.Word
info.func = function() SuggestionsFrame_Click(s.Word, RightClickedEditBox) end
--Add the above info to the options menu as clickable item
UIDropDownMenu_AddButton(info)
end
end
do
--Add a non-clickable separator
local info = UIDropDownMenu_CreateInfo()
--info.owner = this:GetParent()
info.text = ""
info.isTitle = nil
info.value = ""
info.notClickable = 1
info.notCheckable = true
UIDropDownMenu_AddButton(info)
end
do
local info = UIDropDownMenu_CreateInfo()
--info.owner = this:GetParent()
info.text = L["Ignore All"]
info.isTitle = nil
info.value = RightClickedWord
info.func = function() SuggestionsFrame_Click("###IgnoreAll", RightClickedEditBox) end
info.notClickable = nil
info.notCheckable = true
UIDropDownMenu_AddButton(info)
end
do
local info = UIDropDownMenu_CreateInfo()
--info.owner = this:GetParent()
info.text = L["Add to Dictionary"]
info.isTitle = nil
info.value = RightClickedWord
info.func = function() SuggestionsFrame_Click("###AddToDictionary", RightClickedEditBox) end
info.notClickable = nil
info.notCheckable = true
UIDropDownMenu_AddButton(info)
end
do
local info = UIDropDownMenu_CreateInfo()
--info.owner = this:GetParent()
info.text = L["Cancel"]
info.isTitle = nil
info.value = nil
info.notClickable = nil
info.notCheckable = true
UIDropDownMenu_AddButton(info)
end
end
function MisspelledSuggestions_DropDownOnLoad(self)
UIDropDownMenu_Initialize(self, MisspelledSuggestions_InitializeDropDown, "MENU")
end
function SuggestionsFrame_Click(value, editbox)
--value will equal the suggestion word clicked,
--or a "special" tag, for the 'Ignore' and 'Add to Dictionary' functions
--print("Suggestion Clicked: ", value)
local newText = editbox:GetText()
local newCursorPos = nil
local isCapitalizedRightClickedWord = false
--Check if the local (global) var RightClickedWord is populated with a non nil value
assert(RightClickedWord ~= nil, "Misspelled: SuggestionsFrame_Click, Unexpected: RightClickedWord == nil")
if string_sub(RightClickedWord, 1, 1) == string_upper(string_sub(RightClickedWord, 1, 1)) then
isCapitalizedRightClickedWord = true
end
if value == "###IgnoreAll" then
--Add this word to the WordCache so it will be ignored as misspelled until you reload
--print("Ignore:", RightClickedWord)
WordCache[RightClickedWord].Correct = true
WordCache[RightClickedWord].Suggestions = {}
--Remove the misspelled highlighting
newText = string_sub(newText, 1, RightClickedWordStartPos - 1 - #SPELLED_WRONG_HIGHLIGHT) .. RightClickedWord .. string_sub(newText, RightClickedWordEndPos + #FONT_COLOR_CODE_CLOSE + 1)
newCursorPos = RightClickedWordStartPos + #RightClickedWord - #SPELLED_WRONG_HIGHLIGHT - #FONT_COLOR_CODE_CLOSE + 1
elseif value == "###AddToDictionary" then
-- Add this word to the user dictionary
--print("Add to UserDict", RightClickedWord)
Misspelled:AddToUserDict(RightClickedWord)
WordCache[RightClickedWord].Correct = true
WordCache[RightClickedWord].Suggestions = {}
--Remove the misspelled highlighting
newText = string_sub(newText, 1, RightClickedWordStartPos - 1 - #SPELLED_WRONG_HIGHLIGHT) .. RightClickedWord .. string_sub(newText, RightClickedWordEndPos + #FONT_COLOR_CODE_CLOSE + 1)
newCursorPos = RightClickedWordStartPos + #RightClickedWord - #SPELLED_WRONG_HIGHLIGHT - #FONT_COLOR_CODE_CLOSE + 1
else
--replace this word with the selected suggestion.
--Remove the misspelled highlighting in the process.
--
--If the misspelled word was capitalized, capitalize the replacement.
if isCapitalizedRightClickedWord == true then
value = string_upper(string_sub(value, 1, 1)) .. string_sub(value, 2)
end
newText = string_sub(newText, 1, RightClickedWordStartPos - 1 - #SPELLED_WRONG_HIGHLIGHT) .. value .. string_sub(newText, RightClickedWordEndPos + #FONT_COLOR_CODE_CLOSE + 1)
--save the cursor position just after the corrected word, so we can set it later
newCursorPos = RightClickedWordStartPos + #value - #SPELLED_WRONG_HIGHLIGHT - #FONT_COLOR_CODE_CLOSE + 1
--Note: if the replacement word is a different length, then the WordLocations will be messed up.
end
editbox:SetText(newText)
--printable = gsub(newText, "\124", "\124\124") --\124 == "|"
--print("New ChatText:", printable)
--If we replaced a word, with a suggestion, move the cursor to the end of the new word.
if newCursorPos ~= nil then
--Check if the character to the right of the cursor is a space. If so adjust the cursor to the right by 1 char.
if #newText > newCursorPos then
if string_sub(newText, newCursorPos + 1, newCursorPos + 1) == " " then
newCursorPos = newCursorPos + 1
end
end
editbox:SetCursorPosition(newCursorPos)
end
RightClickedWord = nil
end
-----------------------------------------------------
-- End: Right click Suggestions popup
-----------------------------------------------------
-------------------------------------------------------------------------
--
-- Routines for dealing with the user dictionary
--
-------------------------------------------------------------------------
--Load the words saved in the Users dictionary into the baseWords table.
--In r18 we changed the in memory format used to store the baseWords, affixCode and PhoneticCode,
--Saved a ton of memory not using a sub-table per baseWord.
--If necessary convert the user dictionary storage to match the newer format.
function Misspelled:LoadUserDict()
if Misspelled_DB == nil then
Misspelled_DB = {UserDict = {}}
end
local affixKeys, pCode
if Misspelled_DB.UserDict ~= nil then
for k, v in pairs(Misspelled_DB.UserDict) do
--If needed convert the user dictionary format to the, post r18 format.
if type(v) == "table" then
affixKeys = v[1]
pCode = v[2]
if affixKeys == nil then affixKeys = "" end
if pCode == nil then pCode = "" end
v = affixKeys .. "/" .. pCode
Misspelled_DB.UserDict[k] = v
end
if WordDict.baseWords[k] == nil then
WordDict.baseWords[k] = v
end
end
end
end
--Add a new word to the users dictionary, and the currently loaded baseWords table.
--Store both the word and it's phonetic code.
function Misspelled:AddToUserDict(word)
if word == nil then return end
if #word == 0 then return end
if Misspelled_DB.UserDict == nil then
Misspelled_DB.UserDict = {}
end
--Add the new word to the UserDict saved variable
local pcode = WordDict:PhoneticCode(word)
Misspelled_DB.UserDict[word] = "/" .. pcode
--And add it to the currently loaded dictionary
WordDict.baseWords[word] = "/" .. pcode
--Fixup the WordCache
WordCache[word].Correct = true
WordCache[word].Suggestions = {}
end
local Misspelled_Words_To_Delete = {}
function Misspelled:EditUserDict()
if not Misspelled_DB then Misspelled_DB = {} end
if not Misspelled_DB.UserDict then Misspelled_DB.UserDict = {} end
Misspelled_Words_To_Delete = {}
local f = _G.MisspelledUserDictFrame
if not f then
f = CreateFrame("Frame", "MisspelledUserDictFrame", UIParent)
f:SetWidth(320); f:SetHeight(420); f:SetPoint("CENTER")
f:SetBackdrop({bgFile="Interface\\DialogFrame\\UI-DialogBox-Background", edgeFile="Interface\\DialogFrame\\UI-DialogBox-Border", tile=true, tileSize=32, edgeSize=32, insets={left=11,right=12,top=12,bottom=11}})
local title = f:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
title:SetPoint("TOP", 0, -20); title:SetText(L["User Dictionary"])
local scroll = CreateFrame("ScrollFrame", "MisspelledUserDictScroll", f, "UIPanelScrollFrameTemplate")
scroll:SetPoint("TOPLEFT", 24, -52); scroll:SetPoint("BOTTOMRIGHT", -42, 52)
local content = CreateFrame("Frame", nil, scroll); content:SetWidth(245); scroll:SetScrollChild(content)
f.content = content
local del = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
del:SetWidth(100); del:SetHeight(24); del:SetPoint("BOTTOM", 0, 18); del:SetText("Delete")
del:SetScript("OnClick", function()
for word in pairs(Misspelled_Words_To_Delete) do
Misspelled_DB.UserDict[word] = nil; WordDict.baseWords[word] = nil
end
WordCache = {}; WordCacheCount = 0; f:Hide()
end)
f.deleteButton = del
end
for _, child in ipairs({f.content:GetChildren()}) do child:Hide(); child:SetParent(nil) end
local row = 0
for word in pairs(Misspelled_DB.UserDict) do
row = row + 1
local b = CreateFrame("Button", nil, f.content)
b:SetHeight(18); b:SetWidth(240); b:SetPoint("TOPLEFT", 0, -(row-1)*20)
b.text = b:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
b.text:SetPoint("LEFT"); b.text:SetText(word)
b:SetScript("OnClick", function(self)
if Misspelled_Words_To_Delete[word] then Misspelled_Words_To_Delete[word] = nil; self.text:SetTextColor(1,1,1)
else Misspelled_Words_To_Delete[word] = true; self.text:SetTextColor(1,.2,0) end
end)
end
f.content:SetHeight(math.max(1, row*20)); f.deleteButton:Enable(); f:Show()
end
-------------------------------------------------------------------------
-- End: Routines for dealing with the user dictionary
-------------------------------------------------------------------------
-------------------------------------------------------------------------
--
-- Load Guild and Friend Roster Routine
--
-------------------------------------------------------------------------
--Load the player's guild members and friends, as valid words into the
--loaded dictionary.
function Misspelled:LoadGuildAndFriendRoster()
local numFriends, f
local pcode
--print("Misspelled: Friends names and guild members loading...")
--First check your friends list
if GetNumFriends then
numFriends = GetNumFriends()
if numFriends > 0 then
for i = 1, numFriends do
local friendName
if C_FriendList and C_FriendList.GetFriendInfoByIndex then
f = C_FriendList.GetFriendInfoByIndex(i)
friendName = f and f.name
else
friendName = GetFriendInfo(i)
end
if friendName and #friendName > 0 then
if WordDict:Contains(friendName) == false then
if WordDict.soundslike == WordDict.Const.SoundslikeAlgorithms.PHONETIC then
pcode = WordDict:PhoneticCode(friendName)
elseif WordDict.soundslike == WordDict.Const.SoundslikeAlgorithms.GENERIC then
pcode = WordDict:GenericSoundsLike(friendName)
else
pcode = ""
end
WordDict.baseWords[friendName] = "/" .. pcode
end
end
end
end
end
-- Guild members are valid words.
local numTotalInGuild, guildMemberName
if GetNumGuildMembers() ~= 0 then
numTotalInGuild = GetNumGuildMembers()
if ( numTotalInGuild > 0 ) then
for i=1, numTotalInGuild do
guildMemberName = GetGuildRosterInfo(i);
if guildMemberName ~= nil then
if #guildMemberName ~= 0 then
if WordDict:Contains(guildMemberName) == false then
--Look up the phonetic code for this guild member name
if WordDict.soundslike == WordDict.Const.SoundslikeAlgorithms.PHONETIC then
pcode = WordDict:PhoneticCode(guildMemberName)
elseif WordDict.soundslike == WordDict.Const.SoundslikeAlgorithms.GENERIC then
pcode = WordDict:GenericSoundsLike(guildMemberName)
else
pcode = ""
end
--Add the guild member to the loaded dictionary
WordDict.baseWords[guildMemberName] = "/" .. pcode
end
end
end
end
end
--print("Misspelled: Guild Members Loaded")
Misspelled:UnregisterEvent("GUILD_ROSTER_UPDATE")
end
end
-------------------------------------------------------------------------
-- End: Load Guild and Friend Roster Routine
-------------------------------------------------------------------------
--[[ Interface Options Window ]]--
function Misspelled:CreateInterfaceOptions()
local cfgFrame = CreateFrame("FRAME", nil, UIParent)
cfgFrame.name = "Misspelled"
local cfgFrameHeader = cfgFrame:CreateFontString("OVERLAY", nil, "GameFontNormalLarge")
cfgFrameHeader:SetPoint("TOPLEFT", 15, -15)
cfgFrameHeader:SetText(self.Version)
local cfgFrameReloadTip = cfgFrame:CreateFontString("OVERLAY", nil, "GameFontNormal")
cfgFrameReloadTip:SetPoint("TOPLEFT", 20, -252)
cfgFrameReloadTip:SetText(L["Note: reload the game UI to load a different selected dictionary"])
local cfgAutoSelectDict = CreateFrame("CHECKBUTTON", "Misspelled_cfgAutoSelectDict", cfgFrame, "OptionsCheckButtonTemplate")
Misspelled_cfgAutoSelectDict:SetPoint("TOPLEFT", 20, -40)
Misspelled_cfgAutoSelectDictText:SetText(L["Auto Select Dictionary to Load"])
Misspelled_cfgAutoSelectDict:SetChecked(Misspelled_DB.AutoSelectDictionary )
Misspelled_cfgAutoSelectDict:SetScript("OnClick", function(self)
if self:GetChecked() then
PlaySound("igMainMenuOptionCheckBoxOn") -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
else
PlaySound("igMainMenuOptionCheckBoxOff") -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
end
Misspelled_DB.AutoSelectDictionary = not Misspelled_DB.AutoSelectDictionary
--Toggle the sub options
if Misspelled_DB.AutoSelectDictionary == true then
Misspelled_cfgDictdeDE:Disable()
Misspelled_cfgDictenGB:Disable()
Misspelled_cfgDictenUS:Disable()
Misspelled_cfgDictesES:Disable()
Misspelled_cfgDictfrFR:Disable()
Misspelled_cfgDictruRU:Disable()
Misspelled_cfgDictitIT:Disable()
else
Misspelled_cfgDictdeDE:Enable()
Misspelled_cfgDictenGB:Enable()
Misspelled_cfgDictenUS:Enable()
Misspelled_cfgDictesES:Enable()
Misspelled_cfgDictfrFR:Enable()
Misspelled_cfgDictruRU:Enable()
Misspelled_cfgDictitIT:Enable()
if Misspelled_DB.LoadDictionary == nil or #Misspelled_DB.LoadDictionary == 0 then
Misspelled_DB.LoadDictionary = "enUS"
Misspelled_cfgDictenUS:SetChecked(true)
end
end
end)
local cfgDictdeDE = CreateFrame("CHECKBUTTON", "Misspelled_cfgDictdeDE", cfgFrame, "OptionsCheckButtonTemplate")
Misspelled_cfgDictdeDE:SetPoint("TOPLEFT", 40, -64)
Misspelled_cfgDictdeDEText:SetText("deDE")
Misspelled_cfgDictdeDE:SetChecked(Misspelled_DB.LoadDictionary == "deDE")
Misspelled_cfgDictdeDE:SetScript("OnClick", function(self)
if self:GetChecked() then
PlaySound("igMainMenuOptionCheckBoxOn") -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
Misspelled_DB.LoadDictionary = "deDE"
Misspelled_cfgDictenGB:SetChecked(false)
Misspelled_cfgDictenUS:SetChecked(false)
Misspelled_cfgDictesES:SetChecked(false)
Misspelled_cfgDictfrFR:SetChecked(false)
Misspelled_cfgDictruRU:SetChecked(false)
Misspelled_cfgDictitIT:SetChecked(false)
else
PlaySound("igMainMenuOptionCheckBoxOff") -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
end
end)
local cfgDictenGB = CreateFrame("CHECKBUTTON", "Misspelled_cfgDictenGB", cfgFrame, "OptionsCheckButtonTemplate")
Misspelled_cfgDictenGB:SetPoint("TOPLEFT", 40, -88)
Misspelled_cfgDictenGBText:SetText("enGB")
Misspelled_cfgDictenGB:SetChecked(Misspelled_DB.LoadDictionary == "enGB")
Misspelled_cfgDictenGB:SetScript("OnClick", function(self)
if self:GetChecked() then
PlaySound("igMainMenuOptionCheckBoxOn") -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
Misspelled_DB.LoadDictionary = "enGB"
Misspelled_cfgDictdeDE:SetChecked(false)
Misspelled_cfgDictenUS:SetChecked(false)
Misspelled_cfgDictesES:SetChecked(false)
Misspelled_cfgDictfrFR:SetChecked(false)
Misspelled_cfgDictruRU:SetChecked(false)
Misspelled_cfgDictitIT:SetChecked(false)
else
PlaySound("igMainMenuOptionCheckBoxOff") -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
end
end)
local cfgDictenUS = CreateFrame("CHECKBUTTON", "Misspelled_cfgDictenUS", cfgFrame, "OptionsCheckButtonTemplate")
Misspelled_cfgDictenUS:SetPoint("TOPLEFT", 40, -112)
Misspelled_cfgDictenUSText:SetText("enUS")
Misspelled_cfgDictenUS:SetChecked(Misspelled_DB.LoadDictionary == "enUS")
Misspelled_cfgDictenUS:SetScript("OnClick", function(self)
if self:GetChecked() then
PlaySound("igMainMenuOptionCheckBoxOn") -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
Misspelled_DB.LoadDictionary = "enUS"
Misspelled_cfgDictdeDE:SetChecked(false)
Misspelled_cfgDictenGB:SetChecked(false)
Misspelled_cfgDictesES:SetChecked(false)
Misspelled_cfgDictfrFR:SetChecked(false)
Misspelled_cfgDictruRU:SetChecked(false)
Misspelled_cfgDictitIT:SetChecked(false)
else
PlaySound("igMainMenuOptionCheckBoxOff") -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
end
end)
local cfgDictesES = CreateFrame("CHECKBUTTON", "Misspelled_cfgDictesES", cfgFrame, "OptionsCheckButtonTemplate")
Misspelled_cfgDictesES:SetPoint("TOPLEFT", 40, -136)
Misspelled_cfgDictesESText:SetText("esES")
Misspelled_cfgDictesES:SetChecked(Misspelled_DB.LoadDictionary == "esES")
Misspelled_cfgDictesES:SetScript("OnClick", function(self)
if self:GetChecked() then
PlaySound("igMainMenuOptionCheckBoxOn") -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
Misspelled_DB.LoadDictionary = "esES"
Misspelled_cfgDictdeDE:SetChecked(false)
Misspelled_cfgDictenGB:SetChecked(false)
Misspelled_cfgDictenUS:SetChecked(false)
Misspelled_cfgDictfrFR:SetChecked(false)
Misspelled_cfgDictruRU:SetChecked(false)
Misspelled_cfgDictitIT:SetChecked(false)
else
PlaySound("igMainMenuOptionCheckBoxOff") -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
end
end)
local cfgDictfrFR = CreateFrame("CHECKBUTTON", "Misspelled_cfgDictfrFR", cfgFrame, "OptionsCheckButtonTemplate")
Misspelled_cfgDictfrFR:SetPoint("TOPLEFT", 40, -160)
Misspelled_cfgDictfrFRText:SetText("frFR")
Misspelled_cfgDictfrFR:SetChecked(Misspelled_DB.LoadDictionary == "frFR")
Misspelled_cfgDictfrFR:SetScript("OnClick", function(self)
if self:GetChecked() then
PlaySound("igMainMenuOptionCheckBoxOn") -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
Misspelled_DB.LoadDictionary = "frFR"
Misspelled_cfgDictdeDE:SetChecked(false)
Misspelled_cfgDictenGB:SetChecked(false)
Misspelled_cfgDictenUS:SetChecked(false)
Misspelled_cfgDictesES:SetChecked(false)
Misspelled_cfgDictruRU:SetChecked(false)
Misspelled_cfgDictitIT:SetChecked(false)
else
PlaySound("igMainMenuOptionCheckBoxOff") -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
end
end)
local cfgDictruRU = CreateFrame("CHECKBUTTON", "Misspelled_cfgDictruRU", cfgFrame, "OptionsCheckButtonTemplate")
Misspelled_cfgDictruRU:SetPoint("TOPLEFT", 40, -184)
Misspelled_cfgDictruRUText:SetText("ruRU")
Misspelled_cfgDictruRU:SetChecked(Misspelled_DB.LoadDictionary == "ruRU")
Misspelled_cfgDictruRU:SetScript("OnClick", function(self)
if self:GetChecked() then
PlaySound("igMainMenuOptionCheckBoxOn") -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
Misspelled_DB.LoadDictionary = "ruRU"
Misspelled_cfgDictdeDE:SetChecked(false)
Misspelled_cfgDictenGB:SetChecked(false)
Misspelled_cfgDictenUS:SetChecked(false)
Misspelled_cfgDictfrFR:SetChecked(false)
Misspelled_cfgDictesES:SetChecked(false)
Misspelled_cfgDictitIT:SetChecked(false)
else
PlaySound("igMainMenuOptionCheckBoxOff") -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
end
end)
local cfgDictitIT = CreateFrame("CHECKBUTTON", "Misspelled_cfgDictitIT", cfgFrame, "OptionsCheckButtonTemplate")
Misspelled_cfgDictitIT:SetPoint("TOPLEFT", 40, -208)
Misspelled_cfgDictitITText:SetText("itIT")
Misspelled_cfgDictitIT:SetChecked(Misspelled_DB.LoadDictionary == "itIT")
Misspelled_cfgDictitIT:SetScript("OnClick", function(self)
if self:GetChecked() then
PlaySound("igMainMenuOptionCheckBoxOn") -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
Misspelled_DB.LoadDictionary = "itIT"
Misspelled_cfgDictdeDE:SetChecked(false)
Misspelled_cfgDictenGB:SetChecked(false)
Misspelled_cfgDictenUS:SetChecked(false)
Misspelled_cfgDictfrFR:SetChecked(false)
Misspelled_cfgDictesES:SetChecked(false)
Misspelled_cfgDictruRU:SetChecked(false)
else
PlaySound("igMainMenuOptionCheckBoxOff") -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
end
end)
--Edit User Dictionary Button
local cfgEditUserDict = CreateFrame("Button", "EdutUserDictButton", cfgFrame, "UIPanelButtonTemplate")
cfgEditUserDict:SetPoint("TOPLEFT", 20, -287)
cfgEditUserDict:SetText(L["Edit User Dictionary..."])
cfgEditUserDict:SetWidth(200)
cfgEditUserDict:SetHeight(24)
cfgEditUserDict:SetScript("OnClick", function(self)
--PlaySound("igMainMenuOptionCheckBoxOn")
Misspelled:EditUserDict()
end)
--set options on startup
Misspelled_cfgDictdeDE:SetChecked(false)
Misspelled_cfgDictenUS:SetChecked(false)
Misspelled_cfgDictenGB:SetChecked(false)
Misspelled_cfgDictesES:SetChecked(false)
Misspelled_cfgDictfrFR:SetChecked(false)
Misspelled_cfgDictruRU:SetChecked(false)
Misspelled_cfgDictitIT:SetChecked(false)
if Misspelled_DB.LoadDictionary == "deDE" then
Misspelled_cfgDictdeDE:SetChecked(true)
elseif Misspelled_DB.LoadDictionary == "enGB" then
Misspelled_cfgDictenGB:SetChecked(true)
elseif Misspelled_DB.LoadDictionary == "enUS" then
Misspelled_cfgDictenUS:SetChecked(true)
elseif Misspelled_DB.LoadDictionary == "esES" then
Misspelled_cfgDictesES:SetChecked(true)
elseif Misspelled_DB.LoadDictionary == "frFR" then
Misspelled_cfgDictfrFR:SetChecked(true)
elseif Misspelled_DB.LoadDictionary == "ruRU" then
Misspelled_cfgDictruRU:SetChecked(true)
elseif Misspelled_DB.LoadDictionary == "itIT" then
Misspelled_cfgDictitIT:SetChecked(true)
end
if Misspelled_DB.AutoSelectDictionary == true then
Misspelled_cfgDictdeDE:Disable()
Misspelled_cfgDictenGB:Disable()
Misspelled_cfgDictenUS:Disable()
Misspelled_cfgDictesES:Disable()
Misspelled_cfgDictfrFR:Disable()
Misspelled_cfgDictruRU:Disable()
Misspelled_cfgDictitIT:Disable()
else
Misspelled_cfgDictdeDE:Enable()
Misspelled_cfgDictenGB:Enable()
Misspelled_cfgDictenUS:Enable()
Misspelled_cfgDictesES:Enable()
Misspelled_cfgDictfrFR:Enable()
Misspelled_cfgDictruRU:Enable()
Misspelled_cfgDictitIT:Enable()
end
--Add options frame to the list of in-game addon options
--Adding addon options changed in Wow 11.0
if InterfaceOptions_AddCategory then -- Check for compatiability for older Wow clients.
InterfaceOptions_AddCategory(cfgFrame) -- For Wow clients < v11
elseif Settings then -- For Wow clients > v11
local category, layout = Settings.RegisterCanvasLayoutCategory(cfgFrame, cfgFrame.name)
Settings.RegisterAddOnCategory(category)
end
end
-------------------------------------------------------------------------
--
-- Utility Routines
--
-------------------------------------------------------------------------
--Split a string, at patt delimiter, into a table
function Misspelled:split(str, patt)
local vals = {}
local valindex = 0
local word = ""
-- need to add a trailing separator to catch the last value.
str = str .. patt
for i = 1, string_len(str) do
local cha = string_sub(str, i, i)
if cha ~= patt then
word = word .. cha
else
if word ~= nil then
vals[valindex] = word
valindex = valindex + 1
word = ""
else
-- in case we get a line with no data.
break
end
end
end
return vals
end
function Misspelled:tprint (t, indent, done)
-- in case we run it standalone outside of the Wow client Lua env
local Note = Note or print
--local Tell = Tell or io.write
-- show strings differently to distinguish them from numbers
local function show (val)
if type (val) == "string" then
return '"' .. val .. '"'
else
return tostring(val)
end -- if
end -- show
-- entry point here
done = done or {}
indent = indent or 0
for key, value in pairs (t) do
print(string_rep(" ", indent)) -- indent it
if type (value) == "table" and not done [value] then
done [value] = true
Note (show (key), ":");
Misspelled:tprint(value, indent + 2, done)
else
print(show (key), "=")
print (show (value))
end
end
end
function Misspelled:print(...)
local frame = SELECTED_DOCK_FRAME or DEFAULT_CHAT_FRAME
if frame then frame:AddMessage(...) end
end
--Check your friends list to see if the string: name is on the friends list.
function Misspelled:IsFriend(name)
local numFriends
if GetNumFriends then
numFriends = GetNumFriends()
if numFriends > 0 then
for i = 1, numFriends do
local friendName
if C_FriendList and C_FriendList.GetFriendInfoByIndex then
local friend = C_FriendList.GetFriendInfoByIndex(i); friendName = friend and friend.name
else
friendName = GetFriendInfo(i)
end
if name == friendName then
return true
end
end
end
end
return false
end
--check the guild roster to see if the string: name, is a guild member.
function Misspelled:IsGuildMember(name)
local numGuildMembers
if IsInGuild() then
numGuildMembers = GetNumGuildMembers(true); -- true to include offline members
if ( numGuildMembers > 0 ) then
for i=1, numGuildMembers do
if name == GetGuildRosterInfo(i) then
return true
end
end
end
end
return false
end
-------------------------------------------------------------------------
-- End: Utility Routines
-------------------------------------------------------------------------
--[[
--testing
require("bit")
require("WordDict")
--~ require("Dict\\Dic_en_US")
--~ Misspelled_DB = {}
--~ WordDict:Init()
--~ ChatFrameEditBox = {}
--~ function ChatFrameEditBox:GetText()
--~ return "Leatherworking"
--~ end
s = "Test |cffff91c8A|cffxxxxxxBSecondWord|r|r"
s2 = string.gsub(s, SPELLED_WRONG_HIGHLIGHT .. "(.-)|r", function(x) return x end)
print(s2)
---]]