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 @@ + +