Implemented new Editor Option: CodeMirror. (Vim mode not 100% done yet)

This commit is contained in:
danielyxie 2019-01-27 14:08:45 -08:00
parent a2237d4319
commit 8d2c007bcb
39 changed files with 69645 additions and 9328 deletions

@ -0,0 +1,40 @@
@import "theme";
/**
* Customized styling for the Code Mirror editor
*/
#codemirror-form-wrapper {
height: 80%;
margin: 10px 0px 10px 6px;
}
.CodeMirror {
height: 100%;
width: 100%;
border: 2px solid var(--my-highlight-color);
z-index: 1;
font-family: $fontFamily;
font-size: $defaultFontSize;
}
/**
* Highlight matches
*/
.cm-matchhighlight {
background-color: #8F908A;
}
.CodeMirror-selection-highlight-scrollbar {
background-color: #8F908A;
}
/**
* Show Invisibles
*/
.cm-whitespace::before {
position: absolute;
pointer-events: none;
color: #404F7D;
}

@ -18,116 +18,6 @@
position: fixed; position: fixed;
} }
/* Script Editor */
#script-editor-container {
background-color: transparent;
}
#javascript-editor {
margin: 10px;
height: 80%;
width: 100%;
margin-left: 6px;
padding-left: 6px;
padding-top: 6px;
padding-bottom: 6px;
border: 2px solid var(--my-highlight-color);
z-index: 1;
font-family: $fontFamily;
}
.ace_line,
.ace_line * {
background-color: transparent;
margin: 0;
padding: 0;
}
.ace_text-input {
font-size: $defaultFontSize;
background-color: transparent;
}
/* This temp element is used for auto adjusting filename field */
.tmp-element {
visibility: hidden;
white-space: pre;
}
#script-editor-container {
position: fixed;
padding-top: 10px;
}
#script-editor-buttons-wrapper {
width: 100%;
padding-right: 0;
margin-right: 0;
}
#script-editor-wrapper {
height: 100%;
width: 70%;
background: transparent;
}
#script-editor-filename-wrapper {
background-color: #555;
margin-left: 6px;
margin-right: 0;
padding-left: 6px;
width: 100%;
border: 2px solid var(--my-highlight-color);
}
#script-editor-filename-tag {
display: inline-block;
padding-top: 10px;
padding-bottom: 0;
float: center;
background-color: #555;
color: #fff;
}
#script-editor-filename {
$boxShadowArgs: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
@include boxShadow($boxShadowArgs);
background-color: #555;
display: inline-block;
float: center;
resize: none;
color: #fff;
margin: 4px;
padding: 2px;
border: 2px solid var(--my-highlight-color);
}
#script-editor-status {
float: left;
color: #fff;
}
#script-editor-options-panel {
position: absolute;
right: 9%;
bottom: 15%;
border: 2px solid #fff;
width: 19%;
background-color: #444;
padding: 2px;
overflow: auto;
z-index: 1;
color: #fff;
}
#script-editor-options-panel fieldset {
margin-top: 8px;
margin-bottom: 8px;
padding: 2px;
font-size: $defaultFontSize * 0.75;
}
/* Active scripts */ /* Active scripts */
.active-scripts-list { .active-scripts-list {
list-style-type: none; list-style-type: none;

122
css/scripteditor.scss Normal file

@ -0,0 +1,122 @@
@import "mixins";
@import "theme";
/**
* Styling for Script Editor (both Ace and CodeMirror)
*/
#script-editor-container {
background-color: transparent;
}
#ace-editor {
margin: 10px;
height: 80%;
width: 100%;
margin-left: 6px;
padding-left: 6px;
padding-top: 6px;
padding-bottom: 6px;
border: 2px solid var(--my-highlight-color);
z-index: 1;
font-family: $fontFamily;
}
/* This temp element is used for auto adjusting filename field */
.tmp-element {
visibility: hidden;
white-space: pre;
}
#script-editor-container {
position: fixed;
padding-top: 10px;
}
#script-editor-buttons-wrapper {
width: 100%;
padding-right: 0;
margin-right: 0;
}
#script-editor-wrapper {
height: 100%;
width: 70%;
background: transparent;
}
#script-editor-filename-wrapper {
background-color: #555;
margin-left: 6px;
margin-right: 0;
padding-left: 6px;
width: 100%;
border: 2px solid var(--my-highlight-color);
}
#script-editor-filename-tag {
display: inline-block;
padding-top: 10px;
padding-bottom: 0;
float: center;
background-color: #555;
color: #fff;
}
#script-editor-filename {
$boxShadowArgs: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
@include boxShadow($boxShadowArgs);
background-color: #555;
display: inline-block;
float: center;
resize: none;
color: #fff;
margin: 4px;
padding: 2px;
border: 2px solid var(--my-highlight-color);
}
#script-editor-status {
float: left;
color: #fff;
}
#script-editor-options-panel {
position: absolute;
right: 9%;
bottom: 15%;
border: 2px solid #fff;
width: 19%;
background-color: #444;
padding: 2px;
overflow: auto;
z-index: 1;
color: #fff;
}
#script-editor-options-panel fieldset {
margin-top: 8px;
margin-bottom: 8px;
padding: 2px;
font-size: $defaultFontSize * 0.75;
input {
margin: 2px;
}
}
/* Specific overrides for Ace Editor */
.ace_line,
.ace_line * {
background-color: transparent;
margin: 0;
padding: 0;
}
.ace_text-input {
font-size: $defaultFontSize;
background-color: transparent;
}
/* Specified overrides for Code mirror Editor are defined in codemirror-override.scss */

@ -35,11 +35,6 @@ li {
list-style-type: none; list-style-type: none;
} }
span {
margin: 4px;
padding: 4px;
}
#entire-game-container { #entire-game-container {
background-color: transparent; background-color: transparent;
} }

@ -67,5 +67,6 @@
} }
#terminal-input-text-box { #terminal-input-text-box {
margin-left: 2px;
flex: 1 1 auto; flex: 1 1 auto;
} }

6877
dist/engine.bundle.js vendored

File diff suppressed because it is too large Load Diff

100
dist/engine.css vendored

