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.
This commit is contained in:
Oliver Hamlet
2016-01-06 18:26:56 +00:00
parent af8bac8e08
commit 5ef7a656c6
15 changed files with 1134 additions and 482 deletions
+1
View File
@@ -10,6 +10,7 @@ ecmaFeatures:
globals:
loot: false
should: false
extends:
- "eslint:recommended"
+5 -5
View File
@@ -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<int>());
newUserlistEntry.Priority(pluginMetadata["priority"].as<int>());
newUserlistEntry.SetPriorityExplicit(true);
newUserlistEntry.SetPriorityGlobal(pluginMetadata["isGlobalPriority"].as<bool>());
newUserlistEntry.SetPriorityGlobal(pluginMetadata["isPriorityGlobal"].as<bool>());
}
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;
+15 -7
View File
@@ -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);
}
},
+23 -19
View File
@@ -125,8 +125,8 @@ loot-editor-close
</core-tooltip>
<div flex>
<h1>{{data.name}}</h1>
<span id="version">{{data.version}}</span>
<span id="crc">{{data.crcString}}</span>
<span id="version"></span>
<span id="crc"></span>
</div>
<core-tooltip id="isMaster" label="Master File" noarrow>
<core-icon icon="loot-custom-icons:crown"></core-icon>
@@ -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);
}
+5 -5
View File
@@ -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');
+4 -27
View File
@@ -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;
}
});
+8 -8
View File
@@ -213,10 +213,10 @@
<template>
<loot-plugin-card data="{{model}}" id="{{model.id}}" data-active="{{model.isActive}}" data-empty="{{model.isEmpty}}" data-archive="{{model.loadsArchive}}" data-master="{{model.isMaster}}">
<h1>{{model.name}}</h1>
<span class="version">{{model.version}}</span>
<span class="crc">{{model.crcString}}</span>
<span class="tag add">{{model.tagStrings.added}}</span>
<span class="tag remove">{{model.tagStrings.removed}}</span>
<span class="version"></span>
<span class="crc"></span>
<span class="tag add"></span>
<span class="tag remove"></span>
<ul></ul>
</loot-plugin-card>
</template>
@@ -315,12 +315,12 @@
<script src="../../../bower_components/marked/lib/marked.js"></script>
<script src="../../../bower_components/Jed/jed.js"></script>
<script src="../../../bower_components/jed-gettext-parser/jedGettextParser.js"></script>
<script src="js/events.js"></script>
<script src="js/filters.js"></script>
<script src="js/game.js"></script>
<script src="js/helpers.js"></script>
<script src="js/loot.js"></script>
<script src="js/translator.js"></script>
<script src="js/l10n.js"></script>
<script src="js/plugin.js"></script>
<script src="js/filters.js"></script>
<script src="js/events.js"></script>
<script src="js/translator.js"></script>
<script src="js/init.js"></script>
</body>
+27 -23
View File
@@ -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);
+50 -235
View File
@@ -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;
}
};
}));
+71
View File
@@ -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;
});
}
+47
View File
@@ -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 {
+163 -71
View File
@@ -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);
}
};
}));
+2
View File
@@ -11,8 +11,10 @@
<script src="../../../../../bower_components/jed-gettext-parser/jedGettextParser.js"></script>
<script>mocha.setup('bdd')</script>
<script src="../../../../gui/html/js/filters.js"></script>
<script src="../../../../gui/html/js/plugin.js"></script>
<script src="../../../../gui/html/js/translator.js"></script>
<script src="test_filters.js"></script>
<script src="test_plugin.js"></script>
<script src="test_translator.js"></script>
<script>
+206
View File
@@ -0,0 +1,206 @@
'use strict';
/* Mock the Translator class. */
class Translator {
translate(text) {
return text;
}
}
describe('Filters', () => {
let l10n;
beforeEach(() => {
l10n = new Translator();
});
describe('#constructor()', () => {
it('should throw if no parameter is passed', () => {
(() => { new loot.Filters(); }).should.throw(); // eslint-disable-line no-new
});
it('should throw if an empty object is passed', () => {
(() => { new loot.Filters({}); }).should.throw(); // eslint-disable-line no-new
});
it('should not throw if a valid Translator object is passed', () => {
(() => { new loot.Filters(l10n); }).should.not.throw(); // eslint-disable-line no-new
});
it('should initialise filters as not enabled', () => {
const filters = new loot.Filters(l10n);
filters.hideMessagelessPlugins.should.be.false();
filters.hideInactivePlugins.should.be.false();
filters.conflictingPluginNames.should.deepEqual([]);
filters.contentSearchString.should.equal('');
filters.hideVersionNumbers.should.be.false();
filters.hideCRCs.should.be.false();
filters.hideBashTags.should.be.false();
filters.hideAllPluginMessages.should.be.false();
filters.hideNotes.should.be.false();
filters.hideDoNotCleanMessages.should.be.false();
});
it('should initialise "do not clean" search string', () => {
const filters = new loot.Filters(l10n);
filters._doNotCleanString.should.equal('do not clean');
});
});
describe('#pluginFilter()', () => {
let filters;
let plugin;
/* Mock the PluginCardContent class */
class PluginCardContent {
constructor(pluginObj, filtersObj) {
this._messages = pluginObj.messages;
this._hideMessages = filtersObj.hideAllPluginMessages;
}
get messages() {
if (this._hideMessages) {
return [];
}
return this._messages;
}
containsText(text) {
return text === 'found text';
}
}
/* Mock the Plugin class */
class Plugin {
constructor() {
this.name = 'test';
this.isActive = false;
this.messages = [];
}
getCardContent(filtersObj) {
return new PluginCardContent(this, filtersObj);
}
}
beforeEach(() => {
filters = new loot.Filters(l10n);
plugin = new Plugin();
});
it('should return true if no filters are enabled', () => {
filters.pluginFilter(plugin).should.be.true();
});
it('should return false if inactive plugins filter is enabled', () => {
filters.hideInactivePlugins = true;
filters.pluginFilter(plugin).should.be.false();
});
it('should return true if inactive plugins filter is enabled and plugin is active', () => {
filters.hideInactivePlugins = true;
plugin.isActive = true;
filters.pluginFilter(plugin).should.be.true();
});
it('should return false if messageless plugins filter is enabled', () => {
filters.hideMessagelessPlugins = true;
filters.pluginFilter(plugin).should.be.false();
});
it('should return true if messageless plugins filter is enabled and plugin has a non-zero message array', () => {
filters.hideMessagelessPlugins = true;
plugin.messages = [0];
filters.pluginFilter(plugin).should.be.true();
});
it('should return false if all plugin messages and messageless plugin filters are enabled and plugin has a non-zero message array', () => {
filters.hideAllPluginMessages = true;
filters.hideMessagelessPlugins = true;
plugin.messages = [0];
filters.pluginFilter(plugin).should.be.false();
});
it('should return false if conflicting plugins filter is enabled', () => {
filters.conflictingPluginNames = ['conflicting plugin'];
filters.pluginFilter(plugin).should.be.false();
});
it('should return true if conflicting plugins filter is enabled and plugin name is in the conflicting plugins array', () => {
filters.conflictingPluginNames = [
'conflicting plugin',
plugin.name,
];
filters.pluginFilter(plugin).should.be.true();
});
it('should return false if plugin content filter is enabled', () => {
filters.contentSearchString = 'unfound text';
filters.pluginFilter(plugin).should.be.false();
});
it('should return true if plugin content filter is enabled and plugin contains the filter text', () => {
filters.contentSearchString = 'found text';
filters.pluginFilter(plugin).should.be.true();
});
});
describe('#messageFilter()', () => {
let filters;
let note;
let doNotCleanMessage;
beforeEach(() => {
filters = new loot.Filters(l10n);
note = {
type: 'say',
content: 'test message',
};
doNotCleanMessage = {
type: 'warn',
content: 'do not clean',
};
});
it('should return true for a note message when no filters are enabled', () => {
filters.messageFilter(note).should.be.true();
});
it('should return true for a warning "do not clean" message when no filters are enabled', () => {
filters.messageFilter(doNotCleanMessage).should.be.true();
});
it('should return false for a note message when the notes filter is enabled', () => {
filters.hideNotes = true;
filters.messageFilter(note).should.be.false();
});
it('should return true for a warning message when the notes filter is enabled', () => {
filters.hideNotes = true;
filters.messageFilter(doNotCleanMessage).should.be.true();
});
it('should return false for a "do not clean" message when the "do not clean" messages filter is enabled', () => {
filters.hideDoNotCleanMessages = true;
filters.messageFilter(doNotCleanMessage).should.be.false();
});
it('should return true for a message not containing "do not clean" when the "do not clean" messages filter is enabled', () => {
filters.hideDoNotCleanMessages = true;
filters.messageFilter(note).should.be.true();
});
it('should return false for a note message when the all messages filter is enabled', () => {
filters.hideAllPluginMessages = true;
filters.messageFilter(note).should.be.false();
});
it('should return false for a "do not clean" message when the all messages filter is enabled', () => {
filters.hideAllPluginMessages = true;
filters.messageFilter(doNotCleanMessage).should.be.false();
});
});
});
File diff suppressed because it is too large Load Diff