/** * The sidebar tab which displays various game settings, help messages, and configuration options. * The Settings sidebar is the furthest-to-right using a triple-cogs icon. * @extends {SidebarTab} */ class Settings extends SidebarTab { /** @override */ static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { id: "settings", template: "templates/sidebar/settings.html", title: "Settings" }); } /* -------------------------------------------- */ /** @override */ async getData(options={}) { const context = await super.getData(options); // Check for core update let coreUpdate; if ( game.user.isGM && game.data.coreUpdate.hasUpdate ) { coreUpdate = game.i18n.format("SETUP.UpdateAvailable", { type: game.i18n.localize("Software"), channel: game.data.coreUpdate.channel, version: game.data.coreUpdate.version }); } // Check for system update let systemUpdate; if ( game.user.isGM && game.data.systemUpdate.hasUpdate ) { systemUpdate = game.i18n.format("SETUP.UpdateAvailable", { type: game.i18n.localize("System"), channel: game.data.system.title, version: game.data.systemUpdate.version }); } const issues = CONST.WORLD_DOCUMENT_TYPES.reduce((count, documentName) => { const collection = CONFIG[documentName].collection.instance; return count + collection.invalidDocumentIds.size; }, 0) + Object.values(game.issues.packageCompatibilityIssues).reduce((count, {error}) => { return count + error.length; }, 0) + Object.keys(game.issues.usabilityIssues).length; // Return rendering context const isDemo = game.data.demoMode; return foundry.utils.mergeObject(context, { system: game.system, release: game.data.release, versionDisplay: game.release.display, canConfigure: game.user.can("SETTINGS_MODIFY") && !isDemo, canEditWorld: game.user.hasRole("GAMEMASTER") && !isDemo, canManagePlayers: game.user.isGM && !isDemo, canReturnSetup: game.user.hasRole("GAMEMASTER") && !isDemo, modules: game.modules.reduce((n, m) => n + (m.active ? 1 : 0), 0), issues, isDemo, coreUpdate, systemUpdate }); } /* -------------------------------------------- */ /** @override */ activateListeners(html) { html.find("button[data-action]").click(this._onSettingsButton.bind(this)); html.find(".notification-pip.update").click(this._onUpdateNotificationClick.bind(this)); } /* -------------------------------------------- */ /** * Delegate different actions for different settings buttons * @param {MouseEvent} event The originating click event * @private */ _onSettingsButton(event) { event.preventDefault(); const button = event.currentTarget; switch (button.dataset.action) { case "configure": game.settings.sheet.render(true); break; case "modules": new ModuleManagement().render(true); break; case "world": new WorldConfig(game.world).render(true); break; case "players": return ui.menu.items.players.onClick(); case "setup": return game.shutDown(); case "support": new SupportDetails().render(true); break; case "controls": new KeybindingsConfig().render(true); break; case "tours": new ToursManagement().render(true); break; case "docs": new FrameViewer("https://foundryvtt.com/kb", { title: "SIDEBAR.Documentation" }).render(true); break; case "wiki": new FrameViewer("https://foundryvtt.wiki/", { title: "SIDEBAR.Wiki" }).render(true); break; case "invitations": new InvitationLinks().render(true); break; case "logout": return ui.menu.items.logout.onClick(); } } /* -------------------------------------------- */ /** * Executes with the update notification pip is clicked * @param {MouseEvent} event The originating click event * @private */ _onUpdateNotificationClick(event) { event.preventDefault(); const key = event.target.dataset.action === "core-update" ? "CoreUpdateInstructions" : "SystemUpdateInstructions"; ui.notifications.notify(game.i18n.localize(`SETUP.${key}`)); } } /* -------------------------------------------- */ /** * A simple window application which shows the built documentation pages within an iframe * @type {Application} */ class FrameViewer extends Application { constructor(url, options) { super(options); this.url = url; } /* -------------------------------------------- */ /** @override */ static get defaultOptions() { const options = super.defaultOptions; const h = window.innerHeight * 0.9; const w = Math.min(window.innerWidth * 0.9, 1200); options.height = h; options.width = w; options.top = (window.innerHeight - h) / 2; options.left = (window.innerWidth - w) / 2; options.id = "documentation"; options.template = "templates/apps/documentation.html"; return options; } /* -------------------------------------------- */ /** @override */ async getData(options={}) { return { src: this.url }; } /* -------------------------------------------- */ /** @override */ async close(options) { this.element.find("#docs").remove(); return super.close(options); } }