Librarian = ZO_SortFilterList:Subclass() Librarian.defaults = {} Librarian.menuSettings = { name = "LibrarianOptions", authors = "Orionik, |c4EFFF6Calia1120|r, Flamage", version = "3.0", optionSlashCommandText = "/librarianOptions" } Librarian.slashCommandText = "/librarian" local SORT_ARROW_UP = "EsoUI/Art/Miscellaneous/list_sortUp.dds" local SORT_ARROW_DOWN = "EsoUI/Art/Miscellaneous/list_sortDown.dds" local LIBRARIAN_DATA = 1 local LIBRARIAN_SEARCH = 1 local ENTRY_SORT_KEYS = { ["title"] = { }, ["unread"] = { tiebreaker = "timeStamp" }, ["timeStamp"] = { tiebreaker = "title" }, ["wordCount"] = { tiebreaker = "title" } } function Librarian:New() return ZO_SortFilterList.New(self, LibrarianFrame) end function Librarian:Initialize(...) ZO_SortFilterList.Initialize(self, ...) self.masterList = {} self.newBookCount = 0 self.sortHeaderGroup:SelectHeaderByKey("timeStamp") ZO_ScrollList_AddDataType(self.list, LIBRARIAN_DATA, "LibrarianBookRow", 30, function(control, data) self:SetupBookRow(control, data) end) ZO_ScrollList_EnableHighlight(self.list, "ZO_ThinListHighlight") self.localSavedVars = ZO_SavedVars:New("Librarian_SavedVariables", 1, nil, self.defaults, nil) self.globalSavedVars = ZO_SavedVars:NewAccountWide("Librarian_SavedVariables", 1, nil, self.defaults, nil) if not self.globalSavedVars.settings then self.globalSavedVars.settings = {} end self.settings = self.globalSavedVars.settings if not self.globalSavedVars.books then self.globalSavedVars.books = {} end self.books = self.globalSavedVars.books if not self.localSavedVars.characterBooks then self.localSavedVars.characterBooks = {} end self.characterBooks = self.localSavedVars.characterBooks self.characterBooksCache = {} self.unreadPerCollections = self.localSavedVars.unreadPerCollections self.searchBox = GetControl(LibrarianFrame, "SearchBox") self.searchBox:SetHandler("OnTextChanged", function() self:OnSearchTextChanged() end) self.search = ZO_StringSearch:New() self.search:AddProcessor(LIBRARIAN_SEARCH, function(stringSearch, data, searchTerm, cache) return self:ProcessBookEntry(stringSearch, data, searchTerm, cache) end) self.sortFunction = function(listEntry1, listEntry2) return ZO_TableOrderingFunction(listEntry1.data, listEntry2.data, self.currentSortKey, ENTRY_SORT_KEYS, self.currentSortOrder) end self:UpdateSavedVariables() local librarianSettings = LibrarianSettings:New(self.settings, self.menuSettings) local function OnShowAllBooksClicked(checkButton, isChecked) self.settings.showAllBooks = isChecked self:RefreshFilters() end local function GetShowAllBooks() return self.settings.showAllBooks end local showAllBooks = LibrarianFrameShowAllBooks ZO_CheckButton_SetToggleFunction(showAllBooks, OnShowAllBooksClicked) ZO_CheckButton_SetCheckState(showAllBooks, GetShowAllBooks()) self:RefreshData() self:InitializeKeybindStripDescriptors() self:InitializeScene() self:AddLoreReaderUnreadToggle() self:AddLoreLibraryIcons() self:ImportFromLoreLibrary() end function Librarian:AddLoreReaderUnreadToggle() local readerKeybinds if LORE_READER.keybindStripDescriptor then readerKeybinds = LORE_READER.keybindStripDescriptor else readerKeybinds = LORE_READER.PCKeybindStripDescriptor end if readerKeybinds then local toggleKeybind = { alignment = KEYBIND_STRIP_ALIGN_RIGHT, name = function() local book = self:FindBook(LORE_READER.titleText) if not book or book.unread then if self.settings.showUnreadIndicatorInReader then self.loreReaderUnreadIndicator:SetHidden(false) else self.loreReaderUnreadIndicator:SetHidden(true) end return GetString(LIBRARIAN_MARK_READ) else self.loreReaderUnreadIndicator:SetHidden(true) return GetString(LIBRARIAN_MARK_UNREAD) end end, keybind = "UI_SHORTCUT_SECONDARY", callback = function() local book = self:FindBook(LORE_READER.titleText) if not book then return end self:ToggleReadBook(book) KEYBIND_STRIP:UpdateKeybindButtonGroup(readerKeybinds) self:RefreshAllData() end } table.insert(readerKeybinds, toggleKeybind) end self.loreReaderUnreadIndicator = WINDOW_MANAGER:CreateControl("LibrarianLoreReaderUnreadIndicator", ZO_LoreReaderBookContainer, CT_TEXTURE) self.loreReaderUnreadIndicator:SetAnchor(TOPLEFT, ZO_LoreReaderBookContainerFirstPage, TOPLEFT, -32, 3) self.loreReaderUnreadIndicator:SetAlpha(self.settings.unreadIndicatorTransparency) self.loreReaderUnreadIndicator:SetDimensions(32, 32) self.loreReaderUnreadIndicator:SetHidden(true) self.loreReaderUnreadIndicator:SetTexture("esoui/art/miscellaneous/new_icon.dds") end function Librarian:AddLoreLibraryIcons() if not self.settings.showUnreadIndicatorInLoreLibrary then return end -- ADD UNREAD ICON ON EACH ENTRY UNREAD local BOOK_DATA_TYPE = 1 local scrollList = LORE_LIBRARY.list:GetListControl() local initalDataType = scrollList.dataTypes[BOOK_DATA_TYPE] scrollList.dataTypes[BOOK_DATA_TYPE] = nil local function SetUpBookEntry(control, data) initalDataType.setupCallback(control, data) local title, _, known = GetLoreBookInfo(data.categoryIndex, data.collectionIndex, data.bookIndex) local shouldUnreadIconBeHidden = true if known then local book = self:FindBook(title) if not book or book.unread then shouldUnreadIconBeHidden = false end end control:GetNamedChild("UnreadIcon"):SetHidden(shouldUnreadIconBeHidden) end ZO_ScrollList_AddDataType(scrollList, BOOK_DATA_TYPE, "Librarian_LoreLibrary_BookEntry", initalDataType.height, SetUpBookEntry) -- ORDER BOOK LIST TO MAKE UNREAD BOOK FIRST local function BookEntryComparator(leftScrollData, rightScrollData) local leftData = leftScrollData.data local rightData = rightScrollData.data local leftTitle, _, leftKnown = GetLoreBookInfo(leftData.categoryIndex, leftData.collectionIndex, leftData.bookIndex) local rightTitle, _, rightKnown = GetLoreBookInfo(rightData.categoryIndex, rightData.collectionIndex, rightData.bookIndex) if leftKnown == rightKnown then if leftKnown then local leftBook = self:FindBook(leftTitle) local rightBook = self:FindBook(rightTitle) if (leftBook and not leftBook.unread) == (rightBook and not rightBook.unread) then return leftTitle < rightTitle end return leftBook and leftBook.unread end end return leftKnown end LORE_LIBRARY.list.SortScrollList = function(loreLibrarySelf) local scrollData = ZO_ScrollList_GetDataList(loreLibrarySelf.list) table.sort(scrollData, BookEntryComparator) end -- ADD KEYBIND TO MARK BOOK AS READ FROM LORE LIBRARY local loreLibraryKeybinds = LORE_LIBRARY.keybindStripDescriptor -- Make current secondary tertiary for i, keybindDescriptor in ipairs(loreLibraryKeybinds) do if keybindDescriptor.keybind == "UI_SHORTCUT_SECONDARY" then keybindDescriptor.keybind = "UI_SHORTCUT_TERTIARY" end end local toggleKeybind = { alignment = KEYBIND_STRIP_ALIGN_RIGHT, name = function() local selectedRow = LORE_LIBRARY.list:GetMouseOverRow() if selectedRow and selectedRow.known then local book = self:FindBook(selectedRow.text:GetText()) if not book or book.unread then return GetString(LIBRARIAN_MARK_READ) else return GetString(LIBRARIAN_MARK_UNREAD) end end end, keybind = "UI_SHORTCUT_SECONDARY", visible = function() local selectedRow = LORE_LIBRARY.list:GetMouseOverRow() return selectedRow and selectedRow.known end, callback = function() local selectedRow = LORE_LIBRARY.list:GetMouseOverRow() if selectedRow then local book = self:FindBook(selectedRow.text:GetText()) if not book then local bookId = select(4, GetLoreBookInfo(selectedRow.categoryIndex, selectedRow.collectionIndex, selectedRow.bookIndex)) local body, medium, showTitle = ReadLoreBook(selectedRow.categoryIndex, selectedRow.collectionIndex, selectedRow.bookIndex) book = { title = selectedRow.text:GetText(), body = body, medium = medium, showTitle = showTitle, bookId = bookId } self:AddBook(book, true) end if book then self:ToggleReadBook(book) KEYBIND_STRIP:UpdateKeybindButtonGroup(loreLibraryKeybinds) self:RefreshAllData() end end end } table.insert(loreLibraryKeybinds, toggleKeybind) -- ADD UNREAD ICON ON COLLECTION IF IT CONTAINS AN UNREAD BOOK local navigationEntryTemplateInfo = LORE_LIBRARY.navigationTree.templateInfo["ZO_LoreLibraryNavigationEntry"] local previousSetupFunction = navigationEntryTemplateInfo.setupFunction local function TreeEntrySetup(node, control, data, open) previousSetupFunction(node, control, data, open) local shouldHideIcon = true local statusIcon = control:GetNamedChild("StatusIcon") if data.numKnownBooks > 0 then if self.unreadPerCollections[data.categoryIndex] and self.unreadPerCollections[data.categoryIndex][data.collectionIndex] then if self.unreadPerCollections[data.categoryIndex][data.collectionIndex] > 0 then shouldHideIcon = false statusIcon:SetTexture("esoui/art/miscellaneous/new_icon.dds") elseif data.numKnownBooks == data.totalBooks then shouldHideIcon = false statusIcon:SetTexture("esoui/art/miscellaneous/check.dds") end end end statusIcon:SetHidden(shouldHideIcon) end navigationEntryTemplateInfo.setupFunction = TreeEntrySetup navigationEntryTemplateInfo.template = "Librarian_LoreLibraryNavigationEntry" navigationEntryTemplateInfo.objectPool.templateName = "Librarian_LoreLibraryNavigationEntry" -- Fix of ESO because a label header is equal to all of its entries with the current code (API 101033) LORE_LIBRARY.navigationTree.templateInfo["ZO_LabelHeader"].equalityFunction = navigationEntryTemplateInfo.equalityFunction end function Librarian:UpdateSavedVariables() -- before version 3.0, unreadPerCollections didn't exist and bookId wasn't set if not self.unreadPerCollections then self.localSavedVars.unreadPerCollections = {} self.unreadPerCollections = self.localSavedVars.unreadPerCollections -- if this character doesn't know any book yet, no need to go further if next(self.characterBooks) ~= nil then local GetLoreBookInfo, ReadLoreBook = GetLoreBookInfo, ReadLoreBook local categoryIndex, collectionIndex, bookIndex = 0, 0, 0 local numCollections, totalBooks = 0, 0 while true do if bookIndex >= totalBooks then if collectionIndex >= numCollections then if categoryIndex >= GetNumLoreCategories() then break else categoryIndex = categoryIndex + 1 local loreCategoryName numCollections = select(2, GetLoreCategoryInfo(categoryIndex)) collectionIndex = 0 end end collectionIndex = collectionIndex + 1 totalBooks = select(4, GetLoreCollectionInfo(categoryIndex, collectionIndex)) bookIndex = 0 end bookIndex = bookIndex + 1 local title, icon, known, bookId = GetLoreBookInfo(categoryIndex, collectionIndex, bookIndex) book = self:FindBook(title) if book then book.bookId = bookId if known and book.unread and self:FindCharacterBook(title, bookId) then self:AddUnreadBookInCollection(categoryIndex, collectionIndex) end end end end end end function Librarian:InitializeKeybindStripDescriptors() self.keybindStripDescriptor = { { alignment = KEYBIND_STRIP_ALIGN_RIGHT, name = GetString(SI_LORE_LIBRARY_READ), keybind = "UI_SHORTCUT_PRIMARY", visible = function() return self.mouseOverRow end, callback = function() self:ReadBook(self.mouseOverRow.data.title) end, }, { alignment = KEYBIND_STRIP_ALIGN_RIGHT, name = function() if not self.mouseOverRow then return nil end local book = self:FindBook(self.mouseOverRow.data.title) if book.unread then return GetString(LIBRARIAN_MARK_READ) else return GetString(LIBRARIAN_MARK_UNREAD) end end, keybind = "UI_SHORTCUT_SECONDARY", visible = function() return self.mouseOverRow end, callback = function() local book = self:FindBook(self.mouseOverRow.data.title) self:ToggleReadBook(book) self:RefreshAllData() end, } } end function Librarian:InitializeScene() if not LIBRARIAN_SCENE then LIBRARIAN_TITLE_FRAGMENT = ZO_SetTitleFragment:New(LIBRARIAN_WINDOW_TITLE_LIBRARIAN) LIBRARIAN_SCENE = ZO_Scene:New("librarian", SCENE_MANAGER) LIBRARIAN_SCENE:AddFragmentGroup(FRAGMENT_GROUP.MOUSE_DRIVEN_UI_WINDOW) if self.settings.enableCharacterSpin then LIBRARIAN_SCENE:AddFragmentGroup(FRAGMENT_GROUP.FRAME_TARGET_STANDARD_RIGHT_PANEL) end LIBRARIAN_SCENE:AddFragment(ZO_FadeSceneFragment:New(LibrarianFrame)) LIBRARIAN_SCENE:AddFragment(RIGHT_BG_FRAGMENT) LIBRARIAN_SCENE:AddFragment(TITLE_FRAGMENT) LIBRARIAN_SCENE:AddFragment(LIBRARIAN_TITLE_FRAGMENT) LIBRARIAN_SCENE:AddFragment(CODEX_WINDOW_SOUNDS) LIBRARIAN_SCENE:RegisterCallback("StateChange", function(oldState, newState) if(newState == SCENE_SHOWING) then KEYBIND_STRIP:AddKeybindButtonGroup(self.keybindStripDescriptor) elseif(newState == SCENE_HIDDEN) then KEYBIND_STRIP:RemoveKeybindButtonGroup(self.keybindStripDescriptor) end end) end end function Librarian:ImportFromLoreLibrary() local hasImportedBooks = false local chatEnabled = self.settings.chatEnabled local alertEnabled = self.settings.alertEnabled self.settings.chatEnabled = true self.settings.alertEnabled = false local identifier = "LibrarianLoreImport" local GetLoreCategoryInfo, GetLoreCollectionInfo = GetLoreCategoryInfo, GetLoreCollectionInfo local GetLoreBookInfo, ReadLoreBook = GetLoreBookInfo, ReadLoreBook local em = GetEventManager() local function endSearch() em:UnregisterForUpdate(identifier) self.settings.chatEnabled = chatEnabled self.settings.alertEnabled = alertEnabled if hasImportedBooks then self:RefreshAllData() end end local categoryIndex, collectionIndex, bookIndex = 0, 0, 0 local numCollections, totalBooks = 0, 0 local function step() if bookIndex >= totalBooks then if collectionIndex >= numCollections then if categoryIndex >= GetNumLoreCategories() then endSearch() return false else categoryIndex = categoryIndex + 1 local loreCategoryName numCollections = select(2, GetLoreCategoryInfo(categoryIndex)) collectionIndex = 0 end end collectionIndex = collectionIndex + 1 totalBooks = select(4, GetLoreCollectionInfo(categoryIndex, collectionIndex)) bookIndex = 0 end bookIndex = bookIndex + 1 local title, icon, known, bookId = GetLoreBookInfo(categoryIndex, collectionIndex, bookIndex) if known then if not self:FindCharacterBook(title, bookId) then local body, medium, showTitle = ReadLoreBook(categoryIndex, collectionIndex, bookIndex) local book = { title = title, body = body, medium = medium, showTitle = showTitle, bookId = bookId } self:AddBook(book, false) hasImportedBooks = true end end return true end local function steps() local gettime = GetGameTimeMilliseconds local start = gettime() -- do as much as possible in 5ms while true do if ((gettime() - start) >= 5) or not step() then break end end end em:RegisterForUpdate(identifier, 0, steps) end function Librarian:BuildMasterList() for i, book in ipairs(self.books) do local data = {} for k,v in pairs(book) do if k == "body" then data[k] = table.concat(book.body) else data[k] = v end end data.type = LIBRARIAN_SEARCH local characterBook = self:FindCharacterBook(book.title, book.bookId) if characterBook then data.seenByCurrentCharacter = true data.timeStamp = characterBook.timeStamp else data.seenByCurrentCharacter = false data.timeStamp = book.timeStamp end self.masterList[i] = data end end function Librarian:FilterScrollList() local scrollData = ZO_ScrollList_GetDataList(self.list) ZO_ClearNumericallyIndexedTable(scrollData) local bookCount = 0 local unreadCount = 0 local searchTerm = self.searchBox:GetText() for i = 1, #self.masterList do local data = self.masterList[i] if self.settings.showAllBooks or data.seenByCurrentCharacter then if(searchTerm == "" or self.search:IsMatch(searchTerm, data)) then table.insert(scrollData, ZO_ScrollList_CreateDataEntry(LIBRARIAN_DATA, data)) bookCount = bookCount + 1 if data.unread then unreadCount = unreadCount + 1 end end end end local message = string.format(GetString(LIBRARIAN_BOOK_COUNT), bookCount) if unreadCount > 0 then message = string.format(GetString(LIBRARIAN_UNREAD_COUNT), message, unreadCount) end LibrarianFrameBookCount:SetText(message) end function Librarian:SortScrollList() local scrollData = ZO_ScrollList_GetDataList(self.list) table.sort(scrollData, self.sortFunction) end function Librarian:SetupBookRow(control, data) control.data = data control.unread = GetControl(control, "Unread") control.found = GetControl(control, "Found") control.title = GetControl(control, "Title") control.wordCount = GetControl(control, "WordCount") control.unread.nonRecolorable = true if data.unread then control.unread:SetAlpha(1) else control.unread:SetAlpha(0) end control.found.normalColor = ZO_NORMAL_TEXT control.found:SetText(self:FormatClockTime(data.timeStamp)) control.title.normalColor = ZO_NORMAL_TEXT control.title:SetText(data.title) control.wordCount.normalColor = ZO_NORMAL_TEXT control.wordCount:SetText(data.wordCount) ZO_SortFilterList.SetupRow(self, control, data) end function Librarian:ProcessBookEntry(stringSearch, data, searchTerm, cache) local lowerSearchTerm = searchTerm:lower() if data.title and zo_plainstrfind(data.title:lower(), lowerSearchTerm) then return true end if data.body and zo_plainstrfind(data.body:lower(), lowerSearchTerm) then return true end return false end function Librarian:FindCharacterBook(title, bookId) if not self.characterBooks then return nil end if bookId and self.characterBooksCache[bookId] then return self.characterBooksCache[bookId] end for _,book in pairs(self.characterBooks) do if book.title == title then if bookId then self.characterBooksCache[bookId] = book end return book end end return nil end function Librarian:FindBook(title) for _,book in pairs(self.books) do if book.title == title then return book end end end function Librarian:AddBook(book, refreshDataRightAway) if not self:FindCharacterBook(book.title, book.bookId) then if not self:FindBook(book.title) then book.timeStamp = GetTimeStamp() book.unread = true local wordCount = 0 for w in book.body:gmatch("%S+") do wordCount = wordCount + 1 end book.wordCount = wordCount local newBody = book.body book.body = {} while string.len(newBody) > 1024 do table.insert(book.body, string.sub(newBody, 0, 1024)) newBody = string.sub(newBody, 1025) end table.insert(book.body, newBody) table.insert(self.books, book) end local characterBook = { title = book.title, timeStamp = GetTimeStamp() } table.insert(self.characterBooks, characterBook) if book.unread then local categoryIndex, collectionIndex = GetLoreBookIndicesFromBookId(book.bookId) self:AddUnreadBookInCollection(categoryIndex, collectionIndex) end if refreshDataRightAway then self:RefreshAllData() end if self.settings.alertEnabled then --CENTER_SCREEN_ANNOUNCE:AddMessage(EVENT_SKILL_RANK_UPDATE, CSA_EVENT_LARGE_TEXT, SOUNDS.BOOK_ACQUIRED, GetString(SI_LIBRARIAN_NEW_BOOK_FOUND)) local params = CENTER_SCREEN_ANNOUNCE:CreateMessageParams(CSA_EVENT_LARGE_TEXT, SOUNDS.BOOK_ACQUIRED) params:SetCSAType(CENTER_SCREEN_ANNOUNCE_TYPE_DISPLAY_ANNOUNCEMENT ) params:SetText(GetString(LIBRARIAN_NEW_BOOK_FOUND)) CENTER_SCREEN_ANNOUNCE:AddMessageWithParams(params) end if self.settings.chatEnabled then d(string.format(GetString(LIBRARIAN_NEW_BOOK_FOUND_WITH_TITLE), book.title)) end self.newBookCount = self.newBookCount + 1 if self.settings.reloadReminderBookCount and self.settings.reloadReminderBookCount > 0 and self.settings.reloadReminderBookCount <= self.newBookCount then d(GetString(LIBRARIAN_RELOAD_REMINDER)) end end end function Librarian:AddUnreadBookInCollection(categoryIndex, collectionIndex) if not self.unreadPerCollections[categoryIndex] then self.unreadPerCollections[categoryIndex] = {} end if not self.unreadPerCollections[categoryIndex][collectionIndex] then self.unreadPerCollections[categoryIndex][collectionIndex] = 0 end self.unreadPerCollections[categoryIndex][collectionIndex] = self.unreadPerCollections[categoryIndex][collectionIndex] + 1 end function Librarian:ToggleReadBook(book) book.unread = not book.unread local categoryIndex, collectionIndex = GetLoreBookIndicesFromBookId(book.bookId) if categoryIndex and collectionIndex then if not self.unreadPerCollections[categoryIndex] then self.unreadPerCollections[categoryIndex] = {} end if not self.unreadPerCollections[categoryIndex][collectionIndex] then self.unreadPerCollections[categoryIndex][collectionIndex] = 0 end if book.unread then self.unreadPerCollections[categoryIndex][collectionIndex] = self.unreadPerCollections[categoryIndex][collectionIndex] + 1 elseif self.unreadPerCollections[categoryIndex][collectionIndex] > 0 then self.unreadPerCollections[categoryIndex][collectionIndex] = self.unreadPerCollections[categoryIndex][collectionIndex] - 1 end end end function Librarian:Toggle() if LibrarianFrame:IsControlHidden() then SCENE_MANAGER:Show("librarian") else SCENE_MANAGER:Hide("librarian") end end function Librarian:ReadBook(title) local book = self:FindBook(title) LORE_READER:SetupBook(book.title, book.body and table.concat(book.body), book.medium, book.showTitle) SCENE_MANAGER:Push("loreReaderInteraction") -- PlaySound(LORE_READER.OpenSound) end function Librarian:RefreshAllData() self:RefreshData() if self.settings.showUnreadIndicatorInLoreLibrary then local selectedCategoryIndex = LORE_LIBRARY:GetSelectedCategoryIndex() local selectedCollectionIndex = LORE_LIBRARY:GetSelectedCollectionIndex() local selectedCollectionId = select(7, GetLoreCollectionInfo(selectedCategoryIndex, selectedCollectionIndex)) LORE_LIBRARY:SetCollectionIdToSelect(selectedCollectionId) LORE_LIBRARY:BuildCategoryList() end end function Librarian:FormatClockTime(time) local midnightSeconds = GetSecondsSinceMidnight() local utcSeconds = GetTimeStamp() % 86400 local offset = midnightSeconds - utcSeconds if offset < -43200 then offset = offset + 86400 end local dateString = GetDateStringFromTimestamp(time) local timeString = ZO_FormatTime((time + offset) % 86400, TIME_FORMAT_STYLE_CLOCK_TIME, self.settings.timeFormat) return string.format("%s %s", dateString, timeString) end function Librarian:OnSearchTextChanged() ZO_EditDefaultText_OnTextChanged(self.searchBox) -- Let's wait a little bit before updating the filters if the player didn't have time to enter its full string yet -- Because if we try to update the filters too much it sometimes crash local TIME_BEFORE_UPDATING_FILTERS = 600 -- ms self.lastTimeSearchModified = GetGameTimeMilliseconds() local function TryRefreshFilters() if self.lastTimeSearchModified + TIME_BEFORE_UPDATING_FILTERS <= GetGameTimeMilliseconds() then self:RefreshFilters() end end zo_callLater(TryRefreshFilters, TIME_BEFORE_UPDATING_FILTERS) end function Librarian.SlashCommand(args) Librarian:Toggle() end function Librarian.OnShowBook(eventCode, title, body, medium, showTitle, bookId) local book = { title = title, body = body, medium = medium, showTitle = showTitle, bookId = bookId } LIBRARIAN:AddBook(book, true) end function Librarian.OnMouseEnterRow(control) LIBRARIAN:Row_OnMouseEnter(control) end function Librarian.OnMouseExitRow(control) LIBRARIAN:Row_OnMouseExit(control) end function Librarian.OnMouseUpRow(control, button, upInside) LIBRARIAN:ReadBook(control.data.title) end function Librarian.OnAddonLoaded(event, addon) if addon == "Librarian" then EVENT_MANAGER:UnregisterForEvent("Librarian", EVENT_ADD_ON_LOADED) LIBRARIAN = Librarian:New() EVENT_MANAGER:RegisterForEvent("Librarian", EVENT_SHOW_BOOK, Librarian.OnShowBook) end end SLASH_COMMANDS[Librarian.slashCommandText] = Librarian.SlashCommand EVENT_MANAGER:RegisterForEvent("Librarian", EVENT_ADD_ON_LOADED, Librarian.OnAddonLoaded)