From 5ef7a656c6e54c553cb2ab8082466bd66be2241f Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Tue, 15 Dec 2015 15:33:32 +0000 Subject: [PATCH] Rewrite filters.js, and related plugin.js refactoring filters.js now only exports a class that can be used to track filter state, and contains functions for filtering using Array.prototype.filter(). PluginCardContent is a new class for the data that gets displayed on the front of plugin cards. It uses the filters to decide what is visible and also converts the message data structure into something a little simpler. Plugin.modPriority and Plugin.isGlobalPriority have been renamed to Plugin.priority and Plugin.isPriorityGlobal as they are more sensible names. I've also moved the filter state memory functions out of filters.js as they're more settings-related. I've also moved the conflicting plugins CEF callback and the filter application function out, because they were to complex/tightly coupled to other components to stay in there. I expect this breaks code that Plugin and the filters are used in. * The filters.hiddenPluginNo and filters.hiddenMessageNo have not yet been re-implemented and are currently missing. Given the complexity of the filtering, I'm thinking to just calculate their values at the end of a filter's event handler rather than tracking their state. * Since filters.js now exports a class, loot.filters is no longer initialised, * Getting conflicting plugins is also broken, as it should now be done in the conflict filter event handler, but that hasn't been updated. --- .eslintrc.yml | 1 + src/gui/handler.cpp | 10 +- src/gui/html/elements/loot-plugin-card.html | 22 +- src/gui/html/elements/loot-plugin-editor.html | 42 +- src/gui/html/elements/loot-plugin-item.html | 10 +- src/gui/html/elements/loot-search.html | 31 +- src/gui/html/index.html | 16 +- src/gui/html/js/events.js | 50 +- src/gui/html/js/filters.js | 285 ++------- src/gui/html/js/helpers.js | 71 +++ src/gui/html/js/init.js | 47 ++ src/gui/html/js/plugin.js | 234 ++++--- src/tests/gui/html/js/test.html | 2 + src/tests/gui/html/js/test_filters.js | 206 ++++++ src/tests/gui/html/js/test_plugin.js | 589 +++++++++++++++--- 15 files changed, 1134 insertions(+), 482 deletions(-) create mode 100644 src/tests/gui/html/js/test_filters.js diff --git a/.eslintrc.yml b/.eslintrc.yml index 9828dd94..9e3a5da1 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -10,6 +10,7 @@ ecmaFeatures: globals: loot: false + should: false extends: - "eslint:recommended" diff --git a/src/gui/handler.cpp b/src/gui/handler.cpp index 8e592da0..c1bbf9b8 100644 --- a/src/gui/handler.cpp +++ b/src/gui/handler.cpp @@ -488,12 +488,12 @@ namespace loot { // First sort out the priority value. This is only given if it was changed. BOOST_LOG_TRIVIAL(trace) << "Calculating userlist metadata priority value from Javascript variables."; - if (pluginMetadata["modPriority"] && pluginMetadata["isGlobalPriority"]) { + if (pluginMetadata["priority"] && pluginMetadata["isPriorityGlobal"]) { BOOST_LOG_TRIVIAL(trace) << "Priority value was changed, recalculating..."; // Priority value was changed, so add it to the userlist data. - newUserlistEntry.Priority(pluginMetadata["modPriority"].as()); + newUserlistEntry.Priority(pluginMetadata["priority"].as()); newUserlistEntry.SetPriorityExplicit(true); - newUserlistEntry.SetPriorityGlobal(pluginMetadata["isGlobalPriority"].as()); + newUserlistEntry.SetPriorityGlobal(pluginMetadata["isPriorityGlobal"].as()); } else { // Priority value wasn't changed, use the existing userlist value. @@ -1056,8 +1056,8 @@ namespace loot { // Now add to pluginNode. YAML::Node pluginNode; pluginNode["name"] = tempPlugin.Name(); - pluginNode["modPriority"] = tempPlugin.Priority(); - pluginNode["isGlobalPriority"] = tempPlugin.IsPriorityGlobal(); + pluginNode["priority"] = tempPlugin.Priority(); + pluginNode["isPriorityGlobal"] = tempPlugin.IsPriorityGlobal(); pluginNode["messages"] = tempPlugin.Messages(); pluginNode["tags"] = tempPlugin.Tags(); pluginNode["isDirty"] = isDirty; diff --git a/src/gui/html/elements/loot-plugin-card.html b/src/gui/html/elements/loot-plugin-card.html index c2959880..c8ddd05a 100644 --- a/src/gui/html/elements/loot-plugin-card.html +++ b/src/gui/html/elements/loot-plugin-card.html @@ -257,6 +257,10 @@ loot-clear-metadata this.classList.toggle('flip'); } + /* Update version and CRC text */ + this.getElementsByClassName('version')[0].textContent = this.data.getCardContent(loot.filters).version; + this.getElementsByClassName('crc')[0].textContent = this.data.getCardContent(loot.filters).crc; + /* Also initialise the non-simple-string data. */ this.onTagsChange(); this.onMessagesChange(); @@ -278,8 +282,13 @@ loot-clear-metadata onTagsChange: function(oldValue, newValue) { if (this.data) { - this.getElementsByClassName('tag add')[0].classList.toggle('hidden', this.data.tagStrings.added.length == 0); - this.getElementsByClassName('tag remove')[0].classList.toggle('hidden', this.data.tagStrings.removed.length == 0); + var cardContent = this.data.getCardContent(loot.filters); + var tagsAdded = this.getElementsByClassName('tag add')[0]; + tagsAdded.textContent = cardContent.tags.added; + tagsAdded.classList.toggle('hidden', cardContent.tags.added.length == 0); + var tagsRemoved = this.getElementsByClassName('tag remove')[0]; + tagsRemoved.textContent = cardContent.tags.removed; + tagsRemoved.classList.toggle('hidden', cardContent.tags.removed.length == 0); } }, @@ -291,16 +300,15 @@ loot-clear-metadata messageUL.removeChild(messageUL.firstElementChild); } /* Now add new messages. */ - var visibleMessages = filters.applyMessageFilters(this.data.messages); - visibleMessages.forEach(function(message) { + var cardContent = this.data.getCardContent(loot.filters); + cardContent.messages.forEach(function(message) { var messageLi = document.createElement('li'); messageLi.className = message.type; // Use the Marked library for Markdown formatting support. - messageLi.innerHTML = marked(message.content[0].str); + messageLi.innerHTML = marked(message.content); messageUL.appendChild(messageLi); - }); - messageUL.classList.toggle('hidden', visibleMessages.length == 0); + messageUL.classList.toggle('hidden', cardContent.messages.length == 0); } }, diff --git a/src/gui/html/elements/loot-plugin-editor.html b/src/gui/html/elements/loot-plugin-editor.html index cbefbbb0..0ff7eb92 100644 --- a/src/gui/html/elements/loot-plugin-editor.html +++ b/src/gui/html/elements/loot-plugin-editor.html @@ -125,8 +125,8 @@ loot-editor-close

{{data.name}}

- {{data.version}} - {{data.crcString}} + +
@@ -250,8 +250,8 @@ loot-editor-close data: undefined, observe: { - 'data.isGlobalPriority': 'onPriorityChange', - 'data.modPriority': 'onPriorityChange', + 'data.isPriorityGlobal': 'onPriorityChange', + 'data.priority': 'onPriorityChange', 'data.userlist': 'onDataCacheChange', }, @@ -318,10 +318,10 @@ loot-editor-close /* If either of the priority values have been changed, the base priority value they're derived from will have changed, so record both. */ - if (this.shadowRoot.getElementById('globalPriority').checked != oldData.isGlobalPriority - || this.shadowRoot.getElementById('priorityValue').value != oldData.modPriority) { - plugin.isGlobalPriority = this.shadowRoot.getElementById('globalPriority').checked; - plugin.modPriority = this.shadowRoot.getElementById('priorityValue').value; + if (this.shadowRoot.getElementById('globalPriority').checked != oldData.isPriorityGlobal + || this.shadowRoot.getElementById('priorityValue').value != oldData.priority) { + plugin.isPriorityGlobal = this.shadowRoot.getElementById('globalPriority').checked; + plugin.priority = this.shadowRoot.getElementById('priorityValue').value; /* Also mark the priority as being explicitly set in the userlist. */ plugin.userlist.hasExplicitPriority = true; } @@ -349,7 +349,7 @@ loot-editor-close plugin.userlist.msg = rowsData; } else if (tables[j].parentElement.id == 'tags') { rowsData.forEach(function(value, index, arr){ - arr[index] = Plugin.tagFromRowData(value); + arr[index] = loot.Plugin.tagFromRowData(value); }); plugin.userlist.tag = rowsData; } else if (tables[j].parentElement.id == 'dirty') { @@ -391,29 +391,33 @@ loot-editor-close that for userlist and priority data if so. */ var tempData = {}; if (newData.editor) { - if (newData.editor.isGlobalPriority) { - tempData.isGlobalPriority = newData.editor.isGlobalPriority; + if (newData.editor.isPriorityGlobal) { + tempData.isPriorityGlobal = newData.editor.isPriorityGlobal; } - if (newData.editor.modPriority) { - tempData.modPriority = newData.editor.modPriority; + if (newData.editor.priority) { + tempData.priority = newData.editor.priority; } tempData.userlist = newData.editor.userlist; } else { tempData = { - isGlobalPriority: newData.isGlobalPriority, - modPriority: newData.modPriority, + isPriorityGlobal: newData.isPriorityGlobal, + priority: newData.priority, userlist: newData.userlist } } + /* Fill in the version and CRC values. */ + this.shadowRoot.getElementById('version').textContent = newData.getCardContent(loot.filters).version; + this.shadowRoot.getElementById('crc').textContent = newData.getCardContent(loot.filters).crc; + /* Fill in the editor input values. */ if (tempData.userlist && !tempData.userlist.enabled) { this.shadowRoot.getElementById('enableEdits').checked = false; } else { this.shadowRoot.getElementById('enableEdits').checked = true; } - this.shadowRoot.getElementById('globalPriority').checked = tempData.isGlobalPriority; - this.shadowRoot.getElementById('priorityValue').value = tempData.modPriority; + this.shadowRoot.getElementById('globalPriority').checked = tempData.isPriorityGlobal; + this.shadowRoot.getElementById('priorityValue').value = tempData.priority; /* Clear then fill in editor table data. Masterlist-originated rows should have their contents made read-only. */ @@ -493,14 +497,14 @@ loot-editor-close if (newData.masterlist && newData.masterlist.tag) { newData.masterlist.tag.forEach(function(tag) { - var tagData = Plugin.tagToRowData(tag); + var tagData = loot.Plugin.tagToRowData(tag); var row = tables[j].addRow(tagData); tables[j].setReadOnly(row); }, newData); } if (tempData.userlist && tempData.userlist.tag) { tempData.userlist.tag.forEach(function(tag) { - var tagData = Plugin.tagToRowData(tag); + var tagData = loot.Plugin.tagToRowData(tag); tables[j].addRow(tagData); }, tempData); } diff --git a/src/gui/html/elements/loot-plugin-item.html b/src/gui/html/elements/loot-plugin-item.html index f2c289de..2cfb7675 100644 --- a/src/gui/html/elements/loot-plugin-item.html +++ b/src/gui/html/elements/loot-plugin-item.html @@ -140,8 +140,8 @@ observe: { 'data.userlist': 'onUserlistChange', - 'data.modPriority': 'onPriorityChange', - 'data.isGlobalPriority': 'onPriorityIsGlobalChange', + 'data.priority': 'onPriorityChange', + 'data.isPriorityGlobal': 'onPriorityIsGlobalChange', 'data.isEditorOpen': 'onEditorStateChange', }, @@ -163,8 +163,8 @@ }, onPriorityChange: function(oldValue, newValue) { - if (this.data && this.data.modPriority) { - this.shadowRoot.getElementById('priority').textContent = this.data.modPriority; + if (this.data && this.data.priority) { + this.shadowRoot.getElementById('priority').textContent = this.data.priority; this.shadowRoot.getElementById('secondary').classList.remove('hidden'); } else { this.shadowRoot.getElementById('priority').textContent = ''; @@ -173,7 +173,7 @@ }, onPriorityIsGlobalChange: function(oldValue, newValue) { - if (this.data && this.data.isGlobalPriority) { + if (this.data && this.data.isPriorityGlobal) { this.shadowRoot.getElementById('secondary').firstElementChild.classList.remove('hidden'); } else { this.shadowRoot.getElementById('secondary').firstElementChild.classList.add('hidden'); diff --git a/src/gui/html/elements/loot-search.html b/src/gui/html/elements/loot-search.html index fc4fa2a2..1fc91aee 100644 --- a/src/gui/html/elements/loot-search.html +++ b/src/gui/html/elements/loot-search.html @@ -135,33 +135,10 @@ searchTarget is the ID of the core-list element to search the elements of. var crcHidden = document.getElementById('hideCRCs').checked; var bashTagHidden = document.getElementById('hideBashTags').checked; document.getElementById(host.searchTarget).data.forEach(function(plugin, index){ - if (plugin.name.toLowerCase().indexOf(needle) != -1 - || (!versionHidden && plugin.version.toLowerCase().indexOf(needle) != -1) - || (!crcHidden && plugin.crcString.toLowerCase().indexOf(needle) != -1)) { - - host.results.push(index); - plugin.isSearchResult = true; - return; - } - - if (!bashTagHidden) { - var tags = plugin.tagStrings; - if (tags.added.toLowerCase().indexOf(needle) != -1 - || tags.removed.toLowerCase().indexOf(needle) != -1) { - - host.results.push(index); - plugin.isSearchResult = true; - return; - } - } - - var visibleMessages = filters.applyMessageFilters(this.data.messages); - for (var i = 0; i < visibleMessages.length; ++i) { - if (visibleMessages[i].content[0].str.toLowerCase().indexOf(needle) != -1) { - host.results.push(index); - plugin.isSearchResult = true; - return; - } + if (plugin.getCardContent(loot.filters).containsText(needle)) { + host.results.push(index); + plugin.isSearchResult = true; + return; } }); diff --git a/src/gui/html/index.html b/src/gui/html/index.html index a3a9873b..277e7c9f 100644 --- a/src/gui/html/index.html +++ b/src/gui/html/index.html @@ -213,10 +213,10 @@ @@ -315,12 +315,12 @@ + + + - - - - + diff --git a/src/gui/html/js/events.js b/src/gui/html/js/events.js index e9bf8bf8..c07d73e8 100644 --- a/src/gui/html/js/events.js +++ b/src/gui/html/js/events.js @@ -111,10 +111,10 @@ function updateMasterlistNoProgress() { for (var i = 0; i < loot.game.plugins.length; ++i) { if (loot.game.plugins[i].name == plugin.name) { loot.game.plugins[i].isDirty = plugin.isDirty; - loot.game.plugins[i].isGlobalPriority = plugin.isGlobalPriority; + loot.game.plugins[i].isPriorityGlobal = plugin.isPriorityGlobal; loot.game.plugins[i].masterlist = plugin.masterlist; loot.game.plugins[i].messages = plugin.messages; - loot.game.plugins[i].modPriority = plugin.modPriority; + loot.game.plugins[i].priority = plugin.priority; loot.game.plugins[i].tags = plugin.tags; break; } @@ -276,8 +276,8 @@ function onClearAllMetadata(evt) { loot.game.plugins[i].userlist = undefined; loot.game.plugins[i].editor = undefined; - loot.game.plugins[i].modPriority = plugin.modPriority; - loot.game.plugins[i].isGlobalPriority = plugin.isGlobalPriority; + loot.game.plugins[i].priority = plugin.priority; + loot.game.plugins[i].isPriorityGlobal = plugin.isPriorityGlobal; loot.game.plugins[i].messages = plugin.messages; loot.game.plugins[i].tags = plugin.tags; loot.game.plugins[i].isDirty = plugin.isDirty; @@ -317,8 +317,8 @@ function onCopyContent(evt) { isEmpty: plugin.isEmpty, loadsArchive: plugin.loadsArchive, - modPriority: plugin.modPriority, - isGlobalPriority: plugin.isGlobalPriority, + priority: plugin.priority, + isPriorityGlobal: plugin.isPriorityGlobal, messages: plugin.messages, tags: plugin.tags, isDirty: plugin.isDirty @@ -491,8 +491,8 @@ function onEditorClose(evt) { }); promise = loot.query(request).then(JSON.parse).then(function(result){ if (result) { - evt.target.data.modPriority = result.modPriority; - evt.target.data.isGlobalPriority = result.isGlobalPriority; + evt.target.data.priority = result.priority; + evt.target.data.isPriorityGlobal = result.isPriorityGlobal; evt.target.data.messages = result.messages; evt.target.data.tags = result.tags; evt.target.data.isDirty = result.isDirty; @@ -598,8 +598,8 @@ function onClearMetadata(evt) { loot.game.plugins[i].userlist = undefined; loot.game.plugins[i].editor = undefined; - loot.game.plugins[i].modPriority = result.modPriority; - loot.game.plugins[i].isGlobalPriority = result.isGlobalPriority; + loot.game.plugins[i].priority = result.priority; + loot.game.plugins[i].isPriorityGlobal = result.isPriorityGlobal; loot.game.plugins[i].messages = result.messages; loot.game.plugins[i].tags = result.tags; loot.game.plugins[i].isDirty = result.isDirty; @@ -670,8 +670,8 @@ function onContentRefresh(evt) { loot.game.plugins[i].crc = plugin.crc; loot.game.plugins[i].version = plugin.version; - loot.game.plugins[i].modPriority = plugin.modPriority; - loot.game.plugins[i].isGlobalPriority = plugin.isGlobalPriority; + loot.game.plugins[i].priority = plugin.priority; + loot.game.plugins[i].isPriorityGlobal = plugin.isPriorityGlobal; loot.game.plugins[i].messages = plugin.messages; loot.game.plugins[i].tags = plugin.tags; loot.game.plugins[i].isDirty = plugin.isDirty; @@ -715,6 +715,15 @@ function onSearchOpen(evt) { function onSearchClose(evt) { document.getElementById('mainToolbar').classList.remove('search'); } +function onSidebarFilterToggle(evt) { + if (evt.target.id !== 'contentFilter') { + loot.filters[evt.target.id] = evt.target.checked; + } else { + loot.filters.contentSearchString = evt.target.value; + } + saveFilterState(evt); + setFilteredUIData(); +} function setupEventHandlers() { /*Set up handlers for filters.*/ document.getElementById('hideVersionNumbers').addEventListener('change', onToggleDisplayCSS, false); @@ -723,20 +732,15 @@ function setupEventHandlers() { document.getElementById('hideCRCs').addEventListener('change', saveFilterState, false); document.getElementById('hideBashTags').addEventListener('change', onToggleBashTags, false); document.getElementById('hideBashTags').addEventListener('change', saveFilterState, false); - document.getElementById('hideNotes').addEventListener('change', setFilteredUIData, false); - document.getElementById('hideNotes').addEventListener('change', saveFilterState, false); - document.getElementById('hideDoNotCleanMessages').addEventListener('change', setFilteredUIData, false); - document.getElementById('hideDoNotCleanMessages').addEventListener('change', saveFilterState, false); - document.getElementById('hideInactivePlugins').addEventListener('change', setFilteredUIData, false); - document.getElementById('hideInactivePlugins').addEventListener('change', saveFilterState, false); - document.getElementById('hideAllPluginMessages').addEventListener('change', setFilteredUIData, false); - document.getElementById('hideAllPluginMessages').addEventListener('change', saveFilterState, false); - document.getElementById('hideMessagelessPlugins').addEventListener('change', setFilteredUIData, false); - document.getElementById('hideMessagelessPlugins').addEventListener('change', saveFilterState, false); + document.getElementById('hideNotes').addEventListener('change', onSidebarFilterToggle, false); + document.getElementById('hideDoNotCleanMessages').addEventListener('change', onSidebarFilterToggle, false); + document.getElementById('hideInactivePlugins').addEventListener('change', onSidebarFilterToggle, false); + document.getElementById('hideAllPluginMessages').addEventListener('change', onSidebarFilterToggle, false); + document.getElementById('hideMessagelessPlugins').addEventListener('change', onSidebarFilterToggle, false); document.body.addEventListener('loot-filter-conflicts', onConflictsFilter, false); /* Set up event handlers for content filter. */ - document.getElementById('contentFilter').addEventListener('change', setFilteredUIData, false); + document.getElementById('contentFilter').addEventListener('change', onSidebarFilterToggle, false); /* Set up handlers for buttons. */ document.getElementById('redatePluginsButton').addEventListener('click', onRedatePlugins, false); diff --git a/src/gui/html/js/filters.js b/src/gui/html/js/filters.js index dcebfc87..7f229173 100644 --- a/src/gui/html/js/filters.js +++ b/src/gui/html/js/filters.js @@ -1,252 +1,67 @@ -var filters = { - /* Filter functions return true if the given plugin passes the filter and - should be displayed, otherwise false. */ +'use strict'; +(function exportModule(root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define([], factory); + } else { + // Browser globals + root.loot = root.loot || {}; + root.loot.Filters = factory(); + } +}(this, () => { + return class Filters { + constructor(l10n) { + /* Plugin filters */ + this.hideMessagelessPlugins = false; + this.hideInactivePlugins = false; + this.conflictingPluginNames = []; + this.contentSearchString = ''; - hiddenPluginNo: 0, - hiddenMessageNo: 0, - conflicts: [], + /* Plugin content filters */ + this.hideVersionNumbers = false; + this.hideCRCs = false; + this.hideBashTags = false; + this.hideAllPluginMessages = false; + this.hideNotes = false; + this.hideDoNotCleanMessages = false; - searchFilter: function(plugin, needle) { - if (needle.length == 0) { - return true; + this._doNotCleanString = l10n.translate('Do not clean').toLowerCase(); + } + + pluginFilter(plugin) { + if (this.hideInactivePlugins && !plugin.isActive) { + return false; } - if (plugin.name.toLowerCase().indexOf(needle) != -1 - || plugin.crcString.toLowerCase().indexOf(needle) != -1 - || plugin.version.toLowerCase().indexOf(needle) != -1) { - - return true; + if (this.conflictingPluginNames.length !== 0 && this.conflictingPluginNames.indexOf(plugin.name) === -1) { + return false; } - var tags = plugin.tagStrings; - if (tags.added.toLowerCase().indexOf(needle) != -1 - || tags.removed.toLowerCase().indexOf(needle) != -1) { - - return true; + if (this.hideMessagelessPlugins && plugin.getCardContent(this).messages.length === 0) { + return false; } - for (var i = 0; i < plugin.messages.length; ++i) { - if (plugin.messages[i].content[0].str.toLowerCase().indexOf(needle) != -1) { - return true; - } - } - }, - - messagelessFilter: function(plugin) { - /* This function could be further optimised to perform fewer checks, - but it's also responsible for setting the hidden message count, so - has to go through everything. */ - - var hasMessages = false; - /* If any messages exist, check if they are hidden or not. Note - that the messages may not be present as elements, so the check - is actually if they would be hidden according to the message - filters. */ - if (this.allMessageFilter()) { - plugin.messages.forEach(function(message){ - if (this.noteFilter(message) - && this.doNotCleanFilter(message)) { - - hasMessages = true; - return; - } - ++hiddenMessageNo; - }, this); - } else { - hiddenMessageNo += plugin.messages.length; + if (this.contentSearchString.length !== 0 && !plugin.getCardContent(this).containsText(this.contentSearchString)) { + return false; } - if (document.getElementById('hideMessagelessPlugins').checked) { - return hasMessages; - } else { - return true; - } - }, + return true; + } - inactiveFilter: function(plugin) { - if (document.getElementById('hideInactivePlugins').checked) { - return plugin.isActive; - } else { - return true; - } - }, - - conflictsFilter: function(plugin) { - if (this.conflicts.length > 0) { - return this.conflicts.indexOf(plugin.name) != -1; - } else { - return true; - } - }, - - applyPluginFilters: function(plugins) { - var search = document.getElementById('contentFilter').value.toLowerCase(); - hiddenPluginNo = 0; - hiddenMessageNo = 0; - var filteredPlugins = []; - - plugins.forEach(function(plugin){ - /* Messageless filter needs to run first. */ - if (this.messagelessFilter(plugin) - && this.inactiveFilter(plugin) - && this.conflictsFilter(plugin) - && this.searchFilter(plugin, search)) { - - filteredPlugins.push(plugin); - return; - } - ++hiddenPluginNo; - }, this); - - document.getElementById('hiddenPluginNo').textContent = hiddenPluginNo; - document.getElementById('hiddenMessageNo').textContent = hiddenMessageNo; - - return filteredPlugins; - }, - - /* Message filter functions are run from within plugin cards, when the card's - messages are to be added as elements. Each filter should return true if the - message is to be displayed. */ - - noteFilter: function(message) { - if (document.getElementById('hideNotes').checked) { - return message.type != 'say'; - } else { - return true; - } - }, - - doNotCleanFilter: function(message) { - if (document.getElementById('hideDoNotCleanMessages').checked) { - return message.content[0].str.indexOf(loot.l10n.translate("Do not clean")) == -1; - } else { - return true; - } - }, - - allMessageFilter: function() { - return !document.getElementById('hideAllPluginMessages').checked; - }, - - applyMessageFilters: function(messages) { - var filteredMessages = []; - - if (this.allMessageFilter()) { - messages.forEach(function(message){ - if (this.noteFilter(message) - && this.doNotCleanFilter(message)) { - - filteredMessages.push(message); - return; - } - }, this); + messageFilter(message) { + if (this.hideAllPluginMessages) { + return false; } - return filteredMessages; - }, -}; - -function getConflictingPluginsFromFilter() { - var conflictsPlugin = document.body.getAttribute('data-conflicts'); - if (conflictsPlugin) { - /* Now get conflicts for the plugin. */ - var request = JSON.stringify({ - name: 'getConflictingPlugins', - args: [ - conflictsPlugin - ] - }); - - showProgress(loot.l10n.translate('Checking if plugins have been loaded...')); - - return loot.query(request).then(JSON.parse).then(function(result){ - if (result) { - /* Filter everything but the plugin itself if there are no - conflicts. */ - var conflicts = [ conflictsPlugin ]; - for (var key in result) { - if (result[key].conflicts) { - conflicts.push(key); - } - for (var i = 0; i < loot.game.plugins.length; ++i) { - if (loot.game.plugins[i].name == key) { - loot.game.plugins[i].crc = result[key].crc; - loot.game.plugins[i].isEmpty = result[key].isEmpty; - - loot.game.plugins[i].messages = result[key].messages; - loot.game.plugins[i].tags = result[key].tags; - loot.game.plugins[i].isDirty = result[key].isDirty; - break; - } - } - } - closeProgressDialog(); - return conflicts; - } - closeProgressDialog(); - return [ conflictsPlugin ]; - }).catch(processCefError); - } - - return Promise.resolve([]); -} - -function setFilteredUIData() { - /* The conflict filter, if enabled, executes C++ code, so needs to be - handled using a promise, so the rest of the function should wait until - it is completed. - */ - getConflictingPluginsFromFilter().then(function(conflicts) { - filters.conflicts = conflicts; - var filtered = filters.applyPluginFilters(loot.game.plugins); - document.getElementById('cardsNav').data = filtered; - document.getElementById('main').lastElementChild.data = filtered; - - filtered.forEach(function(plugin){ - var element = document.getElementById(plugin.id); - if (element) { - element.onMessagesChange(); - } - }); - - /* Now perform search again. If there is no current search, this won't - do anything. */ - document.getElementById('searchBar').search(); - }); -} -function restoreFilterStates() { - if (loot.settings.filters) { - document.getElementById('hideMessagelessPlugins').checked = loot.settings.filters.hideMessagelessPlugins; - document.getElementById('hideInactivePlugins').checked = loot.settings.filters.hideInactivePlugins; - document.getElementById('hideNotes').checked = loot.settings.filters.hideNotes; - document.getElementById('hideDoNotCleanMessages').checked = loot.settings.filters.hideDoNotCleanMessages; - document.getElementById('hideAllPluginMessages').checked = loot.settings.filters.hideAllPluginMessages; - document.getElementById('hideVersionNumbers').checked = loot.settings.filters.hideVersionNumbers; - document.getElementById('hideCRCs').checked = loot.settings.filters.hideCRCs; - document.getElementById('hideBashTags').checked = loot.settings.filters.hideBashTags; - } -} - -function applyEnabledFilters() { - if (loot.settings.filters) { - if (loot.settings.filters.hideMessagelessPlugins - || loot.settings.filters.hideInactivePlugins - || loot.settings.filters.hideNotes - || loot.settings.filters.hideDoNotCleanMessages - || loot.settings.filters.hideAllPluginMessages) { - setFilteredUIData(); + if (this.hideNotes && message.type === 'say') { + return false; } - if (loot.settings.filters.hideVersionNumbers) { - document.getElementById('hideVersionNumbers').dispatchEvent(new Event('change')); + if (this.hideDoNotCleanMessages && message.content.toLowerCase().indexOf(this._doNotCleanString) !== -1) { + return false; } - if (loot.settings.filters.hideCRCs) { - document.getElementById('hideCRCs').dispatchEvent(new Event('change')); - } - - if (loot.settings.filters.hideBashTags) { - document.getElementById('hideBashTags').dispatchEvent(new Event('change')); - } - } -} + return true; + } + }; +})); diff --git a/src/gui/html/js/helpers.js b/src/gui/html/js/helpers.js index 086f3f5d..9f506cb1 100644 --- a/src/gui/html/js/helpers.js +++ b/src/gui/html/js/helpers.js @@ -70,3 +70,74 @@ function handleUnappliedChangesClose(change) { } }); } +function getConflictingPlugins(pluginName) { + if (!pluginName) { + return Promise.resolve([]); + } + + /* Now get conflicts for the plugin. */ + const request = JSON.stringify({ + name: 'getConflictingPlugins', + args: [ + pluginName, + ], + }); + + showProgress(loot.l10n.translate('Checking if plugins have been loaded...')); + + return loot.query(request).then(JSON.parse).then((result) => { + if (result) { + /* Filter everything but the plugin itself if there are no + conflicts. */ + const conflicts = [pluginName]; + for (const key in result) { + if (result[key].conflicts) { + conflicts.push(key); + } + for (let i = 0; i < loot.game.plugins.length; ++i) { + if (loot.game.plugins[i].name === key) { + loot.game.plugins[i].crc = result[key].crc; + loot.game.plugins[i].isEmpty = result[key].isEmpty; + + loot.game.plugins[i].messages = result[key].messages; + loot.game.plugins[i].tags = result[key].tags; + loot.game.plugins[i].isDirty = result[key].isDirty; + break; + } + } + } + closeProgressDialog(); + return conflicts; + } + closeProgressDialog(); + return [pluginName]; + }).catch(processCefError); +} +function setFilteredUIData(filtersState) { + getConflictingPlugins(loot.filters.conflictTargetPluginName).then((conflictingPluginNames) => { + loot.filters.conflictingPluginNames = conflictingPluginNames; + return loot.game.plugins.filter(loot.filters.pluginFilter, loot.filters); + }).then((filteredPlugins) => { + document.getElementById('cardsNav').data = filteredPlugins; + document.getElementById('pluginCardList').data = filteredPlugins; + + filteredPlugins.forEach((plugin) => { + const element = document.getElementById(plugin.id); + if (element) { + element.onMessagesChange(); + } + }); + + /* Now perform search again. If there is no current search, this won't + do anything. */ + document.getElementById('searchBar').search(); + + /* Re-count all hidden plugins and messages. */ + document.getElementById('hiddenPluginNo').textContent = loot.game.plugins.length - filteredPlugins.length; + let hiddenMessageNo = 0; + loot.game.plugins.forEach((plugin) => { + hiddenMessageNo += plugin.messages.length - plugin.getCardContent(loot.filters).messages.length; + }); + document.getElementById('hiddenMessageNo').textContent = hiddenMessageNo; + }); +} diff --git a/src/gui/html/js/init.js b/src/gui/html/js/init.js index 0b8d618e..e5263c9e 100644 --- a/src/gui/html/js/init.js +++ b/src/gui/html/js/init.js @@ -25,6 +25,53 @@ var marked; var l10n; +function restoreFilterStates() { + if (loot.settings.filters && loot.filters) { + loot.filters.hideMessagelessPlugins = loot.settings.filters.hideMessagelessPlugins; + loot.filters.hideInactivePlugins = loot.settings.filters.hideInactivePlugins; + loot.filters.hideNotes = loot.settings.filters.hideNotes; + loot.filters.hideDoNotCleanMessages = loot.settings.filters.hideDoNotCleanMessages; + loot.filters.hideAllPluginMessages = loot.settings.filters.hideAllPluginMessages; + loot.filters.hideVersionNumbers = loot.settings.filters.hideVersionNumbers; + loot.filters.hideCRCs = loot.settings.filters.hideCRCs; + loot.filters.hideBashTags = loot.settings.filters.hideBashTags; + + document.getElementById('hideMessagelessPlugins').checked = loot.settings.filters.hideMessagelessPlugins; + document.getElementById('hideInactivePlugins').checked = loot.settings.filters.hideInactivePlugins; + document.getElementById('hideNotes').checked = loot.settings.filters.hideNotes; + document.getElementById('hideDoNotCleanMessages').checked = loot.settings.filters.hideDoNotCleanMessages; + document.getElementById('hideAllPluginMessages').checked = loot.settings.filters.hideAllPluginMessages; + document.getElementById('hideVersionNumbers').checked = loot.settings.filters.hideVersionNumbers; + document.getElementById('hideCRCs').checked = loot.settings.filters.hideCRCs; + document.getElementById('hideBashTags').checked = loot.settings.filters.hideBashTags; + } +} + +function applyEnabledFilters() { + if (!loot.filters) { + return; + } + + if (loot.filters.hideMessagelessPlugins + || loot.filters.hideInactivePlugins + || loot.filters.hideNotes + || loot.filters.hideDoNotCleanMessages + || loot.filters.hideAllPluginMessages) { + setFilteredUIData(); + } + + if (loot.filters.hideVersionNumbers) { + document.getElementById('hideVersionNumbers').dispatchEvent(new Event('change')); + } + + if (loot.filters.hideCRCs) { + document.getElementById('hideCRCs').dispatchEvent(new Event('change')); + } + + if (loot.filters.hideBashTags) { + document.getElementById('hideBashTags').dispatchEvent(new Event('change')); + } +} function initVars() { loot.query('getVersion').then(function(result){ try { diff --git a/src/gui/html/js/plugin.js b/src/gui/html/js/plugin.js index 94733205..875a85c0 100644 --- a/src/gui/html/js/plugin.js +++ b/src/gui/html/js/plugin.js @@ -32,23 +32,155 @@ root.loot.Plugin = factory(); } }(this, () => { + /* Messages, tags, CRCs and version strings can all be hidden by filters. + Use getters with no setters for member variables as data should not be + written to objects of this class. */ + class PluginCardContent { + constructor(plugin, filters) { + this._name = plugin.name; + this._isActive = plugin.isActive || false; + this._isEmpty = plugin.isEmpty; + this._isMaster = plugin.isMaster; + this._loadsArchive = plugin.loadsArchive; + + if (!filters.hideVersionNumbers) { + this._version = plugin.version; + } else { + this._version = ''; + } + + if (!filters.hideCRCs) { + this._crc = plugin.crc; + } else { + this._crc = 0; + } + + if (!filters.hideBashTags) { + this._tags = plugin.tags; + } else { + this._tags = []; + } + + this._messages = plugin.messages.map((message) => { + return { + type: message.type, + content: message.content[0].str, + }; + }).filter(filters.messageFilter, filters); + } + + get name() { + return this._name; + } + + get isActive() { + return this._isActive; + } + + get isEmpty() { + return this._isEmpty; + } + + get isMaster() { + return this._isMaster; + } + + get loadsArchive() { + return this._loadsArchive; + } + + get version() { + return this._version; + } + + get crc() { + if (this._crc === 0) { + return ''; + } + + /* Pad CRC string to 8 characters. */ + return ('00000000' + this._crc.toString(16).toUpperCase()).slice(-8); + } + + get tags() { + const tagsAdded = []; + const tagsRemoved = []; + + if (this._tags) { + for (let i = 0; i < this._tags.length; ++i) { + if (this._tags[i].name[0] === '-') { + tagsRemoved.push(this._tags[i].name.substr(1)); + } else { + tagsAdded.push(this._tags[i].name); + } + } + } + /* Now make sure that the same tag doesn't appear in both arrays. + Prefer the removed list. */ + for (let i = 0; i < tagsAdded.length; ++i) { + for (let j = 0; j < tagsRemoved.length; ++j) { + if (tagsRemoved[j].toLowerCase() === tagsAdded[i].toLowerCase()) { + /* Remove tag from the tagsAdded array. */ + tagsAdded.splice(i, 1); + --i; + } + } + } + + return { + added: tagsAdded.join(', '), + removed: tagsRemoved.join(', '), + }; + } + + get messages() { + return this._messages; + } + + containsText(text) { + if (text === undefined || text.length === 0) { + return true; + } + const needle = text.toLowerCase(); + + if (this.name.toLowerCase().indexOf(needle) !== -1 + || this.crc.toLowerCase().indexOf(needle) !== -1 + || this.version.toLowerCase().indexOf(needle) !== -1) { + return true; + } + + if (this.tags.added.toLowerCase().indexOf(needle) !== -1 + || this.tags.removed.toLowerCase().indexOf(needle) !== -1) { + return true; + } + + for (let i = 0; i < this.messages.length; ++i) { + if (this.messages[i].content.toLowerCase().indexOf(needle) !== -1) { + return true; + } + } + + return false; + } + } + return class Plugin { constructor(obj) { /* Plugin data */ this.name = obj.name; - this.crc = obj.crc; - this.version = obj.version; - this.isActive = obj.isActive; - this.isEmpty = obj.isEmpty; - this.isMaster = obj.isMaster; - this.loadsArchive = obj.loadsArchive; + this.crc = obj.crc || 0; + this.version = obj.version || ''; + this.isActive = obj.isActive || false; + this.isEmpty = obj.isEmpty || false; + this.isMaster = obj.isMaster || false; + this.loadsArchive = obj.loadsArchive || false; this.masterlist = obj.masterlist; this.userlist = obj.userlist; - this.modPriority = obj.modPriority; - this.isGlobalPriority = obj.isGlobalPriority; - this._messages = obj.messages; + this.priority = obj.priority || 0; + this.isPriorityGlobal = obj.isPriorityGlobal || false; + this._messages = obj.messages || []; this.tags = obj.tags; this._isDirty = obj.isDirty || false; @@ -100,52 +232,12 @@ return rowData; } - get tagStrings() { - const tagsAdded = []; - const tagsRemoved = []; - - if (this.tags) { - for (let i = 0; i < this.tags.length; ++i) { - if (this.tags[i].name[0] === '-') { - tagsRemoved.push(this.tags[i].name.substr(1)); - } else { - tagsAdded.push(this.tags[i].name); - } - } - } - /* Now make sure that the same tag doesn't appear in both arrays. - Prefer the removed list. */ - for (let i = 0; i < tagsAdded.length; ++i) { - for (let j = 0; j < tagsRemoved.length; ++j) { - if (tagsRemoved[j].toLowerCase() === tagsAdded[i].toLowerCase()) { - /* Remove tag from the tagsAdded array. */ - tagsAdded.splice(i, 1); - --i; - } - } - } - - return { - added: tagsAdded.join(', '), - removed: tagsRemoved.join(', '), - }; - } - get priorityString() { - if (this.modPriority === undefined || this.modPriority === 0) { + if (this.priority === 0) { return ''; } - return this.modPriority.toString(); - } - - get crcString() { - if (this.crc === undefined || this.crc === 0) { - return ''; - } - - /* Pad CRC string to 8 characters. */ - return ('00000000' + this.crc.toString(16).toUpperCase()).slice(-8); + return this.priority.toString(); } get messages() { @@ -161,29 +253,25 @@ let oldErrs = 0; let newErrs = 0; - if (this._messages) { - oldTotal = this._messages.length; + oldTotal = this._messages.length; - this._messages.forEach((message) => { - if (message.type === 'warn') { - ++oldWarns; - } else if (message.type === 'error') { - ++oldErrs; - } - }); - } + this._messages.forEach((message) => { + if (message.type === 'warn') { + ++oldWarns; + } else if (message.type === 'error') { + ++oldErrs; + } + }); - if (messages) { - newTotal = messages.length; + newTotal = messages.length; - messages.forEach((message) => { - if (message.type === 'warn') { - ++newWarns; - } else if (message.type === 'error') { - ++newErrs; - } - }); - } + messages.forEach((message) => { + if (message.type === 'warn') { + ++newWarns; + } else if (message.type === 'error') { + ++newErrs; + } + }); if (newTotal !== oldTotal || newWarns !== oldWarns || newErrs !== oldErrs) { document.dispatchEvent(new CustomEvent('loot-plugin-message-change', { @@ -214,5 +302,9 @@ this._isDirty = dirty; } + + getCardContent(filters) { + return new PluginCardContent(this, filters); + } }; })); diff --git a/src/tests/gui/html/js/test.html b/src/tests/gui/html/js/test.html index 1bc13433..dcf62cdb 100644 --- a/src/tests/gui/html/js/test.html +++ b/src/tests/gui/html/js/test.html @@ -11,8 +11,10 @@ + +