@ -1,3 +1,37 @@
/* COLORS */
/* Attributes */
/**
* Customized styling for the Code Mirror editor
*/
#codemirror-form-wrapper {
height: 80%;
margin: 10px 0px 10px 6px; }
.CodeMirror {
height: 100%;
width: 100%;
border: 2px solid var(--my-highlight-color);
z-index: 1;
font-family: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", "Consolas", "Courier New", Courier, monospace, "Times New Roman";
font-size: 16px; }
/**
* Highlight matches
*/
.cm-matchhighlight {
background-color: #8F908A; }
.CodeMirror-selection-highlight-scrollbar {
background-color: #8F908A; }
/**
* Show Invisibles
*/
.cm-whitespace::before {
position: absolute;
pointer-events: none;
color: #404F7D; }
/* COLORS */ /* COLORS */
/* Attributes */ /* Attributes */
/* COLORS */ /* COLORS */
@ -40,10 +74,6 @@ ul {
li { li {
list-style-type: none; } list-style-type: none; }
span {
margin: 4px;
padding: 4px; }
#entire-game-container { #entire-game-container {
background-color: transparent; } background-color: transparent; }
@ -771,29 +801,18 @@ button {
white-space: pre; } white-space: pre; }
#terminal-input-text-box { #terminal-input-text-box {
margin-left: 2px;
flex: 1 1 auto; } flex: 1 1 auto; }
/* COLORS */ /* COLORS */
/* Attributes */ /* Attributes */
/* CSS for different main menu pages, such as character info, script editor, etc (but excluding /**
terminal which has its own page) */ * Styling for Script Editor (both Ace and CodeMirror)
.generic-menupage-container { */
height: 100%;
padding-left: 10px;
margin-left: 10%;
width: 99%;
overflow-y: scroll; }
/* Character Info */
#character-container {
padding-top: 10px;
position: fixed; }
/* Script Editor */
#script-editor-container { #script-editor-container {
background-color: transparent; } background-color: transparent; }
#javascript-editor { #ace-editor {
margin: 10px; margin: 10px;
height: 80%; height: 80%;
width: 100%; width: 100%;
@ -805,16 +824,6 @@ button {
z-index: 1; z-index: 1;
font-family: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", "Consolas", "Courier New", Courier, monospace, "Times New Roman"; } font-family: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", "Consolas", "Courier New", Courier, monospace, "Times New Roman"; }
.ace_line,
.ace_line * {
background-color: transparent;
margin: 0;
padding: 0; }
.ace_text-input {
font-size: 16px;
background-color: transparent; }
/* This temp element is used for auto adjusting filename field */ /* This temp element is used for auto adjusting filename field */
.tmp-element { .tmp-element {
visibility: hidden; visibility: hidden;
@ -884,6 +893,37 @@ button {
margin-bottom: 8px; margin-bottom: 8px;
padding: 2px; padding: 2px;
font-size: 12px; } font-size: 12px; }
#script-editor-options-panel fieldset input {
margin: 2px; }
/* Specific overrides for Ace Editor */
.ace_line,
.ace_line * {
background-color: transparent;
margin: 0;
padding: 0; }
.ace_text-input {
font-size: 16px;
background-color: transparent; }
/* Specified overrides for Code mirror Editor are defined in codemirror-override.scss */
/* COLORS */
/* Attributes */
/* CSS for different main menu pages, such as character info, script editor, etc (but excluding
terminal which has its own page) */
.generic-menupage-container {
height: 100%;
padding-left: 10px;
margin-left: 10%;
width: 99%;
overflow-y: scroll; }
/* Character Info */
#character-container {
padding-top: 10px;
position: fixed; }
/* Active scripts */ /* Active scripts */
.active-scripts-list { .active-scripts-list {

63373
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

5498
dist/vendor.css vendored

File diff suppressed because one or more lines are too long

@ -118,7 +118,8 @@
<input id="script-editor-filename" type="text" maxlength="30" tabindex="1" /> <input id="script-editor-filename" type="text" maxlength="30" tabindex="1" />
</div> </div>
<div id="javascript-editor"></div> <div id="ace-editor"></div>
<form id="codemirror-form-wrapper"><textarea id="codemirror-editor"></textarea></form>
<div id="script-editor-buttons-wrapper"></div> <!-- Buttons below script editor --> <div id="script-editor-buttons-wrapper"></div> <!-- Buttons below script editor -->
</div> <!-- End wrapper --> </div> <!-- End wrapper -->
@ -126,26 +127,21 @@
<div id="script-editor-options-panel"> <div id="script-editor-options-panel">
<h1 style="color:white;"> Script Editor Options </h1> <h1 style="color:white;"> Script Editor Options </h1>
<fieldset> <fieldset>
<label for="script-editor-option-theme">Theme</label> <label for="script-editor-option-editor">Editor</label>
<select id="script-editor-option-theme"> <select id="script-editor-option-editor">
<option value="Chaos">Chaos</option> <option value="Ace">Ace</option>
<option value="Chrome">Chrome</option> <option value="CodeMirror">CodeMirror</option>
<option value="Monokai">Monokai</option>
<option value="Solarized_Dark">Solarized Dark</option>
<option value="Solarized_Light">Solarized Light</option>
<option value="Terminal">Terminal</option>
<option value="Twilight">Twilight</option>
<option value="XCode">XCode</option>
</select> </select>
</fieldset> </fieldset>
<fieldset>
<label for="script-editor-option-theme">Theme</label>
<select id="script-editor-option-theme"></select>
</fieldset>
<fieldset> <fieldset>
<label for="script-editor-option-keybinding">Key Binding</label> <label for="script-editor-option-keybinding">Key Binding</label>
<select id="script-editor-option-keybinding"> <select id="script-editor-option-keybinding"></select>
<option value="ace">Ace</option>
<option value="vim">Vim</option>
<option value="emacs">Emacs</option>
</select>
</fieldset> </fieldset>
<fieldset> <fieldset>
@ -163,11 +159,11 @@
<input type="checkbox" name="script-editor-option-usesofttab" id="script-editor-option-usesofttab" checked> <input type="checkbox" name="script-editor-option-usesofttab" id="script-editor-option-usesofttab" checked>
</fieldset> </fieldset>
<fieldset> <fieldset id="script-editor-option-flex1-fieldset"></fieldset>
<label for="script-editor-option-maxerr" class="tooltip">Max Error Count</label> <fieldset id="script-editor-option-flex2-fieldset"></fieldset>
<input type="range" max="1000" min="50" value="200" step="1" name="script-editor-option-maxerr" id="script-editor-option-maxerr" /> <fieldset id="script-editor-option-flex3-fieldset"></fieldset>
<em id="script-editor-option-maxerror-value-label" style="font-style: normal;"></em> <fieldset id="script-editor-option-flex4-fieldset"></fieldset>
</fieldset>
</div> <!-- End script editor options panel --> </div> <!-- End script editor options panel -->
</div> </div>

130
package-lock.json generated

@ -1714,6 +1714,30 @@
} }
} }
}, },
"cli": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz",
"integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=",
"requires": {
"exit": "0.1.2",
"glob": "7.1.3"
},
"dependencies": {
"glob": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"requires": {
"fs.realpath": "1.0.0",
"inflight": "1.0.6",
"inherits": "2.0.3",
"minimatch": "3.0.4",
"once": "1.4.0",
"path-is-absolute": "1.0.1"
}
}
}
},
"cli-cursor": { "cli-cursor": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
@ -1806,6 +1830,11 @@
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true "dev": true
}, },
"codemirror": {
"version": "5.43.0",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.43.0.tgz",
"integrity": "sha512-mljwQWUaWIf85I7QwTBryF2ASaIvmYAL4s5UCanCJFfKeXOKhrqdHWdHiZWAMNT+hjLTCnVx2S/SYTORIgxsgA=="
},
"collapse-white-space": { "collapse-white-space": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.4.tgz", "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.4.tgz",
@ -1978,7 +2007,6 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
"integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
"dev": true,
"requires": { "requires": {
"date-now": "0.1.4" "date-now": "0.1.4"
} }
@ -2324,8 +2352,7 @@
"date-now": { "date-now": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
"integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs="
"dev": true
}, },
"debug": { "debug": {
"version": "3.1.0", "version": "3.1.0",
@ -2571,7 +2598,6 @@
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
"integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
"dev": true,
"requires": { "requires": {
"domelementtype": "1.1.3", "domelementtype": "1.1.3",
"entities": "1.1.1" "entities": "1.1.1"
@ -2580,8 +2606,7 @@
"domelementtype": { "domelementtype": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
"integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs="
"dev": true
} }
} }
}, },
@ -2594,8 +2619,7 @@
"domelementtype": { "domelementtype": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz",
"integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI="
"dev": true
}, },
"domhandler": { "domhandler": {
"version": "2.4.2", "version": "2.4.2",
@ -2729,8 +2753,7 @@
"entities": { "entities": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz",
"integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA="
"dev": true
}, },
"errno": { "errno": {
"version": "0.1.7", "version": "0.1.7",
@ -3185,6 +3208,11 @@
"clone-regexp": "1.0.1" "clone-regexp": "1.0.1"
} }
}, },
"exit": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
"integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw="
},
"expand-brackets": { "expand-brackets": {
"version": "2.1.4", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
@ -5789,6 +5817,83 @@
"integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
"dev": true "dev": true
}, },
"jshint": {
"version": "2.9.7",
"resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.7.tgz",
"integrity": "sha512-Q8XN38hGsVQhdlM+4gd1Xl7OB1VieSuCJf+fEJjpo59JH99bVJhXRXAh26qQ15wfdd1VPMuDWNeSWoNl53T4YA==",
"requires": {
"cli": "1.0.1",
"console-browserify": "1.1.0",
"exit": "0.1.2",
"htmlparser2": "3.8.3",
"lodash": "4.17.10",
"minimatch": "3.0.4",
"shelljs": "0.3.0",
"strip-json-comments": "1.0.4"
},
"dependencies": {
"domhandler": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz",
"integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=",
"requires": {
"domelementtype": "1.3.0"
}
},
"domutils": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
"integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
"requires": {
"dom-serializer": "0.1.0",
"domelementtype": "1.3.0"
}
},
"entities": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
"integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY="
},
"htmlparser2": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
"integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=",
"requires": {
"domelementtype": "1.3.0",
"domhandler": "2.3.0",
"domutils": "1.5.1",
"entities": "1.0.0",
"readable-stream": "1.1.14"
}
},
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"readable-stream": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "0.0.1",
"string_decoder": "0.10.31"
}
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
},
"strip-json-comments": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
"integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E="
}
}
},
"json-loader": { "json-loader": {
"version": "0.5.7", "version": "0.5.7",
"resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz",
@ -9869,6 +9974,11 @@
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
"dev": true "dev": true
}, },
"shelljs": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz",
"integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E="
},
"should": { "should": {
"version": "11.2.1", "version": "11.2.1",
"resolved": "https://registry.npmjs.org/should/-/should-11.2.1.tgz", "resolved": "https://registry.npmjs.org/should/-/should-11.2.1.tgz",

@ -15,6 +15,7 @@
"autosize": "^4.0.2", "autosize": "^4.0.2",
"bluebird": "^3.5.1", "bluebird": "^3.5.1",
"brace": "^0.11.1", "brace": "^0.11.1",
"codemirror": "^5.43.0",
"decimal.js": "7.2.3", "decimal.js": "7.2.3",
"enhanced-resolve": "^4.0.0", "enhanced-resolve": "^4.0.0",
"escodegen": "^1.11.0", "escodegen": "^1.11.0",
@ -22,6 +23,7 @@
"file-saver": "^1.3.8", "file-saver": "^1.3.8",
"interpret": "^1.0.0", "interpret": "^1.0.0",
"jquery": "^3.3.1", "jquery": "^3.3.1",
"jshint": "^2.9.7",
"json-loader": "^0.5.4", "json-loader": "^0.5.4",
"jsplumb": "^2.6.8", "jsplumb": "^2.6.8",
"jszip": "^3.1.5", "jszip": "^3.1.5",

@ -15,8 +15,8 @@ import { saveObject } from "../SaveObject";
import { Script, import { Script,
RunningScript} from "../Script"; RunningScript} from "../Script";
import { Server } from "../Server"; import { Server } from "../Server";
import { OwnedAugmentationsOrderSetting } from "../SettingEnums"; import { OwnedAugmentationsOrderSetting } from "../Settings/SettingEnums";
import { Settings } from "../Settings"; import { Settings } from "../Settings/Settings";
import { SourceFiles } from "../SourceFile"; import { SourceFiles } from "../SourceFile";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";

@ -524,6 +524,7 @@ export let CONSTANTS: IMap<any> = {
* Home Computer RAM is now capped at 2 ^ 30 GB (1073741824 GB) * Home Computer RAM is now capped at 2 ^ 30 GB (1073741824 GB)
* Pop-up dialog boxes are a little bit bigger * Pop-up dialog boxes are a little bit bigger
* Bug Fix: When importing scripts, "./" will now be properly ignored (e.g. import { foo } from "./lib.script" )
` `
} }

@ -10,8 +10,8 @@ import { FactionInfos } from "./FactionInfo";
import { Locations} from "../Location"; import { Locations} from "../Location";
import { HackingMission, setInMission } from "../Missions"; import { HackingMission, setInMission } from "../Missions";
import { Player } from "../Player"; import { Player } from "../Player";
import { PurchaseAugmentationsOrderSetting } from "../SettingEnums"; import { PurchaseAugmentationsOrderSetting } from "../Settings/SettingEnums";
import { Settings } from "../Settings"; import { Settings } from "../Settings/Settings";
import {Page, routing} from "../ui/navigationTracking"; import {Page, routing} from "../ui/navigationTracking";
import {numeralWrapper} from "../ui/numeralFormat"; import {numeralWrapper} from "../ui/numeralFormat";

@ -1,6 +1,6 @@
import {Engine} from "./engine"; import {Engine} from "./engine";
import {Player} from "./Player"; import {Player} from "./Player";
import {Settings} from "./Settings"; import {Settings} from "./Settings/Settings";
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners"; import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
import {createElement} from "../utils/uiHelpers/createElement"; import {createElement} from "../utils/uiHelpers/createElement";
import {createPopup} from "../utils/uiHelpers/createPopup"; import {createPopup} from "../utils/uiHelpers/createPopup";

@ -14,7 +14,7 @@ import {Player} from "./Player";
import {Server, AllServers, AddToAllServers} from "./Server"; import {Server, AllServers, AddToAllServers} from "./Server";
import {purchaseServer, import {purchaseServer,
purchaseRamForHomeComputer} from "./ServerPurchases"; purchaseRamForHomeComputer} from "./ServerPurchases";
import {Settings} from "./Settings"; import {Settings} from "./Settings/Settings";
import { SourceFileFlags } from "./SourceFile/SourceFileFlags"; import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
import {SpecialServerNames, SpecialServerIps} from "./SpecialServerIps"; import {SpecialServerNames, SpecialServerIps} from "./SpecialServerIps";

@ -6,7 +6,7 @@ import { inMission } from "./Missions";
import { Player } from "./Player"; import { Player } from "./Player";
import { redPillFlag } from "./RedPill"; import { redPillFlag } from "./RedPill";
import { GetServerByHostname } from "./Server"; import { GetServerByHostname } from "./Server";
import { Settings } from "./Settings"; import { Settings } from "./Settings/Settings";
import { dialogBoxCreate, import { dialogBoxCreate,
dialogBoxOpened} from "../utils/DialogBox"; dialogBoxOpened} from "../utils/DialogBox";
import {Reviver, Generic_toJSON, import {Reviver, Generic_toJSON,

@ -4,7 +4,7 @@ import { Player } from "./Player";
import { Environment } from "./NetscriptEnvironment"; import { Environment } from "./NetscriptEnvironment";
import { WorkerScript, addWorkerScript} from "./NetscriptWorker"; import { WorkerScript, addWorkerScript} from "./NetscriptWorker";
import { Server, getServer} from "./Server"; import { Server, getServer} from "./Server";
import { Settings } from "./Settings"; import { Settings } from "./Settings/Settings";
import { Script, findRunningScript, import { Script, findRunningScript,
RunningScript } from "./Script"; RunningScript } from "./Script";

@ -40,7 +40,7 @@ import {Script, findRunningScript, RunningScript,
import {Server, getServer, AddToAllServers, import {Server, getServer, AddToAllServers,
AllServers, processSingleServerGrowth, AllServers, processSingleServerGrowth,
GetServerByHostname, numCycleForGrowth} from "./Server"; GetServerByHostname, numCycleForGrowth} from "./Server";
import {Settings} from "./Settings"; import {Settings} from "./Settings/Settings";
import {SpecialServerIps} from "./SpecialServerIps"; import {SpecialServerIps} from "./SpecialServerIps";
import {Stock} from "./StockMarket/Stock"; import {Stock} from "./StockMarket/Stock";
import {StockMarket, StockSymbols, SymbolToStockMap, import {StockMarket, StockSymbols, SymbolToStockMap,

@ -1,4 +1,4 @@
import {Settings} from "./Settings"; import {Settings} from "./Settings/Settings";
function NetscriptPort() { function NetscriptPort() {
this.data = []; this.data = [];

@ -12,7 +12,7 @@ import {NetscriptFunctions} from "./NetscriptFunctions";
import {executeJSScript} from "./NetscriptJSEvaluator"; import {executeJSScript} from "./NetscriptJSEvaluator";
import {NetscriptPort} from "./NetscriptPort"; import {NetscriptPort} from "./NetscriptPort";
import {AllServers} from "./Server"; import {AllServers} from "./Server";
import {Settings} from "./Settings"; import {Settings} from "./Settings/Settings";
import {generate} from 'escodegen'; import {generate} from 'escodegen';

@ -25,7 +25,7 @@ import {Locations} from "./Locations";
import {hasBn11SF, hasWallStreetSF,hasAISF} from "./NetscriptFunctions"; import {hasBn11SF, hasWallStreetSF,hasAISF} from "./NetscriptFunctions";
import { Sleeve } from "./PersonObjects/Sleeve/Sleeve"; import { Sleeve } from "./PersonObjects/Sleeve/Sleeve";
import {AllServers, Server, AddToAllServers} from "./Server"; import {AllServers, Server, AddToAllServers} from "./Server";
import {Settings} from "./Settings"; import {Settings} from "./Settings/Settings";
import {SpecialServerIps, SpecialServerNames} from "./SpecialServerIps"; import {SpecialServerIps, SpecialServerNames} from "./SpecialServerIps";
import {SourceFiles, applySourceFile} from "./SourceFile"; import {SourceFiles, applySourceFile} from "./SourceFile";
import { SourceFileFlags } from "./SourceFile/SourceFileFlags"; import { SourceFileFlags } from "./SourceFile/SourceFileFlags";

@ -14,7 +14,7 @@ import {loadMessages, initMessages, Messages} from "./Message";
import {Player, loadPlayer} from "./Player"; import {Player, loadPlayer} from "./Player";
import {loadAllRunningScripts} from "./Script"; import {loadAllRunningScripts} from "./Script";
import {AllServers, loadAllServers} from "./Server"; import {AllServers, loadAllServers} from "./Server";
import {Settings} from "./Settings"; import {Settings} from "./Settings/Settings";
import {loadSpecialServerIps, SpecialServerIps} from "./SpecialServerIps"; import {loadSpecialServerIps, SpecialServerIps} from "./SpecialServerIps";
import {loadStockMarket, StockMarket} from "./StockMarket/StockMarket"; import {loadStockMarket, StockMarket} from "./StockMarket/StockMarket";
import {dialogBoxCreate} from "../utils/DialogBox"; import {dialogBoxCreate} from "../utils/DialogBox";

@ -1,19 +1,3 @@
var ace = require('brace');
var beautify = require('js-beautify').js_beautify;
require('brace/mode/javascript');
require('../netscript');
require('brace/theme/chaos');
require('brace/theme/chrome');
require('brace/theme/monokai');
require('brace/theme/solarized_dark');
require('brace/theme/solarized_light');
require('brace/theme/terminal');
require('brace/theme/twilight');
require('brace/theme/xcode');
require("brace/keybinding/vim");
require("brace/keybinding/emacs");
require("brace/ext/language_tools");
// Importing this doesn't work for some reason. // Importing this doesn't work for some reason.
const walk = require("acorn/dist/walk"); const walk = require("acorn/dist/walk");
@ -26,8 +10,11 @@ import {evaluateImport} from "./NetscriptEvaluator";
import {NetscriptFunctions} from "./NetscriptFunctions"; import {NetscriptFunctions} from "./NetscriptFunctions";
import {addWorkerScript, WorkerScript} from "./NetscriptWorker"; import {addWorkerScript, WorkerScript} from "./NetscriptWorker";
import {Player} from "./Player"; import {Player} from "./Player";
import { AceEditor } from "./ScriptEditor/Ace";
import { CodeMirrorEditor } from "./ScriptEditor/CodeMirror";
import {AllServers, processSingleServerGrowth} from "./Server"; import {AllServers, processSingleServerGrowth} from "./Server";
import {Settings} from "./Settings"; import { Settings } from "./Settings/Settings";
import { EditorSetting } from "./Settings/SettingEnums";
import {post} from "./ui/postToTerminal"; import {post} from "./ui/postToTerminal";
import {TextFile} from "./TextFile"; import {TextFile} from "./TextFile";
import {parse, Node} from "../utils/acorn"; import {parse, Node} from "../utils/acorn";
@ -41,47 +28,42 @@ import {createElement} from "../utils/uiHelpers/createE
import {getTimestamp} from "../utils/helpers/getTimestamp"; import {getTimestamp} from "../utils/helpers/getTimestamp";
import {roundToTwo} from "../utils/helpers/roundToTwo"; import {roundToTwo} from "../utils/helpers/roundToTwo";
var keybindings = {
ace: null,
vim: "ace/keyboard/vim",
emacs: "ace/keyboard/emacs",
};
function isScriptFilename(f) { function isScriptFilename(f) {
return f.endsWith(".js") || f.endsWith(".script") || f.endsWith(".ns"); return f.endsWith(".js") || f.endsWith(".script") || f.endsWith(".ns");
} }
var scriptEditorRamCheck = null, scriptEditorRamText = null; var scriptEditorRamCheck = null, scriptEditorRamText = null;
function scriptEditorInit() { function scriptEditorInit() {
//Create buttons at the bottom of script editor // Wrapper container that holds all the buttons below the script editor
var wrapper = document.getElementById("script-editor-buttons-wrapper"); const wrapper = document.getElementById("script-editor-buttons-wrapper");
if (wrapper == null) { if (wrapper == null) {
console.log("Error finding 'script-editor-buttons-wrapper'"); console.error("Could not find 'script-editor-buttons-wrapper'");
return; return false;
} }
var beautifyButton = createElement("a", {
class:"a-link-button", display:"inline-block",
innerText:"Beautify",
// Beautify button
const beautifyButton = createElement("button", {
class: "std-button",
display: "inline-block",
innerText: "Beautify",
clickListener:()=>{ clickListener:()=>{
beautifyScript(); let editor = getCurrentEditor();
return false; if (editor != null) {
} editor.beautifyScript();
}); }
var closeButton = createElement("a", {
class:"a-link-button", display:"inline-block",
innerText:"Save & Close (Ctrl/Cmd + b)",
clickListener:()=>{
saveAndCloseScriptEditor();
return false; return false;
} }
}); });
// Text that displays RAM calculation
scriptEditorRamText = createElement("p", { scriptEditorRamText = createElement("p", {
display:"inline-block", margin:"10px", id:"script-editor-status-text" display:"inline-block", margin:"10px", id:"script-editor-status-text"
}); });
var checkboxLabel = createElement("label", { // Label for checkbox (defined below)
const checkboxLabel = createElement("label", {
for:"script-editor-ram-check", margin:"4px", marginTop: "8px", for:"script-editor-ram-check", margin:"4px", marginTop: "8px",
innerText:"Dynamic RAM Usage Checker", color:"white", innerText:"Dynamic RAM Usage Checker", color:"white",
tooltip:"Enable/Disable the dynamic RAM Usage display. You may " + tooltip:"Enable/Disable the dynamic RAM Usage display. You may " +
@ -89,18 +71,34 @@ function scriptEditorInit() {
"performance issues" "performance issues"
}); });
// Checkbox for enabling/disabling dynamic RAM calculation
scriptEditorRamCheck = createElement("input", { scriptEditorRamCheck = createElement("input", {
type:"checkbox", name:"script-editor-ram-check", id:"script-editor-ram-check", type:"checkbox", name:"script-editor-ram-check", id:"script-editor-ram-check",
margin:"4px", marginTop: "8px", margin:"4px", marginTop: "8px",
}); });
scriptEditorRamCheck.checked = true; scriptEditorRamCheck.checked = true;
var documentationButton = createElement("a", { // Link to Netscript documentation
display:"inline-block", class:"a-link-button", innerText:"Netscript Documentation", const documentationButton = createElement("a", {
class: "std-button",
display: "inline-block",
href:"https://bitburner.readthedocs.io/en/latest/index.html", href:"https://bitburner.readthedocs.io/en/latest/index.html",
target:"_blank" innerText:"Netscript Documentation",
target:"_blank",
}); });
// Save and Close button
const closeButton = createElement("button", {
class: "std-button",
display: "inline-block",
innerText: "Save & Close (Ctrl/Cmd + b)",
clickListener:()=>{
saveAndCloseScriptEditor();
return false;
}
});
// Add all buttons to the UI
wrapper.appendChild(beautifyButton); wrapper.appendChild(beautifyButton);
wrapper.appendChild(closeButton); wrapper.appendChild(closeButton);
wrapper.appendChild(scriptEditorRamText); wrapper.appendChild(scriptEditorRamText);
@ -108,146 +106,86 @@ function scriptEditorInit() {
wrapper.appendChild(checkboxLabel); wrapper.appendChild(checkboxLabel);
wrapper.appendChild(documentationButton); wrapper.appendChild(documentationButton);
//Initialize ACE Script editor // Initialize editors
var editor = ace.edit('javascript-editor'); const initParams = {
editor.getSession().setMode('ace/mode/netscript'); saveAndCloseFn: saveAndCloseScriptEditor,
editor.setTheme('ace/theme/monokai'); quitFn: Engine.loadTerminalContent,
document.getElementById('javascript-editor').style.fontSize='16px'; }
editor.setOption("showPrintMargin", false);
/* Script editor options */ AceEditor.init(initParams);
//Theme CodeMirrorEditor.init(initParams);
var themeDropdown = document.getElementById("script-editor-option-theme");
if (Settings.EditorTheme) { // Setup the selector for which Editor to use
var initialIndex = 2; const editorSelector = document.getElementById("script-editor-option-editor");
for (var i = 0; i < themeDropdown.options.length; ++i) { if (editorSelector == null) {
if (themeDropdown.options[i].value === Settings.EditorTheme) { console.error(`Could not find DOM Element for editor selector (id=script-editor-option-editor)`);
initialIndex = i; return false;
}
for (let i = 0; i < editorSelector.options.length; ++i) {
if (editorSelector.options[i].value === Settings.Editor) {
editorSelector.selectedIndex = i;
break; break;
} }
} }
themeDropdown.selectedIndex = initialIndex;
} else {
themeDropdown.selectedIndex = 2;
}
themeDropdown.onchange = function() { editorSelector.onchange = () => {
var val = themeDropdown.value; const opt = editorSelector.value;
Settings.EditorTheme = val; switch (opt) {
var themePath = "ace/theme/" + val.toLowerCase(); case EditorSetting.Ace:
editor.setTheme(themePath); const codeMirrorCode = CodeMirrorEditor.getCode();
}; const codeMirrorFn = CodeMirrorEditor.getFilename();
themeDropdown.onchange(); AceEditor.create();
CodeMirrorEditor.setInvisible();
//Keybinding AceEditor.openScript(codeMirrorFn, codeMirrorCode);
var keybindingDropdown = document.getElementById("script-editor-option-keybinding");
if (Settings.EditorKeybinding) {
var initialIndex = 0;
for (var i = 0; i < keybindingDropdown.options.length; ++i) {
if (keybindingDropdown.options[i].value === Settings.EditorKeybinding) {
initialIndex = i;
break; break;
} case EditorSetting.CodeMirror:
} const aceCode = AceEditor.getCode();
keybindingDropdown.selectedIndex = initialIndex; const aceFn = AceEditor.getFilename();
} else { CodeMirrorEditor.create();
keybindingDropdown.selectedIndex = 0; AceEditor.setInvisible();
} CodeMirrorEditor.openScript(aceFn, aceCode);
keybindingDropdown.onchange = function() { break;
var val = keybindingDropdown.value; default:
Settings.EditorKeybinding = val; console.error(`Unrecognized Editor Setting: ${opt}`);
editor.setKeyboardHandler(keybindings[val.toLowerCase()]); return;
};
keybindingDropdown.onchange();
//Highlight Active line
var highlightActiveChkBox = document.getElementById("script-editor-option-highlightactiveline");
highlightActiveChkBox.onchange = function() {
editor.setHighlightActiveLine(highlightActiveChkBox.checked);
};
//Show Invisibles
var showInvisiblesChkBox = document.getElementById("script-editor-option-showinvisibles");
showInvisiblesChkBox.onchange = function() {
editor.setShowInvisibles(showInvisiblesChkBox.checked);
};
//Use Soft Tab
var softTabChkBox = document.getElementById("script-editor-option-usesofttab");
softTabChkBox.onchange = function() {
editor.getSession().setUseSoftTabs(softTabChkBox.checked);
};
//Jshint Maxerr
var maxerr = document.getElementById("script-editor-option-maxerr");
var maxerrLabel = document.getElementById("script-editor-option-maxerror-value-label");
maxerrLabel.innerHTML = maxerr.value;
maxerr.onchange = function() {
editor.getSession().$worker.send("changeOptions", [{maxerr:maxerr.value}]);
maxerrLabel.innerHTML = maxerr.value;
} }
//Configure some of the VIM keybindings Settings.Editor = opt;
ace.config.loadModule('ace/keyboard/vim', function(module) { }
var VimApi = module.CodeMirror.Vim;
VimApi.defineEx('write', 'w', function(cm, input) {
saveAndCloseScriptEditor();
});
VimApi.defineEx('quit', 'q', function(cm, input) {
Engine.loadTerminalContent();
});
VimApi.defineEx('xwritequit', 'x', function(cm, input) {
saveAndCloseScriptEditor();
});
VimApi.defineEx('wqwritequit', 'wq', function(cm, input) {
saveAndCloseScriptEditor();
});
});
//Function autocompleter editorSelector.onchange(); // Trigger the onchange event handler
editor.setOption("enableBasicAutocompletion", true); }
var autocompleter = {
getCompletions: function(editor, session, pos, prefix, callback) {
if (prefix.length === 0) {callback(null, []); return;}
var words = [];
var fns = NetscriptFunctions(null);
for (let name in fns) {
if (fns.hasOwnProperty(name)) {
words.push({
name: name,
value: name,
});
//Get functions from namespaces export function getCurrentEditor() {
const namespaces = ["bladeburner", "hacknet", "codingcontract", "gang"]; switch (Settings.Editor) {
if (namespaces.includes(name)) { case EditorSetting.Ace:
let namespace = fns[name]; return AceEditor;
if (typeof namespace !== "object") {continue;} case EditorSetting.CodeMirror:
let namespaceFns = Object.keys(namespace); return CodeMirrorEditor;
for (let i = 0; i < namespaceFns.length; ++i) { default:
words.push({ console.log(`Invalid Editor Setting: ${Settings.Editor}`);
name: namespaceFns[i], throw new Error(`Invalid Editor Setting: ${Settings.Editor}`);
value: namespaceFns[i], return null;
});
} }
}
}
}
callback(null, words);
},
}
editor.completers = [autocompleter];
} }
//Updates RAM usage in script //Updates RAM usage in script
async function updateScriptEditorContent() { export async function updateScriptEditorContent() {
var filename = document.getElementById("script-editor-filename").value; var filename = document.getElementById("script-editor-filename").value;
if (scriptEditorRamCheck == null || !scriptEditorRamCheck.checked || !isScriptFilename(filename)) { if (scriptEditorRamCheck == null || !scriptEditorRamCheck.checked || !isScriptFilename(filename)) {
scriptEditorRamText.innerText = "N/A"; scriptEditorRamText.innerText = "N/A";
return; return;
} }
var editor = ace.edit('javascript-editor');
var code = editor.getValue(); let code;
try {
code = getCurrentEditor().getCode();
} catch(e) {
scriptEditorRamText.innerText = "RAM: ERROR";
return;
}
var codeCopy = code.repeat(1); var codeCopy = code.repeat(1);
var ramUsage = await calculateRamUsage(codeCopy); var ramUsage = await calculateRamUsage(codeCopy);
if (ramUsage !== -1) { if (ramUsage !== -1) {
@ -269,20 +207,16 @@ $(document).keydown(function(e) {
} }
}); });
function beautifyScript() {
var editor = ace.edit('javascript-editor');
var code = editor.getValue();
code = beautify(code, {
indent_size: 4,
brace_style: "preserve-inline",
});
editor.setValue(code);
}
function saveAndCloseScriptEditor() { function saveAndCloseScriptEditor() {
var filename = document.getElementById("script-editor-filename").value; var filename = document.getElementById("script-editor-filename").value;
var editor = ace.edit('javascript-editor');
var code = editor.getValue(); try {
let code = getCurrentEditor().getCode();
} catch(e) {
dialogBoxCreate("Something went wrong when trying to save (getCurrentEditor().getCode()). Please report to game developer with details");
return;
}
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) { if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
//Make sure filename + code properly follow tutorial //Make sure filename + code properly follow tutorial
if (filename !== "foodnstuff.script") { if (filename !== "foodnstuff.script") {
@ -387,8 +321,7 @@ function Script(fn = "", code = "", server = "") {
Script.prototype.saveScript = function() { Script.prototype.saveScript = function() {
if (routing.isOn(Page.ScriptEditor)) { if (routing.isOn(Page.ScriptEditor)) {
//Update code and filename //Update code and filename
var editor = ace.edit('javascript-editor'); const code = getCurrentEditor().getCode();
var code = editor.getValue();
this.code = code.replace(/^\s+|\s+$/g, ''); this.code = code.replace(/^\s+|\s+$/g, '');
var filename = document.getElementById("script-editor-filename").value; var filename = document.getElementById("script-editor-filename").value;
@ -1099,5 +1032,5 @@ AllServersMap.fromJSON = function(value) {
Reviver.constructors.AllServersMap = AllServersMap; Reviver.constructors.AllServersMap = AllServersMap;
export {updateScriptEditorContent, loadAllRunningScripts, findRunningScript, export {loadAllRunningScripts, findRunningScript,
RunningScript, Script, AllServersMap, scriptEditorInit, isScriptFilename}; RunningScript, Script, AllServersMap, scriptEditorInit, isScriptFilename};

301
src/ScriptEditor/Ace.js Normal file

@ -0,0 +1,301 @@
import { ScriptEditor } from "./ScriptEditor";
const ace = require('brace');
require('brace/mode/javascript');
require('./AceNetscriptMode');
require('brace/theme/chaos');
require('brace/theme/chrome');
require('brace/theme/monokai');
require('brace/theme/solarized_dark');
require('brace/theme/solarized_light');
require('brace/theme/terminal');
require('brace/theme/twilight');
require('brace/theme/xcode');
require("brace/keybinding/vim");
require("brace/keybinding/emacs");
require("brace/ext/language_tools");
import { NetscriptFunctions } from "../NetscriptFunctions";
import { Settings } from "../Settings/Settings";
import { clearEventListeners } from "../../utils/uiHelpers/clearEventListeners";
import { createElement } from "../../utils/uiHelpers/createElement";
import { createOptionElement } from "../../utils/uiHelpers/createOptionElement";
import { getSelectText,
getSelectValue } from "../../utils/uiHelpers/getSelectData";
import { removeChildrenFromElement } from "../../utils/uiHelpers/removeChildrenFromElement";
// Wrapper for Ace editor
const Keybindings = {
ace: null,
vim: "ace/keyboard/vim",
emacs: "ace/keyboard/emacs",
};
function validateInitializationParamters(params) {
if (params.saveAndCloseFn == null) { return false; } // Save & close button function
if (params.quitFn == null) { return false; } // Quitting editor, aka Engine.loadTerminalContent
return true;
}
class AceEditorWrapper extends ScriptEditor {
constructor() {
super();
}
init(params) {
if (this.editor != null) {
console.error(`AceEditor.init() called when it's already initialized`);
return false;
}
// Validate/Sanitize input
if (!validateInitializationParamters(params)) {
console.error(`'params' argument passed into initAceEditor() does not have proper properties`);
return false;
}
// Store the filename input
this.filenameInput = document.getElementById("script-editor-filename");
if (this.filenameInput == null) {
console.error(`Could not get Script Editor filename element (id=script-editor-filename)`);
return false;
}
// Initialize ACE Script editor
this.editor = ace.edit('ace-editor');
this.editor.getSession().setMode('ace/mode/netscript');
this.editor.setTheme('ace/theme/monokai');
const editorElement = document.getElementById('ace-editor');
if (editorElement == null) { return false; }
editorElement.style.fontSize = '16px';
this.editor.setOption("showPrintMargin", false);
//Configure some of the VIM keybindings
ace.config.loadModule('ace/keyboard/vim', function(module) {
var VimApi = module.CodeMirror.Vim;
VimApi.defineEx('write', 'w', function(cm, input) {
params.saveAndCloseFn();
});
VimApi.defineEx('quit', 'q', function(cm, input) {
params.quitFn();
});
VimApi.defineEx('xwritequit', 'x', function(cm, input) {
params.saveAndCloseFn();
});
VimApi.defineEx('wqwritequit', 'wq', function(cm, input) {
params.saveAndCloseFn();
});
});
//Function autocompleter
this.editor.setOption("enableBasicAutocompletion", true);
var autocompleter = {
getCompletions: function(editor, session, pos, prefix, callback) {
if (prefix.length === 0) {callback(null, []); return;}
var words = [];
var fns = NetscriptFunctions(null);
for (let name in fns) {
if (fns.hasOwnProperty(name)) {
words.push({
name: name,
value: name,
});
//Get functions from namespaces
const namespaces = ["bladeburner", "hacknet", "codingcontract", "gang"];
if (namespaces.includes(name)) {
let namespace = fns[name];
if (typeof namespace !== "object") {continue;}
let namespaceFns = Object.keys(namespace);
for (let i = 0; i < namespaceFns.length; ++i) {
words.push({
name: namespaceFns[i],
value: namespaceFns[i],
});
}
}
}
}
callback(null, words);
},
}
this.editor.completers = [autocompleter];
return true;
}
initialized() {
return (this.editor != null);
}
// Create the configurable Options for this Editor
create() {
function safeGetElementById(id, whatFor="") {
const elem = document.getElementById(id);
if (elem == null) {
throw new Error(`Could not find ${whatFor} DOM element(id=${id})`);
}
return elem;
}
function safeClearEventListeners(id, whatFor="") {
const elem = clearEventListeners(id);
if (elem == null) {
throw new Error(`Could not find ${whatFor} DOM element(id=${id})`);
}
return elem;
}
try {
const optionsPanel = safeGetElementById("script-editor-options-panel", "Script Editor Options Panel");
// Set editor to visible
const elem = document.getElementById("ace-editor");
if (elem instanceof HTMLElement) {
elem.style.display = "block";
}
// Theme
const themeDropdown = safeClearEventListeners("script-editor-option-theme", "Theme Selector");
removeChildrenFromElement(themeDropdown);
themeDropdown.add(createOptionElement("Chaos"));
themeDropdown.add(createOptionElement("Chrome"));
themeDropdown.add(createOptionElement("Monokai"));
themeDropdown.add(createOptionElement("Solarized Dark", "Solarized_Dark"));
themeDropdown.add(createOptionElement("Solarized Light", "Solarized_Light"));
themeDropdown.add(createOptionElement("Terminal"));
themeDropdown.add(createOptionElement("Twilight"));
themeDropdown.add(createOptionElement("XCode"));
if (Settings.EditorTheme) {
var initialIndex = 2;
for (var i = 0; i < themeDropdown.options.length; ++i) {
if (themeDropdown.options[i].value === Settings.EditorTheme) {
initialIndex = i;
break;
}
}
themeDropdown.selectedIndex = initialIndex;
} else {
themeDropdown.selectedIndex = 2;
}
themeDropdown.onchange = () => {
const val = themeDropdown.value;
Settings.EditorTheme = val;
const themePath = "ace/theme/" + val.toLowerCase();
this.editor.setTheme(themePath);
};
themeDropdown.onchange();
// Keybinding
const keybindingDropdown = safeClearEventListeners("script-editor-option-keybinding", "Keybinding Selector");
removeChildrenFromElement(keybindingDropdown);
keybindingDropdown.add(createOptionElement("Ace", "ace"));
keybindingDropdown.add(createOptionElement("Vim", "vim"));
keybindingDropdown.add(createOptionElement("Emacs", "emacs"));
if (Settings.EditorKeybinding) {
var initialIndex = 0;
for (var i = 0; i < keybindingDropdown.options.length; ++i) {
if (keybindingDropdown.options[i].value === Settings.EditorKeybinding) {
initialIndex = i;
break;
}
}
keybindingDropdown.selectedIndex = initialIndex;
} else {
keybindingDropdown.selectedIndex = 0;
}
keybindingDropdown.onchange = () => {
var val = keybindingDropdown.value;
Settings.EditorKeybinding = val;
this.editor.setKeyboardHandler(Keybindings[val.toLowerCase()]);
};
keybindingDropdown.onchange();
// Highlight Active line
const highlightActiveChkBox = safeClearEventListeners("script-editor-option-highlightactiveline", "Active Line Checkbox");
highlightActiveChkBox.onchange = () => {
this.editor.setHighlightActiveLine(highlightActiveChkBox.checked);
};
// Show Invisibles
const showInvisiblesChkBox = safeClearEventListeners("script-editor-option-showinvisibles", "Show Invisible Checkbox");
showInvisiblesChkBox.onchange = () => {
this.editor.setShowInvisibles(showInvisiblesChkBox.checked);
};
// Use Soft Tab
const softTabChkBox = safeClearEventListeners("script-editor-option-usesofttab", "Soft Tab Checkbox");
softTabChkBox.onchange = () => {
this.editor.getSession().setUseSoftTabs(softTabChkBox.checked);
};
// Some helper functions for dealing with flexible options
function resetFlexibleOption(id) {
const fieldset = safeGetElementById(id);
removeChildrenFromElement(fieldset);
fieldset.style.display = "block";
return fieldset;
}
function removeFlexibleOption(id) {
// This doesn't really remove it, just sets it to invisible
const fieldset = resetFlexibleOption(id);
fieldset.style.display = "none";
return fieldset;
}
// Jshint Maxerr (Flex 1)
const flex1Fieldset = resetFlexibleOption("script-editor-option-flex1-fieldset");
const flex1Id = "script-editor-option-maxerr";
const flex1ValueLabel = createElement("em", { innerText: "200" });
flex1Fieldset.appendChild(createElement("label", {
for: flex1Id,
innerText: "Max Error Count",
}));
const flex1Input = createElement("input", {
id: flex1Id,
max: "1000",
min: "50",
name: flex1Id,
step: "1",
type: "range",
value: "200",
changeListener: () => {
this.editor.getSession().$worker.send("changeOptions", [{maxerr:flex1Input.value}]);
flex1ValueLabel.innerText = flex1Input.value;
}
});
flex1Fieldset.appendChild(flex1Input);
flex1Fieldset.appendChild(flex1ValueLabel);
// Nothing for Flex Options 2-4
removeFlexibleOption("script-editor-option-flex2-fieldset");
removeFlexibleOption("script-editor-option-flex3-fieldset");
removeFlexibleOption("script-editor-option-flex4-fieldset");
} catch(e) {
console.error(`Exception caught: ${e}`);
return false;
}
}
isFocused() {
if (this.editor == null) { return false; }
return this.editor.isFocused();
}
// Sets the editor to be invisible. Does not require this class to be initialized
setInvisible() {
const elem = document.getElementById("ace-editor");
if (elem instanceof HTMLElement) {
elem.style.display = "none";
}
}
}
export const AceEditor = new AceEditorWrapper();

@ -0,0 +1,523 @@
// Wrapper for CodeMirror editor
// https://github.com/codemirror/codemirror
import { ScriptEditor } from "./ScriptEditor";
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/monokai.css';
import 'codemirror/theme/3024-day.css';
import 'codemirror/theme/3024-night.css';
import 'codemirror/theme/abcdef.css';
import 'codemirror/theme/ambiance-mobile.css';
import 'codemirror/theme/ambiance.css';
import 'codemirror/theme/base16-dark.css';
import 'codemirror/theme/base16-light.css';
import 'codemirror/theme/bespin.css';
import 'codemirror/theme/blackboard.css';
import 'codemirror/theme/cobalt.css';
import 'codemirror/theme/colorforth.css';
import 'codemirror/theme/darcula.css';
import 'codemirror/theme/dracula.css';
import 'codemirror/theme/duotone-dark.css';
import 'codemirror/theme/duotone-light.css';
import 'codemirror/theme/eclipse.css';
import 'codemirror/theme/elegant.css';
import 'codemirror/theme/erlang-dark.css';
import 'codemirror/theme/gruvbox-dark.css';
import 'codemirror/theme/hopscotch.css';
import 'codemirror/theme/icecoder.css';
import 'codemirror/theme/idea.css';
import 'codemirror/theme/isotope.css';
import 'codemirror/theme/lesser-dark.css';
import 'codemirror/theme/liquibyte.css';
import 'codemirror/theme/lucario.css';
import 'codemirror/theme/material.css';
import 'codemirror/theme/mbo.css';
import 'codemirror/theme/mdn-like.css';
import 'codemirror/theme/midnight.css';
import 'codemirror/theme/neat.css';
import 'codemirror/theme/neo.css';
import 'codemirror/theme/night.css';
import 'codemirror/theme/oceanic-next.css';
import 'codemirror/theme/panda-syntax.css';
import 'codemirror/theme/paraiso-dark.css';
import 'codemirror/theme/paraiso-light.css';
import 'codemirror/theme/pastel-on-dark.css';
import 'codemirror/theme/railscasts.css';
import 'codemirror/theme/rubyblue.css';
import 'codemirror/theme/seti.css';
import 'codemirror/theme/shadowfox.css';
import 'codemirror/theme/solarized.css';
import 'codemirror/theme/ssms.css';
import 'codemirror/theme/the-matrix.css';
import 'codemirror/theme/tomorrow-night-bright.css';
import 'codemirror/theme/tomorrow-night-eighties.css';
import 'codemirror/theme/ttcn.css';
import 'codemirror/theme/twilight.css';
import 'codemirror/theme/vibrant-ink.css';
import 'codemirror/theme/xq-dark.css';
import 'codemirror/theme/xq-light.css';
import 'codemirror/theme/yeti.css';
import 'codemirror/theme/zenburn.css';
import "../../css/codemirror-overrides.scss";
import CodeMirror from "codemirror/lib/codemirror.js";
import "codemirror/mode/javascript/javascript.js";
import "./CodeMirrorNetscriptMode";
import 'codemirror/keymap/sublime.js';
import 'codemirror/keymap/vim.js';
import 'codemirror/keymap/emacs.js';
import 'codemirror/addon/comment/continuecomment.js';
import 'codemirror/addon/edit/closebrackets.js';
import 'codemirror/addon/edit/matchbrackets.js';
import 'codemirror/addon/fold/foldcode.js';
import 'codemirror/addon/fold/foldgutter.js';
import 'codemirror/addon/fold/foldgutter.css';
import 'codemirror/addon/fold/brace-fold.js';
import 'codemirror/addon/fold/indent-fold.js';
import 'codemirror/addon/fold/comment-fold.js';
import 'codemirror/addon/hint/show-hint.js';
import 'codemirror/addon/hint/show-hint.css';
import 'codemirror/addon/lint/lint.js';
import 'codemirror/addon/lint/lint.css';
import 'codemirror/addon/search/match-highlighter.js';
import 'codemirror/addon/selection/active-line.js';
window.JSHINT = require('jshint').JSHINT;
import './CodeMirrorNetscriptHint.js';
import { NetscriptFunctions } from "../NetscriptFunctions";
import { CodeMirrorThemeSetting } from "../Settings/SettingEnums";
import { Settings } from "../Settings/Settings";
import { clearEventListeners } from "../../utils/uiHelpers/clearEventListeners";
import { createElement } from "../../utils/uiHelpers/createElement";
import { createOptionElement } from "../../utils/uiHelpers/createOptionElement";
import { getSelectText,
getSelectValue } from "../../utils/uiHelpers/getSelectData";
import { removeChildrenFromElement } from "../../utils/uiHelpers/removeChildrenFromElement";
// Max number of invisibles to be shown in a group if the "Show Invisibles" option
// is marked
const MaxInvisibles = 20;
function validateInitializationParamters(params) {
if (params.saveAndCloseFn == null) { return false; } // Save & close button function
if (params.quitFn == null) { return false; } // Quitting editor, aka Engine.loadTerminalContent
return true;
}
class CodeMirrorEditorWrapper extends ScriptEditor {
constructor() {
super();
this.tabsStyleElement = null;
}
init(params) {
if (this.editor != null) {
console.error(`CodeMirrorEditor.init() called when it's already initialized`);
return false;
}
// Validate/Sanitize input
if (!validateInitializationParamters(params)) {
console.error(`'params' argument passed into CodeMirrorEditor.init() does not have proper properties`);
return false;
}
// Store the filename input
this.filenameInput = document.getElementById("script-editor-filename");
if (this.filenameInput == null) {
console.error(`Could not get Script Editor filename element (id=script-editor-filename)`);
return false;
}
// Add styling for the "Show Invisibles" option for spaces
const classBase = '.CodeMirror .cm-whitespace-';
const spaceChar = '·';
const style = document.createElement('style');
style.setAttribute('data-name', 'js-show-invisibles');
let rules = '';
let spaceChars = '';
for (let i = 1; i <= MaxInvisibles; ++i) {
spaceChars += spaceChar;
const rule = classBase + i + '::before { content: "' + spaceChars + '";}\n';
rules += rule;
}
style.textContent = rules;
document.head.appendChild(style);
// Add an element for the "Show Invisible" option for tabs
this.tabsStyleElement = document.createElement('style');
document.head.appendChild(this.tabsStyleElement);
// Define a "Save" command for CodeMirror so shortcuts like Ctrl + s
// will save in-game
CodeMirror.commands.save = function() { params.saveAndCloseFn(); }
// Add Netscript Functions to the autocompleter
const netscriptFns = [];
var fnsObj = NetscriptFunctions(null);
for (let name in fnsObj) {
if (fnsObj.hasOwnProperty(name)) {
netscriptFns.push(name);
//Get functions from namespaces
const namespaces = ["bladeburner", "hacknet", "codingcontract", "gang"];
if (namespaces.includes(name)) {
let namespace = fnsObj[name];
if (typeof namespace !== "object") {continue;}
let namespaceFns = Object.keys(namespace);
for (let i = 0; i < namespaceFns.length; ++i) {
netscriptFns.push(namespaceFns[i]);
}
}
}
}
CodeMirror.hint.netscript = function(editor) {
const origList = CodeMirror.hint.javascript(editor) || {from: editor.getCursor(), to: editor.getCursor(), list: []};
origList.list.push(...netscriptFns);
let list = origList.list || [];
let cursor = editor.getCursor();
let currentLine = editor.getLine(cursor.line);
let start = cursor.ch;
let end = start;
while (end < currentLine.length && /[\w$]+/.test(currentLine.charAt(end))) ++end;
while (start && /[\w$]+/.test(currentLine.charAt(start - 1))) --start;
let curWord = start != end && currentLine.slice(start, end);
let regex = new RegExp('^' + curWord, 'i');
let result = {
list: (!curWord ? list : list.filter(function (item) {
return item.match(regex);
})).sort(),
from: CodeMirror.Pos(cursor.line, start),
to: CodeMirror.Pos(cursor.line, end)
};
return result;
};
}
initialized() {
return (this.filenameInput != null);
}
create() {
function safeGetElementById(id, whatFor="") {
const elem = document.getElementById(id);
if (elem == null) {
throw new Error(`Could not find ${whatFor} DOM element(id=${id})`);
}
return elem;
}
function safeClearEventListeners(id, whatFor="") {
const elem = clearEventListeners(id);
if (elem == null) {
throw new Error(`Could not find ${whatFor} DOM element(id=${id})`);
}
return elem;
}
try {
if (!this.initialized()) {
console.warn(`CodeMirrorEditor.create() called when editor was not initialized`);
return;
}
// Initialize CodeMirror Editor
const textAreaElement = safeGetElementById("codemirror-editor", "CodeMirror Textarea");
const formElement = safeGetElementById("codemirror-form-wrapper", "CodeMirror Form Wrapper");
formElement.style.display = "block";
this.editor = CodeMirror.fromTextArea(textAreaElement, {
autofocus: true,
extraKeys: { "Ctrl-Space": "autocomplete" },
foldGutter: true,
gutters: ["CodeMirror-lint-markers", "CodeMirror-linenumbers", "CodeMirror-foldgutter"],
highlightSelectionMatches: true,
hintOptions: { hint: CodeMirror.hint.netscript },
indentUnit: 4,
keyMap: "default",
lineNumbers: true,
matchBrackets: true,
maxInvisibles: 32,
mode: "netscript",
theme: Settings.EditorTheme,
});
// Setup Theme Option
const themeDropdown = safeClearEventListeners("script-editor-option-theme", "Theme Selector");
removeChildrenFromElement(themeDropdown);
const themeOptions = Object.keys(CodeMirrorThemeSetting);
for (let i = 0; i < themeOptions.length; ++i) {
const themeKey = themeOptions[i];
const themeValue = CodeMirrorThemeSetting[themeKey];
themeDropdown.add(createOptionElement(themeKey, themeValue));
}
if (Settings.EditorTheme) {
var initialIndex = 0;
for (var i = 0; i < themeDropdown.options.length; ++i) {
if (themeDropdown.options[i].value === Settings.EditorTheme) {
initialIndex = i;
break;
}
}
themeDropdown.selectedIndex = initialIndex;
} else {
themeDropdown.selectedIndex = 0;
}
themeDropdown.onchange = () => {
const val = themeDropdown.value;
Settings.EditorTheme = val;
this.editor.setOption("theme", val);
};
themeDropdown.onchange();
// Setup Keymap Option
const keybindingDropdown = safeClearEventListeners("script-editor-option-keybinding", "Keymap Selector");
if (keybindingDropdown == null) {
console.error(`Could not find Script Editor's keybinding selector element (id="script-editor-option-keybinding")`);
return false;
}
removeChildrenFromElement(keybindingDropdown);
keybindingDropdown.add(createOptionElement("Default", "default"));
keybindingDropdown.add(createOptionElement("Sublime", "sublime"));
keybindingDropdown.add(createOptionElement("Vim", "vim"));
keybindingDropdown.add(createOptionElement("Emacs", "emacs"));
if (Settings.EditorKeybinding) {
var initialIndex = 0;
for (var i = 0; i < keybindingDropdown.options.length; ++i) {
if (keybindingDropdown.options[i].value === Settings.EditorKeybinding) {
initialIndex = i;
break;
}
}
keybindingDropdown.selectedIndex = initialIndex;
} else {
keybindingDropdown.selectedIndex = 0;
}
keybindingDropdown.onchange = () => {
const val = keybindingDropdown.value;
Settings.EditorKeybinding = val;
this.editor.removeKeyMap("sublime");
this.editor.removeKeyMap("emacs");
this.editor.removeKeyMap("vim");
this.editor.addKeyMap(val);
this.editor.setOption("keyMap", val);
console.log(`Set keymap to ${val} for CodeMirror`);
};
keybindingDropdown.onchange();
// Highlight Active line
const highlightActiveChkBox = safeClearEventListeners("script-editor-option-highlightactiveline", "Active Line Checkbox");
highlightActiveChkBox.onchange = () => {
this.editor.setOption("styleActiveLine", highlightActiveChkBox.checked);
};
highlightActiveChkBox.onchange();
// Show Invisibles
const showInvisiblesChkBox = safeClearEventListeners("script-editor-option-showinvisibles", "Show Invisible Checkbox");
showInvisiblesChkBox.onchange = () => {
const overlayMode = {
name: 'invisibles',
token: function nextToken(stream) {
var ret,
spaces = 0,
space = stream.peek() === ' ';
if (space) {
while (space && spaces < MaxInvisibles) {
++spaces;
stream.next();
space = stream.peek() === ' ';
}
ret = 'whitespace whitespace-' + spaces;
} else {
while (!stream.eol() && !space) {
stream.next();
space = stream.peek() === ' ';
}
ret = 'cm-eol';
}
return ret;
}
};
if (showInvisiblesChkBox.checked) {
// Spaces
this.editor.addOverlay(overlayMode);
// Tabs
this.tabsStyleElement.innerHTML = ".cm-tab {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAYAAAAkuj5RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABDSURBVEhLYxgFo2BkAGYH/9r/QFoAxIGyhx6AORzZA4xD1TcHNjYzQplDB2CLgaECYHkADIZqqgGBoZdsRsHgBgwMAB8iFHF42AERAAAAAElFTkSuQmCC);background-position: right;background-repeat: no-repeat;}";
} else {
this.editor.removeOverlay("invisibles");
this.tabsStyleElement.innerHTML = "";
}
};
showInvisiblesChkBox.onchange();
//Use Soft Tab
const softTabChkBox = safeClearEventListeners("script-editor-option-usesofttab", "Soft Tab Checkbox");
softTabChkBox.onchange = () => {
this.editor.setOption("indentWithTabs", !softTabChkBox.checked);
if (softTabChkBox.checked) {
this.editor.addKeyMap({
name: "soft-tabs-keymap",
"Tab": function (cm) {
if (cm.somethingSelected()) {
var sel = cm.getSelection("\n");
// Indent only if there are multiple lines selected, or if the selection spans a full line
if (sel.length > 0 && (sel.indexOf("\n") > -1 || sel.length === cm.getLine(cm.getCursor().line).length)) {
cm.indentSelection("add");
return;
}
}
if (cm.options.indentWithTabs)
cm.execCommand("insertTab");
else
cm.execCommand("insertSoftTab");
},
"Shift-Tab": function (cm) {
cm.indentSelection("subtract");
}
});
} else {
this.editor.removeKeyMap("soft-tabs-keymap");
}
};
softTabChkBox.onchange();
// Some helper functions for dealing with flexible options
function resetFlexibleOption(id) {
const fieldset = safeGetElementById(id);
removeChildrenFromElement(fieldset);
fieldset.style.display = "block";
return fieldset;
}
function removeFlexibleOption(id) {
// This doesn't really remove it, just sets it to invisible
const fieldset = resetFlexibleOption(id);
fieldset.style.display = "none";
return fieldset;
}
// Flex 1: Automatically Close Brackets and Quotes
const flex1Fieldset = resetFlexibleOption("script-editor-option-flex1-fieldset");
const flex1Id = "script-editor-option-flex1";
flex1Fieldset.appendChild(createElement("label", {
for: flex1Id,
innerText: "Auto-Close Brackets/Quotes",
}));
const flex1Checkbox = createElement("input", {
checked: true,
id: flex1Id,
name: flex1Id,
type: "checkbox",
});
flex1Fieldset.appendChild(flex1Checkbox);
flex1Checkbox.onchange = () => {
this.editor.setOption("autoCloseBrackets", flex1Checkbox.checked);
};
flex1Checkbox.onchange();
// Flex 2: Disable/Enable Linting
const flex2Fieldset = resetFlexibleOption("script-editor-option-flex2-fieldset");
const flex2Id = "script-editor-option-flex2";
flex2Fieldset.appendChild(createElement("label", {
for: flex2Id,
innerText: "Enable Linting",
}));
const flex2Checkbox = createElement("input", {
checked: true,
id: flex2Id,
name: flex2Id,
type: "checkbox",
});
flex2Fieldset.appendChild(flex2Checkbox);
flex2Checkbox.onchange = () => {
if (flex2Checkbox.checked) {
this.editor.setOption("lint", CodeMirror.lint.netscript);
} else {
this.editor.setOption("lint", false);
}
}
flex2Checkbox.onchange();
// Flex 3: Continue Comments
const flex3Fieldset = resetFlexibleOption("script-editor-option-flex3-fieldset");
const flex3Id = "script-editor-option-flex3";
flex3Fieldset.appendChild(createElement("label", {
for: flex3Id,
innerText: "Continue Comments",
}));
const flex3Checkbox = createElement("input", {
checked: true,
id: flex3Id,
name: flex3Id,
type: "checkbox",
});
flex3Fieldset.appendChild(flex3Checkbox);
flex3Checkbox.onchange = () => {
this.editor.setOption("continueComments", flex3Checkbox.checked);
}
flex3Checkbox.onchange();
removeFlexibleOption("script-editor-option-flex4-fieldset");
this.editor.refresh();
console.log(this.editor.options);
} catch(e) {
console.error(`Exception caught: ${e}`);
return false;
}
}
isFocused() {
if (this.editor == null) { return false; }
return this.editor.hasFocus();
}
// Sets the editor to be invisible
setInvisible() {
if (!this.initialized()) {
console.warn(`CodeMirrorEditor.setInvisible() called when editor was not initialized`);
return;
}
if (this.editor != null) {
this.editor.toTextArea();
this.editor = null;
}
const elem = document.getElementById("codemirror-form-wrapper");
if (elem instanceof HTMLElement) {
elem.style.display = "none";
}
}
}
export const CodeMirrorEditor = new CodeMirrorEditorWrapper();

@ -0,0 +1,78 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("codemirror/lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["codemirror/lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
// declare global: JSHINT
function validator(text, options) {
if (!window.JSHINT) {
if (window.console) {
window.console.error("Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run.");
}
return [];
}
// To ignore the 'async/await' errors, we'll manually edit the code ('text')
// that gets processed by JSHINT to include the ignore directory
const splitText = text.split("\n");
const ignoreDirective = " // jshint ignore:line";
for (let i = 0; i < splitText.length; ++i) {
if (splitText[i].match(/.*async function.+{/g)) {
splitText[i] += ignoreDirective;
} else if (splitText[i].match(/.*await.+;/g)) {
splitText[i] += ignoreDirective;
}
}
const sanitizedText = splitText.join("\n");
if (!options.indent) // JSHint error.character actually is a column index, this fixes underlining on lines using tabs for indentation
options.indent = 1; // JSHint default value is 4
JSHINT(sanitizedText, options, options.globals);
var errors = JSHINT.data().errors, result = [];
if (errors) parseErrors(errors, result);
return result;
}
CodeMirror.registerHelper("lint", "netscript", validator);
function parseErrors(errors, output) {
for ( var i = 0; i < errors.length; i++) {
var error = errors[i];
if (error) {
if (error.line == 0) { continue; }
if (error.line < 0) {
if (window.console) {
window.console.warn("Cannot display JSHint error (invalid line " + error.line + ")", error);
}
continue;
}
var start = error.character - 1, end = start + 1;
if (error.evidence) {
var index = error.evidence.substring(start).search(/.\b/);
if (index > -1) {
end += index;
}
}
// Convert to format expected by validation service
var hint = {
message: error.reason,
severity: error.code ? (error.code.startsWith('W') ? "warning" : "error") : "error",
from: CodeMirror.Pos(error.line - 1, start),
to: CodeMirror.Pos(error.line - 1, end)
};
output.push(hint);
}
}
}
});

File diff suppressed because it is too large Load Diff

@ -0,0 +1,58 @@
// Base Script Editor class for the Ace/CodeMirror/etc. wrappers
const beautify = require('js-beautify').js_beautify;
export class ScriptEditor {
constructor() {
this.editor = null; // Stores the CodeMirror editor reference
this.filenameInput = null; // Stores the filename input DOM element
}
init() {
throw new Error(`Tried to initialize base ScriptEditor class`);
}
beautifyScript() {
if (this.editor == null) {
console.warn(`ScriptEditor.beautifyScript() called when editor was not initialized`);
return;
}
let code = this.editor.getValue();
code = beautify(code, {
indent_size: 4,
brace_style: "preserve-inline",
});
this.editor.setValue(code);
}
openScript(filename="", code="") {
if (this.editor == null || this.filenameInput == null) {
console.warn(`ScriptEditor.openScript() called when editor was not initialized`);
return;
}
if (filename != "") {
this.filenameInput.value = filename;
this.editor.setValue(code);
}
this.editor.focus();
}
getCode() {
if (this.editor == null) {
console.warn(`ScriptEditor.getCode() called when editor was not initialized`);
return "";
}
return this.editor.getValue();
}
getFilename() {
if (this.filenameInput == null) {
console.warn(`ScriptEditor.getFilename() called when editor was not initialized`);
return "";
}
return this.filenameInput.value;
}
}

@ -1,16 +0,0 @@
/**
* Enum Of allowed values for the 'OwnedAugmentationsOrder' setting
*/
export enum OwnedAugmentationsOrderSetting {
Alphabetically,
AcquirementTime,
}
/**
* Enum Of allowed values for the 'OwnedAugmentationsOrder' setting
*/
export enum PurchaseAugmentationsOrderSetting {
Cost,
Default,
Reputation,
}

@ -0,0 +1,83 @@
// Enums that defined allowed values for setting configuration
export enum CodeMirrorThemeSetting {
Monokai = "monokai",
Day_3024 = "3024-day",
Night_3024 = "3024-night",
abcdef = "abcdef",
Ambiance_mobile = "ambiance-mobile",
Ambiance = "ambiance",
Base16_dark = "base16-dark",
Base16_light = "base16-light",
Bespin = "bespin",
Blackboard = "blackboard",
Cobalt = "cobalt",
Colorforth = "colorforth",
Darcula = "darcula",
Dracula = "dracula",
Duotone_dark = "duotone-dark",
Duotone_light = "duotone-light",
Eclipse = "eclipse",
Elegant = "elegant",
Erlang_dark = "erlang-dark",
Gruvbox_dark = "gruvbox-dark",
Hopscotch = "hopscotch",
Icecoder = "icecoder",
Idea = "idea",
Isotope = "isotope",
Lesser_dark = "lesser-dark",
Liquibyte = "liquibyte",
Lucario = "lucario",
Material = "material",
Mbo = "mbo",
Mdn_like = "mdn-like",
Midnight = "midnight",
Neat = "neat",
Neo = "neo",
Night = "night",
Oceanic_next = "oceanic-next",
Panda_syntax = "panda-syntax",
Paraiso_dark = "paraiso-dark",
Paraiso_light = "paraiso-light",
Pastel_on_dark = "pastel-on-dark",
Railscasts = "railscasts",
Rubyblue = "rubyblue",
Seti = "seti",
Shadowfox = "shadowfox",
Solarized = "solarized",
ssms = "ssms",
The_matrix = "the-matrix",
Tomorrow_night_bright = "tomorrow-night-bright",
Tomorrow_night_eighties = "tomorrow-night-eighties",
Ttcn = "ttcn",
Twilight = "twilight",
Vibrant_ink = "vibrant-ink",
xq_dark = "xq-dark",
xq_light = "xq-light",
Yeti = "yeti",
Zenburn = "zenburn",
}
/**
* Allowed values for the "Editor" setting
*/
export enum EditorSetting {
Ace = "Ace",
CodeMirror = "CodeMirror",
}
/**
* Allowed values for the 'OwnedAugmentationsOrder' setting
*/
export enum PurchaseAugmentationsOrderSetting {
Cost,
Default,
Reputation,
}
/**
* Allowed values for the 'OwnedAugmentationsOrder' setting
*/
export enum OwnedAugmentationsOrderSetting {
Alphabetically,
AcquirementTime,
}

@ -1,5 +1,8 @@
import { ISelfInitializer, ISelfLoading } from "./types"; import { ISelfInitializer, ISelfLoading } from "../types";
import { OwnedAugmentationsOrderSetting, PurchaseAugmentationsOrderSetting } from "./SettingEnums"; import { CodeMirrorThemeSetting,
EditorSetting,
OwnedAugmentationsOrderSetting,
PurchaseAugmentationsOrderSetting } from "./SettingEnums";
/** /**
* Represents the default settings the player could customize. * Represents the default settings the player could customize.
@ -65,6 +68,11 @@ interface IDefaultSettings {
* Represents all possible settings the player wants to customize to their play style. * Represents all possible settings the player wants to customize to their play style.
*/ */
interface ISettings extends IDefaultSettings { interface ISettings extends IDefaultSettings {
/**
* Which editor should be used (CodeMirror or Ace)?
*/
Editor: EditorSetting;
/** /**
* The keybinding to use in the script editor. * The keybinding to use in the script editor.
* TODO: This should really be an enum of allowed values. * TODO: This should really be an enum of allowed values.
@ -75,7 +83,7 @@ interface ISettings extends IDefaultSettings {
* The theme used in the script editor. * The theme used in the script editor.
* TODO: This should really be an enum of allowed values. * TODO: This should really be an enum of allowed values.
*/ */
EditorTheme: string; EditorTheme: string | CodeMirrorThemeSetting;
/** /**
* What order the player's owned Augmentations/Source Files should be displayed in * What order the player's owned Augmentations/Source Files should be displayed in
@ -110,6 +118,7 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
AutosaveInterval: defaultSettings.AutosaveInterval, AutosaveInterval: defaultSettings.AutosaveInterval,
CodeInstructionRunTime: 25, CodeInstructionRunTime: 25,
DisableHotkeys: defaultSettings.DisableHotkeys, DisableHotkeys: defaultSettings.DisableHotkeys,
Editor: EditorSetting.Ace,
EditorKeybinding: "ace", EditorKeybinding: "ace",
EditorTheme: "Monokai", EditorTheme: "Monokai",
Locale: "en", Locale: "en",

@ -30,7 +30,7 @@ import {findRunningScript, RunningScript,
AllServersMap, isScriptFilename} from "./Script"; AllServersMap, isScriptFilename} from "./Script";
import {AllServers, GetServerByHostname, import {AllServers, GetServerByHostname,
getServer, Server} from "./Server"; getServer, Server} from "./Server";
import {Settings} from "./Settings"; import {Settings} from "./Settings/Settings";
import {SpecialServerIps, import {SpecialServerIps,
SpecialServerNames} from "./SpecialServerIps"; SpecialServerNames} from "./SpecialServerIps";
import {getTextFile} from "./TextFile"; import {getTextFile} from "./TextFile";

@ -51,10 +51,12 @@ import { displayCreateProgramContent,
initCreateProgramButtons } from "./Programs/ProgramHelpers"; initCreateProgramButtons } from "./Programs/ProgramHelpers";
import {redPillFlag, hackWorldDaemon} from "./RedPill"; import {redPillFlag, hackWorldDaemon} from "./RedPill";
import {saveObject, loadGame} from "./SaveObject"; import {saveObject, loadGame} from "./SaveObject";
import {loadAllRunningScripts, scriptEditorInit, import { getCurrentEditor,
updateScriptEditorContent} from "./Script"; loadAllRunningScripts,
scriptEditorInit,
updateScriptEditorContent } from "./Script";
import {AllServers, Server, initForeignServers} from "./Server"; import {AllServers, Server, initForeignServers} from "./Server";
import {Settings} from "./Settings"; import {Settings} from "./Settings/Settings";
import { initSourceFiles, SourceFiles } from "./SourceFile"; import { initSourceFiles, SourceFiles } from "./SourceFile";
import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags"; import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
@ -103,6 +105,7 @@ import "../css/buttons.scss";
import "../css/mainmenu.scss"; import "../css/mainmenu.scss";
import "../css/characteroverview.scss"; import "../css/characteroverview.scss";
import "../css/terminal.scss"; import "../css/terminal.scss";
import "../css/scripteditor.scss";
import "../css/menupages.scss"; import "../css/menupages.scss";
import "../css/redpill.scss"; import "../css/redpill.scss";
import "../css/stockmarket.scss"; import "../css/stockmarket.scss";
@ -137,6 +140,14 @@ import "../css/treant.css";
*/ */
$(document).keydown(function(e) { $(document).keydown(function(e) {
if (Settings.DisableHotkeys === true) {return;} if (Settings.DisableHotkeys === true) {return;}
// These hotkeys should be disabled if the player is writing scripts
try {
if (getCurrentEditor().isFocused()) {
return;
}
} catch(e) {}
if (!Player.isWorking && !redPillFlag && !inMission && !cinematicTextFlag) { if (!Player.isWorking && !redPillFlag && !inMission && !cinematicTextFlag) {
if (e.keyCode == 84 && e.altKey) { if (e.keyCode == 84 && e.altKey) {
e.preventDefault(); e.preventDefault();
@ -268,12 +279,12 @@ const Engine = {
loadScriptEditorContent: function(filename = "", code = "") { loadScriptEditorContent: function(filename = "", code = "") {
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.scriptEditorContent.style.display = "block"; Engine.Display.scriptEditorContent.style.display = "block";
var editor = ace.edit('javascript-editor'); try {
if (filename != "") { getCurrentEditor().openScript(filename, code);
document.getElementById("script-editor-filename").value = filename; } catch(e) {
editor.setValue(code); exceptionAlert(e);
} }
editor.focus();
updateScriptEditorContent(); updateScriptEditorContent();
routing.navigateTo(Page.ScriptEditor); routing.navigateTo(Page.ScriptEditor);
MainMenuLinks.ScriptEditor.classList.add("active"); MainMenuLinks.ScriptEditor.classList.add("active");

@ -120,7 +120,8 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<input id="script-editor-filename" type="text" maxlength="30" tabindex="1" /> <input id="script-editor-filename" type="text" maxlength="30" tabindex="1" />
</div> </div>
<div id="javascript-editor"></div> <div id="ace-editor"></div>
<form id="codemirror-form-wrapper"><textarea id="codemirror-editor"></textarea></form>
<div id="script-editor-buttons-wrapper"></div> <!-- Buttons below script editor --> <div id="script-editor-buttons-wrapper"></div> <!-- Buttons below script editor -->
</div> <!-- End wrapper --> </div> <!-- End wrapper -->
@ -128,26 +129,21 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<div id="script-editor-options-panel"> <div id="script-editor-options-panel">
<h1 style="color:white;"> Script Editor Options </h1> <h1 style="color:white;"> Script Editor Options </h1>
<fieldset> <fieldset>
<label for="script-editor-option-theme">Theme</label> <label for="script-editor-option-editor">Editor</label>
<select id="script-editor-option-theme"> <select id="script-editor-option-editor">
<option value="Chaos">Chaos</option> <option value="Ace">Ace</option>
<option value="Chrome">Chrome</option> <option value="CodeMirror">CodeMirror</option>
<option value="Monokai">Monokai</option>
<option value="Solarized_Dark">Solarized Dark</option>
<option value="Solarized_Light">Solarized Light</option>
<option value="Terminal">Terminal</option>
<option value="Twilight">Twilight</option>
<option value="XCode">XCode</option>
</select> </select>
</fieldset> </fieldset>
<fieldset>
<label for="script-editor-option-theme">Theme</label>
<select id="script-editor-option-theme"></select>
</fieldset>
<fieldset> <fieldset>
<label for="script-editor-option-keybinding">Key Binding</label> <label for="script-editor-option-keybinding">Key Binding</label>
<select id="script-editor-option-keybinding"> <select id="script-editor-option-keybinding"></select>
<option value="ace">Ace</option>
<option value="vim">Vim</option>
<option value="emacs">Emacs</option>
</select>
</fieldset> </fieldset>
<fieldset> <fieldset>
@ -165,11 +161,11 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<input type="checkbox" name="script-editor-option-usesofttab" id="script-editor-option-usesofttab" checked> <input type="checkbox" name="script-editor-option-usesofttab" id="script-editor-option-usesofttab" checked>
</fieldset> </fieldset>
<fieldset> <fieldset id="script-editor-option-flex1-fieldset"></fieldset>
<label for="script-editor-option-maxerr" class="tooltip">Max Error Count</label> <fieldset id="script-editor-option-flex2-fieldset"></fieldset>
<input type="range" max="1000" min="50" value="200" step="1" name="script-editor-option-maxerr" id="script-editor-option-maxerr" /> <fieldset id="script-editor-option-flex3-fieldset"></fieldset>
<em id="script-editor-option-maxerror-value-label" style="font-style: normal;"></em> <fieldset id="script-editor-option-flex4-fieldset"></fieldset>
</fieldset>
</div> <!-- End script editor options panel --> </div> <!-- End script editor options panel -->
</div> </div>

@ -1,5 +1,5 @@
import {Engine} from "../engine"; import {Engine} from "../engine";
import {Settings} from "../Settings"; import {Settings} from "../Settings/Settings";
import {numeralWrapper} from "./numeralFormat"; import {numeralWrapper} from "./numeralFormat";

@ -12,10 +12,13 @@ interface ICreateElementAnchorOptions {
*/ */
interface ICreateElementInputOptions { interface ICreateElementInputOptions {
checked?: boolean; checked?: boolean;
max?: string;
maxLength?: number; maxLength?: number;
min?: string;
name?: string; name?: string;
pattern?: string; pattern?: string;
placeholder?: string; placeholder?: string;
step?: string;
type?: string; type?: string;
value?: string; value?: string;
} }
@ -130,6 +133,15 @@ function setElementInput(el: HTMLInputElement, params: ICreateElementInputOption
if (params.placeholder !== undefined) { if (params.placeholder !== undefined) {
el.placeholder = params.placeholder; el.placeholder = params.placeholder;
} }
if (params.max !== undefined) {
el.max = params.max;
}
if (params.min !== undefined) {
el.min = params.min;
}
if (params.step !== undefined) {
el.step = params.step;
}
} }
function setElementLabel(el: HTMLLabelElement, params: ICreateElementLabelOptions) { function setElementLabel(el: HTMLLabelElement, params: ICreateElementLabelOptions) {