Fixed issues with Active Scripts UI. Implemented event emitter for Active Scripts UI

This commit is contained in:
danielyxie 2019-05-17 13:41:16 -07:00
parent c1ec3c5eba
commit 3b7f9c9fb0
15 changed files with 160 additions and 92 deletions

@ -13,6 +13,12 @@
margin: 6px; margin: 6px;
padding: 4px; padding: 4px;
} }
.accordion-header {
> pre {
color: white;
}
}
} }
.active-scripts-server-header { .active-scripts-server-header {
@ -26,31 +32,32 @@
text-align: left; text-align: left;
border: none; border: none;
outline: none; outline: none;
}
.active-scripts-server-header.active, &:after {
.active-scripts-server-header:hover {
background-color: #555;
}
.active-scripts-server-header.active:hover {
background-color: #666;
}
.active-scripts-server-header:after {
content: '\02795'; /* "plus" sign (+) */ content: '\02795'; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125; font-size: $defaultFontSize * 0.8125;
color: #fff; color: #fff;
float: right; float: right;
margin-left: 5px; margin-left: 5px;
}
&.active, &:hover {
background-color: #555;
}
} }
.active-scripts-server-header.active:after { .active-scripts-server-header.active {
&:after {
content: "\2796"; /* "minus" sign (-) */ content: "\2796"; /* "minus" sign (-) */
font-size: $defaultFontSize * 0.8125; font-size: $defaultFontSize * 0.8125;
color: #fff; color: #fff;
float: right; float: right;
margin-left: 5px; margin-left: 5px;
}
&:hover {
background-color: #666;
}
} }
.active-scripts-server-panel { .active-scripts-server-panel {
@ -59,24 +66,23 @@
width: 55%; width: 55%;
margin-left: 5%; margin-left: 5%;
display: none; display: none;
}
.active-scripts-server-panel div, div, ul, ul > li {
.active-scripts-server-panel ul,
.active-scripts-server-panel ul > li {
background-color: #555; background-color: #555;
}
} }
.active-scripts-script-header { .active-scripts-script-header {
background-color: #555; background-color: #555;
color: var(--my-font-color);
padding: 4px 25px 4px 10px;
cursor: pointer;
width: auto;
text-align: left;
border: none; border: none;
color: var(--my-font-color);
cursor: pointer;
display: block;
outline: none; outline: none;
padding: 4px 25px 4px 10px;
position: relative; position: relative;
text-align: left;
width: auto;
&:after { &:after {
content: '\02795'; /* "plus" sign (+) */ content: '\02795'; /* "plus" sign (+) */
@ -104,13 +110,14 @@
} }
.active-scripts-script-panel { .active-scripts-script-panel {
padding: 0 18px;
background-color: #555; background-color: #555;
width: auto;
display: none; display: none;
font-size: 14px;
margin-bottom: 6px; margin-bottom: 6px;
padding: 0 18px;
width: auto;
p, h2, ul, li { pre, h2, ul, li {
background-color: #555; background-color: #555;
width: auto; width: auto;
color: #fff; color: #fff;

@ -243,8 +243,8 @@ a:visited {
/* Accordion menus (Header with collapsible panel) */ /* Accordion menus (Header with collapsible panel) */
.accordion-header { .accordion-header {
background-color: #444; background-color: #444;
font-size: $defaultFontSize * 1.25;
color: #fff; color: #fff;
font-size: $defaultFontSize * 1.25;
margin: 6px 6px 0 6px; margin: 6px 6px 0 6px;
padding: 4px 6px; padding: 4px 6px;
cursor: pointer; cursor: pointer;

@ -0,0 +1,6 @@
/**
* Event emitter that triggers when scripts are started/stopped
*/
import { EventEmitter } from "../utils/EventEmitter";
export const WorkerScriptStartStopEventEmitter = new EventEmitter();

@ -4,6 +4,7 @@
*/ */
import { WorkerScript } from "./WorkerScript"; import { WorkerScript } from "./WorkerScript";
import { workerScripts } from "./WorkerScripts"; import { workerScripts } from "./WorkerScripts";
import { WorkerScriptStartStopEventEmitter } from "./WorkerScriptStartStopEventEmitter";
import { RunningScript } from "../Script/RunningScript"; import { RunningScript } from "../Script/RunningScript";
import { AllServers } from "../Server/AllServers"; import { AllServers } from "../Server/AllServers";
@ -106,6 +107,7 @@ function removeWorkerScript(id: WorkerScript | number): void {
// Delete script from global pool (workerScripts) // Delete script from global pool (workerScripts)
workerScripts.splice(<number>index, 1); workerScripts.splice(<number>index, 1);
WorkerScriptStartStopEventEmitter.emitEvent();
} }
/** /**

@ -5,6 +5,7 @@
import { killWorkerScript } from "./Netscript/killWorkerScript"; import { killWorkerScript } from "./Netscript/killWorkerScript";
import { WorkerScript } from "./Netscript/WorkerScript"; import { WorkerScript } from "./Netscript/WorkerScript";
import { workerScripts } from "./Netscript/WorkerScripts"; import { workerScripts } from "./Netscript/WorkerScripts";
import { WorkerScriptStartStopEventEmitter } from "./Netscript/WorkerScriptStartStopEventEmitter";
import { CONSTANTS } from "./Constants"; import { CONSTANTS } from "./Constants";
import { Engine } from "./engine"; import { Engine } from "./engine";
@ -25,7 +26,6 @@ import {
} from "./Script/ScriptHelpers"; } from "./Script/ScriptHelpers";
import { AllServers } from "./Server/AllServers"; import { AllServers } from "./Server/AllServers";
import { Settings } from "./Settings/Settings"; import { Settings } from "./Settings/Settings";
import { EventEmitter } from "./utils/EventEmitter";
import { setTimeoutRef } from "./utils/SetTimeoutRef"; import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { generate } from "escodegen"; import { generate } from "escodegen";
@ -45,9 +45,6 @@ for (var i = 0; i < CONSTANTS.NumNetscriptPorts; ++i) {
NetscriptPorts.push(new NetscriptPort()); NetscriptPorts.push(new NetscriptPort());
} }
// WorkerScript-related event emitter. Used for the UI
export const WorkerScriptEventEmitter = new EventEmitter();
export function prestigeWorkerScripts() { export function prestigeWorkerScripts() {
for (var i = 0; i < workerScripts.length; ++i) { for (var i = 0; i < workerScripts.length; ++i) {
// TODO Signal event emitter // TODO Signal event emitter
@ -138,7 +135,7 @@ function startNetscript2Script(workerScript) {
} }
function startNetscript1Script(workerScript) { function startNetscript1Script(workerScript) {
var code = workerScript.code; const code = workerScript.code;
workerScript.running = true; workerScript.running = true;
//Process imports //Process imports
@ -448,9 +445,9 @@ export function addWorkerScript(runningScriptObj, server) {
// Start the script's execution // Start the script's execution
let p = null; // Script's resulting promise let p = null; // Script's resulting promise
if (s.name.endsWith(".js") || s.name.endsWith(".ns")) { if (s.name.endsWith(".js") || s.name.endsWith(".ns")) {
p = startNetscript2Script(workerScripts[i]); p = startNetscript2Script(s);
} else { } else {
p = startNetscript1Script(workerScripts[i]); p = startNetscript1Script(s);
if (!(p instanceof Promise)) { return; } if (!(p instanceof Promise)) { return; }
} }
@ -488,6 +485,7 @@ export function addWorkerScript(runningScriptObj, server) {
w.scriptRef.log("Script crashed with runtime error"); w.scriptRef.log("Script crashed with runtime error");
} else { } else {
w.scriptRef.log("Script killed"); w.scriptRef.log("Script killed");
return; // Already killed, so stop here
} }
w.running = false; w.running = false;
w.env.stopFlag = true; w.env.stopFlag = true;
@ -505,6 +503,7 @@ export function addWorkerScript(runningScriptObj, server) {
// Add the WorkerScript to the global pool // Add the WorkerScript to the global pool
workerScripts.push(s); workerScripts.push(s);
WorkerScriptStartStopEventEmitter.emitEvent();
return; return;
} }

@ -1,5 +1,7 @@
// Class representing a Script instance that is actively running. /**
// A Script can have multiple active instances * Class representing a Script instance that is actively running.
* A Script can have multiple active instances
*/
import { Script } from "./Script"; import { Script } from "./Script";
import { FconfSettings } from "../Fconf/FconfSettings"; import { FconfSettings } from "../Fconf/FconfSettings";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
@ -22,10 +24,8 @@ export class RunningScript {
// Script arguments // Script arguments
args: any[] = []; args: any[] = [];
// Holds a map of servers hacked, where server = key and the value for each // Map of [key: server ip] -> Hacking data. Used for offline progress calculations.
// server is an array of four numbers. The four numbers represent: // Hacking data format: [MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken]
// [MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken]
// This data is used for offline progress
dataMap: IMap<number[]> = {}; dataMap: IMap<number[]> = {};
// Script filename // Script filename

@ -1,6 +1,9 @@
// Class representing a script file /**
// This does NOT represent a script that is actively running and * Class representing a script file.
// being evaluated. See RunningScript for that *
* This does NOT represent a script that is actively running and
* being evaluated. See RunningScript for that
*/
import { calculateRamUsage } from "./RamCalculations"; import { calculateRamUsage } from "./RamCalculations";
import { Page, routing } from "../ui/navigationTracking"; import { Page, routing } from "../ui/navigationTracking";
@ -34,7 +37,6 @@ export class Script {
// IP of server that this script is on. // IP of server that this script is on.
server: string = ""; server: string = "";
constructor(fn: string="", code: string="", server: string="", otherScripts: Script[]=[]) { constructor(fn: string="", code: string="", server: string="", otherScripts: Script[]=[]) {
this.filename = fn; this.filename = fn;
this.code = code; this.code = code;
@ -44,6 +46,9 @@ export class Script {
if (this.code !== "") { this.updateRamUsage(otherScripts); } if (this.code !== "") { this.updateRamUsage(otherScripts); }
}; };
/**
* Download the script as a file
*/
download(): void { download(): void {
const filename = this.filename + ".js"; const filename = this.filename + ".js";
const file = new Blob([this.code], {type: 'text/plain'}); const file = new Blob([this.code], {type: 'text/plain'});
@ -63,10 +68,14 @@ export class Script {
} }
} }
// Save a script FROM THE SCRIPT EDITOR /**
* Save a script from the script editor
* @param {string} code - The new contents of the script
* @param {Script[]} otherScripts - Other scripts on the server. Used to process imports
*/
saveScript(code: string, serverIp: string, otherScripts: Script[]): void { saveScript(code: string, serverIp: string, otherScripts: Script[]): void {
if (routing.isOn(Page.ScriptEditor)) { if (routing.isOn(Page.ScriptEditor)) {
//Update code and filename // Update code and filename
this.code = code.replace(/^\s+|\s+$/g, ''); this.code = code.replace(/^\s+|\s+$/g, '');
const filenameElem: HTMLInputElement | null = document.getElementById("script-editor-filename") as HTMLInputElement; const filenameElem: HTMLInputElement | null = document.getElementById("script-editor-filename") as HTMLInputElement;
@ -75,18 +84,16 @@ export class Script {
return; return;
} }
this.filename = filenameElem!.value; this.filename = filenameElem!.value;
// Server
this.server = serverIp; this.server = serverIp;
//Calculate/update ram usage, execution time, etc.
this.updateRamUsage(otherScripts); this.updateRamUsage(otherScripts);
this.module = ""; this.module = "";
} }
} }
// Updates the script's RAM usage based on its code /**
* Calculates and updates the script's RAM usage based on its code
* @param {Script[]} otherScripts - Other scripts on the server. Used to process imports
*/
async updateRamUsage(otherScripts: Script[]) { async updateRamUsage(otherScripts: Script[]) {
var res = await calculateRamUsage(this.code, otherScripts); var res = await calculateRamUsage(this.code, otherScripts);
if (res > 0) { if (res > 0) {

@ -45,6 +45,7 @@ import { LocationName } from "./Locations/data/LocationNames";
import { LocationRoot } from "./Locations/ui/Root"; import { LocationRoot } from "./Locations/ui/Root";
import { checkForMessagesToSend, initMessages } from "./Message/MessageHelpers"; import { checkForMessagesToSend, initMessages } from "./Message/MessageHelpers";
import { inMission, currMission } from "./Missions"; import { inMission, currMission } from "./Missions";
import { workerScripts } from "./Netscript/WorkerScripts";
import { import {
loadAllRunningScripts, loadAllRunningScripts,
updateOnlineScriptTimes, updateOnlineScriptTimes,
@ -90,6 +91,8 @@ import { displayCharacterInfo } from "./ui/displayCharacterInfo";
import { Page, routing } from "./ui/navigationTracking"; import { Page, routing } from "./ui/navigationTracking";
import { numeralWrapper } from "./ui/numeralFormat"; import { numeralWrapper } from "./ui/numeralFormat";
import { setSettingsLabels } from "./ui/setSettingsLabels"; import { setSettingsLabels } from "./ui/setSettingsLabels";
import { ActiveScriptsRoot } from "./ui/ActiveScripts/Root";
import { initializeMainMenuHeaders } from "./ui/MainMenu/Headers"; import { initializeMainMenuHeaders } from "./ui/MainMenu/Headers";
import { initializeMainMenuLinks, MainMenuLinks } from "./ui/MainMenu/Links"; import { initializeMainMenuLinks, MainMenuLinks } from "./ui/MainMenu/Links";
@ -262,7 +265,10 @@ const Engine = {
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.activeScriptsContent.style.display = "block"; Engine.Display.activeScriptsContent.style.display = "block";
routing.navigateTo(Page.ActiveScripts); routing.navigateTo(Page.ActiveScripts);
updateActiveScriptsItems(); ReactDOM.render(
<ActiveScriptsRoot p={Player} workerScripts={workerScripts} />,
Engine.Display.activeScriptsContent
)
MainMenuLinks.ActiveScripts.classList.add("active"); MainMenuLinks.ActiveScripts.classList.add("active");
}, },
@ -474,7 +480,10 @@ const Engine = {
Engine.Display.terminalContent.style.display = "none"; Engine.Display.terminalContent.style.display = "none";
Engine.Display.characterContent.style.display = "none"; Engine.Display.characterContent.style.display = "none";
Engine.Display.scriptEditorContent.style.display = "none"; Engine.Display.scriptEditorContent.style.display = "none";
Engine.Display.activeScriptsContent.style.display = "none"; Engine.Display.activeScriptsContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.activeScriptsContent);
clearHacknetNodesUI(); clearHacknetNodesUI();
Engine.Display.createProgramContent.style.display = "none"; Engine.Display.createProgramContent.style.display = "none";
@ -805,13 +814,14 @@ const Engine = {
} }
if (Engine.Counters.updateActiveScriptsDisplay <= 0) { if (Engine.Counters.updateActiveScriptsDisplay <= 0) {
// Always update, but make the interval longer if the page isn't active
updateActiveScriptsItems();
if (routing.isOn(Page.ActiveScripts)) { if (routing.isOn(Page.ActiveScripts)) {
Engine.Counters.updateActiveScriptsDisplay = 5; ReactDOM.render(
} else { <ActiveScriptsRoot p={Player} workerScripts={workerScripts} />,
Engine.Counters.updateActiveScriptsDisplay = 10; Engine.Display.activeScriptsContent
)
} }
Engine.Counters.updateActiveScriptsDisplay = 5;
} }
if (Engine.Counters.updateDisplays <= 0) { if (Engine.Counters.updateDisplays <= 0) {

@ -15,7 +15,7 @@ type IProps = {
workerScripts: WorkerScript[]; workerScripts: WorkerScript[];
} }
export class ActiveScriptsRoot extends React.Component<IProps, any> { export class ActiveScriptsRoot extends React.Component<IProps> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
} }

@ -35,6 +35,7 @@ export function ScriptProduction(props: IProps): React.ReactElement {
<span id="active-scripts-total-prod-aug-total" className="money-gold"> <span id="active-scripts-total-prod-aug-total" className="money-gold">
{numeralWrapper.formatMoney(props.p.scriptProdSinceLastAug)} {numeralWrapper.formatMoney(props.p.scriptProdSinceLastAug)}
</span> </span>
(<span className="money-gold"> (<span className="money-gold">
<span id="active-scripts-total-prod-aug-avg" className="money-gold"> <span id="active-scripts-total-prod-aug-avg" className="money-gold">
{numeralWrapper.formatMoney(prodRateSinceLastAug)} {numeralWrapper.formatMoney(prodRateSinceLastAug)}

@ -28,7 +28,7 @@ export function ServerAccordion(props: IProps): React.ReactElement {
progress: server.ramUsed / server.maxRam, progress: server.ramUsed / server.maxRam,
totalTicks: 30 totalTicks: 30
}; };
const headerTxt = `${paddedName} ${createProgressBarText(barOptions)}`.replace(/\s/g, '&nbsp;'); const headerTxt = `${paddedName} ${createProgressBarText(barOptions)}`;
const scripts = props.workerScripts.map((ws) => { const scripts = props.workerScripts.map((ws) => {
return ( return (
@ -39,7 +39,7 @@ export function ServerAccordion(props: IProps): React.ReactElement {
return ( return (
<Accordion <Accordion
headerContent={ headerContent={
<p>{headerTxt}</p> <pre>{headerTxt}</pre>
} }
panelContent={ panelContent={
<ul>{scripts}</ul> <ul>{scripts}</ul>

@ -6,9 +6,10 @@ import * as React from "react";
import { ServerAccordion } from "./ServerAccordion"; import { ServerAccordion } from "./ServerAccordion";
import { WorkerScript } from "../../Netscript/WorkerScript";
import { WorkerScriptStartStopEventEmitter } from "../../Netscript/WorkerScriptStartStopEventEmitter";
import { getServer } from "../../Server/ServerHelpers"; import { getServer } from "../../Server/ServerHelpers";
import { BaseServer } from "../../Server/BaseServer"; import { BaseServer } from "../../Server/BaseServer";
import { WorkerScript } from "../../Netscript/WorkerScript";
// Map of server hostname -> all workerscripts on that server for all active scripts // Map of server hostname -> all workerscripts on that server for all active scripts
interface IServerData { interface IServerData {
@ -24,17 +25,37 @@ type IProps = {
workerScripts: WorkerScript[]; workerScripts: WorkerScript[];
}; };
export class ServerAccordions extends React.Component<IProps> { type IState = {
rerenderFlag: boolean;
}
const subscriberId = "ActiveScriptsUI";
export class ServerAccordions extends React.Component<IProps, IState> {
serverToScriptMap: IServerToScriptsMap = {}; serverToScriptMap: IServerToScriptsMap = {};
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
this.state = {
rerenderFlag: false,
}
this.updateServerToScriptsMap(); this.updateServerToScriptsMap();
// TODO this.rerender = this.rerender.bind(this);
// We subscribe to an event emitter that publishes whenever a script is }
// started/stopped. This allows us to only update the map when necessary
componentDidMount() {
WorkerScriptStartStopEventEmitter.addSubscriber({
cb: this.rerender,
id: subscriberId,
})
}
componentWillUnmount() {
WorkerScriptStartStopEventEmitter.removeSubscriber(subscriberId);
} }
updateServerToScriptsMap(): void { updateServerToScriptsMap(): void {
@ -60,6 +81,13 @@ export class ServerAccordions extends React.Component<IProps> {
this.serverToScriptMap = map; this.serverToScriptMap = map;
} }
rerender() {
this.updateServerToScriptsMap();
this.setState((prevState) => {
return { rerenderFlag: !prevState.rerenderFlag }
});
}
render() { render() {
const elems = Object.keys(this.serverToScriptMap).map((serverName) => { const elems = Object.keys(this.serverToScriptMap).map((serverName) => {
const data = this.serverToScriptMap[serverName]; const data = this.serverToScriptMap[serverName];

@ -12,6 +12,7 @@ import { AccordionButton } from "../React/AccordionButton";
import { killWorkerScript } from "../../Netscript/killWorkerScript"; import { killWorkerScript } from "../../Netscript/killWorkerScript";
import { WorkerScript } from "../../Netscript/WorkerScript"; import { WorkerScript } from "../../Netscript/WorkerScript";
import { dialogBoxCreate } from "../../../utils/DialogBox";
import { logBoxCreate } from "../../../utils/LogBox"; import { logBoxCreate } from "../../../utils/LogBox";
import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
import { arrayToString } from "../../../utils/helpers/arrayToString"; import { arrayToString } from "../../../utils/helpers/arrayToString";
@ -24,8 +25,14 @@ export function WorkerScriptAccordion(props: IProps): React.ReactElement {
const workerScript = props.workerScript; const workerScript = props.workerScript;
const scriptRef = workerScript.scriptRef; const scriptRef = workerScript.scriptRef;
const logClickHandler = logBoxCreate.bind(null, scriptRef); const logClickHandler = logBoxCreate.bind(null, scriptRef);
const killScriptButton = killWorkerScript.bind(null, scriptRef, scriptRef.server); const killScript = killWorkerScript.bind(null, scriptRef, scriptRef.server);
function killScriptClickHandler() {
killScript();
dialogBoxCreate("Killing script");
}
// Calculations for script stats // Calculations for script stats
const onlineMps = scriptRef.onlineMoneyMade / scriptRef.onlineRunningTime; const onlineMps = scriptRef.onlineMoneyMade / scriptRef.onlineRunningTime;
@ -37,31 +44,30 @@ export function WorkerScriptAccordion(props: IProps): React.ReactElement {
<Accordion <Accordion
headerClass="active-scripts-script-header" headerClass="active-scripts-script-header"
headerContent={ headerContent={
<> <>{props.workerScript.name}</>
</>
} }
panelClass="active-scripts-script-panel" panelClass="active-scripts-script-panel"
panelContent={ panelContent={
<> <>
<p>Threads: {props.workerScript.scriptRef.threads}</p> <pre>Threads: {props.workerScript.scriptRef.threads}</pre>
<p>Args: {arrayToString(props.workerScript.args)}</p> <pre>Args: {arrayToString(props.workerScript.args)}</pre>
<p>Online Time: {convertTimeMsToTimeElapsedString(scriptRef.onlineRunningTime * 1e3)}</p> <pre>Online Time: {convertTimeMsToTimeElapsedString(scriptRef.onlineRunningTime * 1e3)}</pre>
<p>Offline Time: {convertTimeMsToTimeElapsedString(scriptRef.offlineRunningTime * 1e3)}</p> <pre>Offline Time: {convertTimeMsToTimeElapsedString(scriptRef.offlineRunningTime * 1e3)}</pre>
<p>Total online production: {numeralWrapper.formatMoney(scriptRef.onlineMoneyMade)}</p> <pre>Total online production: {numeralWrapper.formatMoney(scriptRef.onlineMoneyMade)}</pre>
<p>{(Array(26).join(" ") + numeralWrapper.formatBigNumber(scriptRef.onlineExpGained) + " hacking exp").replace( / /g, "&nbsp;")}</p> <pre>{(Array(26).join(" ") + numeralWrapper.formatBigNumber(scriptRef.onlineExpGained) + " hacking exp")}</pre>
<p>Online production rate: {numeralWrapper.formatMoney(onlineMps)} / second</p> <pre>Online production rate: {numeralWrapper.formatMoney(onlineMps)} / second</pre>
<p>{(Array(25).join(" ") + numeralWrapper.formatBigNumber(onlineEps) + " hacking exp / second").replace( / /g, "&nbsp;")}</p> <pre>{(Array(25).join(" ") + numeralWrapper.formatBigNumber(onlineEps) + " hacking exp / second")}</pre>
<p>Total offline production: {numeralWrapper.formatMoney(scriptRef.offlineMoneyMade)}</p> <pre>Total offline production: {numeralWrapper.formatMoney(scriptRef.offlineMoneyMade)}</pre>
<p>{(Array(27).join(" ") + numeralWrapper.formatBigNumber(scriptRef.offlineExpGained) + " hacking exp").replace( / /g, "&nbsp;")}</p> <pre>{(Array(27).join(" ") + numeralWrapper.formatBigNumber(scriptRef.offlineExpGained) + " hacking exp")}</pre>
<p>Offline production rate: {numeralWrapper.formatMoney(offlineMps)} / second</p> <pre>Offline production rate: {numeralWrapper.formatMoney(offlineMps)} / second</pre>
<p>{(Array(26).join(" ") + numeralWrapper.formatBigNumber(offlineEps) + " hacking exp / second").replace( / /g, "&nbsp;")}</p> <pre>{(Array(26).join(" ") + numeralWrapper.formatBigNumber(offlineEps) + " hacking exp / second")}</pre>
<AccordionButton <AccordionButton
onClick={logClickHandler} onClick={logClickHandler}
text="Log" text="Log"
/> />
<AccordionButton <AccordionButton
onClick={killScriptButton} onClick={killScriptClickHandler}
text="Kill Script" text="Kill Script"
/> />
</> </>

@ -9,6 +9,7 @@ type IProps = {
panelClass?: string; // Override default class panelClass?: string; // Override default class
panelContent: React.ReactElement; panelContent: React.ReactElement;
panelInitiallyOpened?: boolean; panelInitiallyOpened?: boolean;
style?: string;
} }
type IState = { type IState = {

@ -23,7 +23,6 @@ function logBoxInit(): void {
console.error(`Could not find LogBox's close button`); console.error(`Could not find LogBox's close button`);
return; return;
} }
logBoxClose();
closeButton.addEventListener("click", function() { closeButton.addEventListener("click", function() {
logBoxClose(); logBoxClose();
@ -40,6 +39,8 @@ function logBoxInit(): void {
logBoxContainer = document.getElementById("log-box-container"); logBoxContainer = document.getElementById("log-box-container");
logText = document.getElementById("log-box-text"); logText = document.getElementById("log-box-text");
logBoxClose();
document.removeEventListener("DOMContentLoaded", logBoxInit); document.removeEventListener("DOMContentLoaded", logBoxInit);
}; };