diff --git a/src/gui/html/js/events.js b/src/gui/html/js/events.js
index c07d73e8..a5b0d8e3 100644
--- a/src/gui/html/js/events.js
+++ b/src/gui/html/js/events.js
@@ -1,4 +1,68 @@
'use strict';
+function onGamePluginsChange(evt) {
+ if (!evt.detail.valuesAreTotals) {
+ evt.detail.totalMessageNo += parseInt(document.getElementById('totalMessageNo').textContent, 10);
+ evt.detail.warnMessageNo += parseInt(document.getElementById('totalWarningNo').textContent, 10);
+ evt.detail.errorMessageNo += parseInt(document.getElementById('totalErrorNo').textContent, 10);
+ evt.detail.totalPluginNo += parseInt(document.getElementById('totalPluginNo').textContent, 10);
+ evt.detail.activePluginNo += parseInt(document.getElementById('activePluginNo').textContent, 10);
+ evt.detail.dirtyPluginNo += parseInt(document.getElementById('dirtyPluginNo').textContent, 10);
+ }
+
+ document.getElementById('filterTotalMessageNo').textContent = evt.detail.totalMessageNo;
+ document.getElementById('totalMessageNo').textContent = evt.detail.totalMessageNo;
+ document.getElementById('totalWarningNo').textContent = evt.detail.warnMessageNo;
+ document.getElementById('totalErrorNo').textContent = evt.detail.errorMessageNo;
+
+ document.getElementById('filterTotalPluginNo').textContent = evt.detail.totalPluginNo;
+ document.getElementById('totalPluginNo').textContent = evt.detail.totalPluginNo;
+ document.getElementById('activePluginNo').textContent = evt.detail.activePluginNo;
+ document.getElementById('dirtyPluginNo').textContent = evt.detail.dirtyPluginNo;
+}
+function onGameGlobalMessagesChange(evt) {
+ document.getElementById('filterTotalMessageNo').textContent = parseInt(document.getElementById('filterTotalMessageNo').textContent, 10) + evt.detail.totalDiff;
+ document.getElementById('totalMessageNo').textContent = parseInt(document.getElementById('totalMessageNo').textContent, 10) + evt.detail.totalDiff;
+ document.getElementById('totalWarningNo').textContent = parseInt(document.getElementById('totalWarningNo').textContent, 10) + evt.detail.warningDiff;
+ document.getElementById('totalErrorNo').textContent = parseInt(document.getElementById('totalErrorNo').textContent, 10) + evt.detail.errorDiff;
+
+ /* Remove old messages from UI. */
+ const generalMessagesList = document.getElementById('summary').getElementsByTagName('ul')[0];
+ while (generalMessagesList.firstElementChild) {
+ generalMessagesList.removeChild(generalMessagesList.firstElementChild);
+ }
+
+ /* Add new messages. */
+ if (evt.detail.messages) {
+ evt.detail.messages.forEach((message) => {
+ const li = document.createElement('li');
+ li.className = message.type;
+ /* Use the Marked library for Markdown formatting support. */
+ li.innerHTML = marked(message.content[0].str);
+ generalMessagesList.appendChild(li);
+ });
+ }
+}
+function onGameMasterlistChange(evt) {
+ document.getElementById('masterlistRevision').textContent = evt.detail.revision;
+ document.getElementById('masterlistDate').textContent = evt.detail.date;
+}
+function onGameFolderChange(evt) {
+ updateSelectedGame(evt.detail.folder);
+ /* Enable/disable the redate plugins option. */
+ let index = undefined;
+ for (let i = 0; i < loot.settings.games.length; ++i) {
+ if (loot.settings.games[i].folder === evt.detail.folder) {
+ index = i;
+ break;
+ }
+ }
+ const redateButton = document.getElementById('redatePluginsButton');
+ if (index && loot.settings.games[index].type === 'Skyrim') {
+ redateButton.removeAttribute('disabled');
+ } else {
+ redateButton.setAttribute('disabled', true);
+ }
+}
function onPluginMessageChange(evt) {
document.getElementById('filterTotalMessageNo').textContent = parseInt(document.getElementById('filterTotalMessageNo').textContent, 10) + evt.detail.totalDiff;
document.getElementById('totalMessageNo').textContent = parseInt(document.getElementById('totalMessageNo').textContent, 10) + evt.detail.totalDiff;
@@ -782,4 +846,10 @@ function setupEventHandlers() {
/* Set up handler for plugin message and dirty info changes. */
document.addEventListener('loot-plugin-message-change', onPluginMessageChange);
document.addEventListener('loot-plugin-isdirty-change', onPluginIsDirtyChange);
+
+ /* Set up event handlers for game member variable changes. */
+ document.addEventListener('loot-game-folder-change', onGameFolderChange);
+ document.addEventListener('loot-game-masterlist-change', onGameMasterlistChange);
+ document.addEventListener('loot-game-global-messages-change', onGameGlobalMessagesChange);
+ document.addEventListener('loot-game-plugins-change', onGamePluginsChange);
}
diff --git a/src/gui/html/js/game.js b/src/gui/html/js/game.js
new file mode 100644
index 00000000..6ccac03f
--- /dev/null
+++ b/src/gui/html/js/game.js
@@ -0,0 +1,257 @@
+'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.Game = factory();
+ }
+}(this, () => {
+ return class Game {
+ constructor(obj, l10n) {
+ this.folder = obj.folder || '';
+ this.globalMessages = obj.globalMessages || [];
+ this.masterlist = obj.masterlist || {};
+ this.plugins = obj.plugins || [];
+
+ this.loadOrder = undefined;
+ this.oldLoadOrder = undefined;
+
+ this._notApplicableString = l10n.translate('N/A');
+ }
+
+ get folder() {
+ return this._folder;
+ }
+
+ set folder(folder) {
+ if (folder !== this._folder) {
+ document.dispatchEvent(new CustomEvent('loot-game-folder-change', {
+ detail: { folder },
+ }));
+ }
+
+ this._folder = folder;
+ }
+
+ get globalMessages() {
+ return this._globalMessages;
+ }
+
+ set globalMessages(globalMessages) {
+ /* Update the message counts. */
+ let oldTotal = 0;
+ let newTotal = 0;
+ let oldWarns = 0;
+ let newWarns = 0;
+ let oldErrs = 0;
+ let newErrs = 0;
+
+ if (this._globalMessages) {
+ oldTotal = this._globalMessages.length;
+ this._globalMessages.forEach((message) => {
+ if (message.type === 'warn') {
+ ++oldWarns;
+ } else if (message.type === 'error') {
+ ++oldErrs;
+ }
+ });
+ }
+
+ if (globalMessages) {
+ newTotal = globalMessages.length;
+
+ globalMessages.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-game-global-messages-change', {
+ detail: {
+ totalDiff: newTotal - oldTotal,
+ warningDiff: newWarns - oldWarns,
+ errorDiff: newErrs - oldErrs,
+ messages: globalMessages,
+ },
+ }));
+ }
+
+ this._globalMessages = globalMessages;
+ }
+
+ get masterlist() {
+ return this._masterlist;
+ }
+
+ set masterlist(masterlist) {
+ if (masterlist !== this._masterlist
+ && (masterlist === undefined || this._masterlist === undefined
+ || masterlist.revision !== this._masterlist.revision
+ || masterlist.date !== this._masterlist.date)) {
+ let revision = this._notApplicableString;
+ let date = this._notApplicableString;
+ if (masterlist && masterlist.revision) {
+ revision = masterlist.revision;
+ }
+ if (masterlist && masterlist.date) {
+ date = masterlist.date;
+ }
+
+ document.dispatchEvent(new CustomEvent('loot-game-masterlist-change', {
+ detail: {
+ revision,
+ date,
+ },
+ }));
+ }
+
+ this._masterlist = masterlist;
+ }
+
+ get plugins() {
+ return this._plugins;
+ }
+
+ set plugins(plugins) {
+ /* Update plugin and message counts. Unlike for global messages
+ it's not worth calculating the count differences, just count
+ from zero. */
+ let totalMessageNo = 0;
+ let warnMessageNo = 0;
+ let errorMessageNo = 0;
+ let activePluginNo = 0;
+ let dirtyPluginNo = 0;
+
+ /* Include global messages in the count. */
+ if (this.globalMessages) {
+ totalMessageNo = this.globalMessages.length;
+ this.globalMessages.forEach((message) => {
+ if (message.type === 'warn') {
+ ++warnMessageNo;
+ } else if (message.type === 'error') {
+ ++errorMessageNo;
+ }
+ });
+ }
+
+ plugins.forEach((plugin) => {
+ if (plugin.isActive) {
+ ++activePluginNo;
+ }
+ if (plugin.isDirty) {
+ ++dirtyPluginNo;
+ }
+ if (plugin.messages) {
+ totalMessageNo += plugin.messages.length;
+ plugin.messages.forEach((message) => {
+ if (message.type === 'warn') {
+ ++warnMessageNo;
+ } else if (message.type === 'error') {
+ ++errorMessageNo;
+ }
+ });
+ }
+ });
+
+ document.dispatchEvent(new CustomEvent('loot-game-plugins-change', {
+ detail: {
+ valuesAreTotals: true,
+ totalMessageNo,
+ warnMessageNo,
+ errorMessageNo,
+ totalPluginNo: plugins.length,
+ activePluginNo,
+ dirtyPluginNo,
+ },
+ }));
+
+ this._plugins = plugins;
+ }
+
+ appendPlugin(plugin) {
+ let totalChange = 0;
+ let warnChange = 0;
+ let errorChange = 0;
+ let activeChange = 0;
+ let dirtyChange = 0;
+
+ if (plugin.isActive) {
+ ++activeChange;
+ }
+ if (plugin.isDirty) {
+ ++dirtyChange;
+ }
+ if (plugin.messages) {
+ totalChange += plugin.messages.length;
+ plugin.messages.forEach((message) => {
+ if (message.type === 'warn') {
+ ++warnChange;
+ } else if (message.type === 'error') {
+ ++errorChange;
+ }
+ });
+ }
+
+ document.dispatchEvent(new CustomEvent('loot-game-plugins-change', {
+ detail: {
+ valuesAreTotals: false,
+ totalMessageNo: totalChange,
+ warnMessageNo: warnChange,
+ errorMessageNo: errorChange,
+ totalPluginNo: 1,
+ activePluginNo: activeChange,
+ dirtyPluginNo: dirtyChange,
+ },
+ }));
+
+ this._plugins.push(plugin);
+ }
+
+ removePluginAtIndex(index) {
+ let totalChange = 0;
+ let warnChange = 0;
+ let errorChange = 0;
+ let activeChange = 0;
+ let dirtyChange = 0;
+
+ if (this._plugins[index].isActive) {
+ --activeChange;
+ }
+ if (this._plugins[index].isDirty) {
+ --dirtyChange;
+ }
+ if (this._plugins[index].messages) {
+ totalChange -= this._plugins[index].messages.length;
+ this._plugins[index].messages.forEach((message) => {
+ if (message.type === 'warn') {
+ --warnChange;
+ } else if (message.type === 'error') {
+ --errorChange;
+ }
+ });
+ }
+
+ document.dispatchEvent(new CustomEvent('loot-game-plugins-change', {
+ detail: {
+ valuesAreTotals: false,
+ totalMessageNo: totalChange,
+ warnMessageNo: warnChange,
+ errorMessageNo: errorChange,
+ totalPluginNo: -1,
+ activePluginNo: activeChange,
+ dirtyPluginNo: dirtyChange,
+ },
+ }));
+
+ this._plugins.splice(index, 1);
+ }
+ };
+}));
diff --git a/src/gui/html/js/helpers.js b/src/gui/html/js/helpers.js
index 9f506cb1..37a0118a 100644
--- a/src/gui/html/js/helpers.js
+++ b/src/gui/html/js/helpers.js
@@ -141,3 +141,19 @@ function setFilteredUIData(filtersState) {
document.getElementById('hiddenMessageNo').textContent = hiddenMessageNo;
});
}
+/* Call whenever game is changed or game menu / game table are rewritten. */
+function updateSelectedGame(gameFolder) {
+ document.getElementById('gameMenu').value = gameFolder;
+
+ /* Also disable deletion of the game's row in the settings dialog. */
+ const table = document.getElementById('gameTable');
+ for (let i = 0; i < table.tBodies[0].rows.length; ++i) {
+ if (table.tBodies[0].rows[i].getElementsByClassName('folder').length > 0) {
+ if (table.tBodies[0].rows[i].getElementsByClassName('folder')[0].value === gameFolder) {
+ table.setReadOnly(table.tBodies[0].rows[i], ['delete']);
+ } else {
+ table.setReadOnly(table.tBodies[0].rows[i], ['delete'], false);
+ }
+ }
+ }
+}
diff --git a/src/gui/html/js/loot.js b/src/gui/html/js/loot.js
index 6cb6d401..b6aa2df7 100644
--- a/src/gui/html/js/loot.js
+++ b/src/gui/html/js/loot.js
@@ -1,110 +1,6 @@
var loot = {
installedGames: [],
settings: {},
- game: {
- folder: '',
- globalMessages: [],
- masterlist: {},
- plugins: [],
-
- /* Call whenever game is changed or game menu / game table are rewritten. */
- updateSelectedGame: function() {
- document.getElementById('gameMenu').value = this.folder;
-
- /* Also disable deletion of the game's row in the settings dialog. */
- var table = document.getElementById('gameTable');
- for (var i = 0; i < table.tBodies[0].rows.length; ++i) {
- if (table.tBodies[0].rows[i].getElementsByClassName('folder').length > 0) {
- if (table.tBodies[0].rows[i].getElementsByClassName('folder')[0].value == this.folder) {
- table.setReadOnly(table.tBodies[0].rows[i], ['delete']);
- } else {
- table.setReadOnly(table.tBodies[0].rows[i], ['delete'], false);
- }
- }
- }
- },
-
- /* Call whenever game is changed. */
- updateRedatePluginsButtonState: function() {
- /* Also enable/disable the redate plugins option. */
- var index = undefined;
- for (var i = 0; i < loot.settings.games.length; ++i) {
- if (loot.settings.games[i].folder == this.folder) {
- index = i;
- break;
- }
- }
- var redateButton = document.getElementById('redatePluginsButton');
- if (index != undefined && loot.settings.games[index].type == 'Skyrim') {
- redateButton.removeAttribute('disabled');
- } else {
- redateButton.setAttribute('disabled', true);
- }
- },
-
- pluginsObserver: function(changes) {
- changes.forEach(function(change){
- /* Need to handle plugin addition and removal.
- Update plugin and message counts. */
- var totalChange = 0;
- var warnChange = 0;
- var errorChange = 0;
- var activeChange = 0;
- var dirtyChange = 0;
-
- if (change.addedCount > 0) {
- /* Addition */
- for (var i = change.index; i < change.index + change.addedCount; ++i) {
- if (change.object[i].isActive) {
- ++activeChange;
- }
- if (change.object[i].isDirty) {
- ++dirtyChange;
- }
- if (change.object[i].messages) {
- totalChange += change.object[i].messages.length;
- change.object[i].messages.forEach(function(message) {
- if (message.type == 'warn') {
- ++warnChange;
- } else if (message.type == 'error') {
- ++errorChange;
- }
- });
- }
- }
- }
- if (change.removed.length > 0) {
- /* Removal */
- change.removed.forEach(function(plugin){
- if (plugin.isActive) {
- --activeChange;
- }
- if (plugin.isDirty) {
- --dirtyChange;
- }
- if (plugin.messages) {
- totalChange -= plugin.messages.length;
- plugin.messages.forEach(function(message) {
- if (message.type == 'warn') {
- --warnChange;
- } else if (message.type == 'error') {
- --errorChange;
- }
- });
- }
- });
- }
- document.getElementById('filterTotalMessageNo').textContent = parseInt(document.getElementById('filterTotalMessageNo').textContent, 10) + totalChange;
- document.getElementById('totalMessageNo').textContent = document.getElementById('filterTotalMessageNo').textContent;
- document.getElementById('totalWarningNo').textContent = parseInt(document.getElementById('totalWarningNo').textContent, 10) + warnReduction;
- document.getElementById('totalErrorNo').textContent = parseInt(document.getElementById('totalErrorNo').textContent, 10) + errorReduction;
- document.getElementById('filterTotalPluginNo').textContent = parseInt(document.getElementById('filterTotalPluginNo').textContent, 10) + change.addedCount - change.removed.length;
- document.getElementById('totalPluginNo').textContent = document.getElementById('filterTotalPluginNo').textContent;
- document.getElementById('activePluginNo').textContent = parseInt(document.getElementById('activePluginNo').textContent, 10) + activeReduction;
- document.getElementById('dirtyPluginNo').textContent = parseInt(document.getElementById('dirtyPluginNo').textContent, 10) + dirtyReduction;
- });
- }
- },
/* Call whenever installedGames is changed or game menu is rewritten. */
updateEnabledGames: function() {
@@ -156,7 +52,7 @@ var loot = {
document.getElementById('updateMasterlist').checked = this.settings.updateMasterlist;
this.updateEnabledGames();
- this.game.updateSelectedGame();
+ updateSelectedGame(this.game.folder);
},
/* Observer for loot members. */
@@ -170,129 +66,6 @@ var loot = {
});
},
- /* Observer for loot.game members. */
- gameObserver: function(changes) {
- changes.forEach(function(change){
- if (change.name == 'folder') {
- change.object.updateSelectedGame();
- change.object.updateRedatePluginsButtonState();
- } else if (change.name == 'masterlist') {
- if (change.object[change.name] && change.object[change.name].revision) {
- document.getElementById('masterlistRevision').textContent = change.object[change.name].revision;
- } else {
- document.getElementById('masterlistRevision').textContent = loot.l10n.translate("N/A");
- }
- if (change.object[change.name] && change.object[change.name].date) {
- document.getElementById('masterlistDate').textContent = change.object[change.name].date;
- } else {
- document.getElementById('masterlistDate').textContent = loot.l10n.translate("N/A");
- }
- } else if (change.name == 'globalMessages') {
- /* For the messages, they don't have a JS 'class' so need to everything
- here. Count up the global message types that exist, then remove them
- and add the new ones, counting their types, then update the message
- counts. I tried using an observer for this, but it wouldn't fire for
- some reason. */
-
- var oldTotal = change.oldValue.length;
- var newTotal = 0;
- var oldWarn = 0;
- var newWarn = 0;
- var oldErr = 0;
- var newErr = 0;
-
- /* Count up old warnings and errors. */
- change.oldValue.forEach(function(message){
- if (message.type == 'warn') {
- ++oldWarn;
- } else if (message.type == 'error') {
- ++oldErr;
- }
- });
-
- /* Remove old messages from UI. */
- var generalMessagesList = document.getElementById('summary').getElementsByTagName('ul')[0];
- while (generalMessagesList.firstElementChild) {
- generalMessagesList.removeChild(generalMessagesList.firstElementChild);
- }
-
- /* Add new messages. */
- if (change.object[change.name]) {
- newTotal = change.object[change.name].length;
- change.object[change.name].forEach(function(message){
- var li = document.createElement('li');
- li.className = message.type;
- /* Use the Marked library for Markdown formatting support. */
- li.innerHTML = marked(message.content[0].str);
- generalMessagesList.appendChild(li);
-
- if (li.className == 'warn') {
- ++newWarn;
- } else if (li.className == 'error') {
- ++newErr;
- }
- });
- }
-
- /* Update message counts in UI. */
- document.getElementById('filterTotalMessageNo').textContent = parseInt(document.getElementById('filterTotalMessageNo').textContent, 10) + newTotal - oldTotal;
- document.getElementById('totalMessageNo').textContent = document.getElementById('filterTotalMessageNo').textContent;
- document.getElementById('totalWarningNo').textContent = parseInt(document.getElementById('totalWarningNo').textContent, 10) + newWarn - oldWarn;
- document.getElementById('totalErrorNo').textContent = parseInt(document.getElementById('totalErrorNo').textContent, 10) + newErr - oldErr;
- } else if (change.name == 'plugins') {
- /* Update plugin and message counts. Unlike for global messages
- it's not worth calculating the count differences, just count
- from zero. */
- var totalMessageNo = 0;
- var warnMessageNo = 0;
- var errorMessageNo = 0;
- var activePluginNo = 0;
- var dirtyPluginNo = 0;
-
- if (change.object.globalMessages) {
- totalMessageNo = change.object.globalMessages.length;
- change.object.globalMessages.forEach(function(message){
- if (message.type == 'warn') {
- ++warnMessageNo;
- } else if (message.type == 'error') {
- ++errorMessageNo;
- }
- });
- }
-
- change.object[change.name].forEach(function(plugin) {
- if (plugin.isActive) {
- ++activePluginNo;
- }
- if (plugin.isDirty) {
- ++dirtyPluginNo;
- }
- if (plugin.messages) {
- totalMessageNo += plugin.messages.length;
- plugin.messages.forEach(function(message) {
- if (message.type == 'warn') {
- ++warnMessageNo;
- } else if (message.type == 'error') {
- ++errorMessageNo;
- }
- });
- }
- });
- document.getElementById('filterTotalMessageNo').textContent = totalMessageNo;
- document.getElementById('totalMessageNo').textContent = totalMessageNo;
- document.getElementById('totalWarningNo').textContent = warnMessageNo;
- document.getElementById('totalErrorNo').textContent = errorMessageNo;
- document.getElementById('filterTotalPluginNo').textContent = change.object[change.name].length;
- document.getElementById('totalPluginNo').textContent = change.object[change.name].length;
- document.getElementById('activePluginNo').textContent = activePluginNo;
- document.getElementById('dirtyPluginNo').textContent = dirtyPluginNo;
-
- /* Register a new array observer. */
- Array.observe(change.object[change.name], change.object.pluginsObserver);
- }
- });
- },
-
/* Returns a cefQuery as a Promise. */
query: function(request) {
return new Promise(function(resolve, reject) {
diff --git a/src/tests/gui/html/js/test.html b/src/tests/gui/html/js/test.html
index dcf62cdb..77e945f6 100644
--- a/src/tests/gui/html/js/test.html
+++ b/src/tests/gui/html/js/test.html
@@ -12,9 +12,11 @@
+
+