aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOsmium Sorcerer <os@sof.beauty>2026-06-06 02:07:05 +0000
committerOsmium Sorcerer <os@sof.beauty>2026-06-06 03:09:27 +0000
commite0ce108e0806d18353ad85125b2b5f1b1c67e07d (patch)
tree4e70de464db82bf28d42b10bf260ba7361402f55
parentfd75f3116aa30eb4958cc747f944f202ec69a484 (diff)
CSP hardening: remove inline scripts
The next layer after input validaton to achive the paranoid levels of security. Remove all event handlers inside HTML attributes and add them in TS for each element, allowing `script-src 'self'` to be used as a CSP directive. Buttons that passed some value and had a shared function went into a global listener with data-action attribute, while all the individual elements received their own event listener. This is a mess, but my goal was to end up as close as I could to one-to-one translation of how functions were originally attached to elements.
-rw-r--r--public/client.html126
-rw-r--r--webAO/client.ts1
-rw-r--r--webAO/dom-events.ts226
-rw-r--r--webAO/packets/handlers/handleSI.ts6
4 files changed, 276 insertions, 83 deletions
diff --git a/public/client.html b/public/client.html
index 4fa3216..55214d3 100644
--- a/public/client.html
+++ b/public/client.html
@@ -106,12 +106,11 @@
</div>
<div id="client_charselect">
<p>Choose your character</p>
- <button onclick="pickChar(-1)">Or spectate</button>
+ <button data-action="pick-char" data-id="-1">Or spectate</button>
<br /><br />
<input
id="client_charactersearch"
placeholder="Search"
- oninput="chartable_filter(event)"
/>
<br /><br />
<div id="client_chartable"></div>
@@ -124,7 +123,7 @@
<p id="client_error_code">
Error code: <span id="error_id">(none)</span>
</p>
- <button id="client_reconnect" onclick="ReconnectButton()">
+ <button id="client_reconnect">
Reconnect
</button>
</div>
@@ -139,57 +138,57 @@
<div id="client_fullview">
<img id="client_court" />
<div id="client_stitch_court">
- <img id="client_court_def" onerror="imgError(this);" />
- <img id="client_court_deft" onerror="imgError(this);" />
- <img id="client_court_wit" onerror="imgError(this);" />
- <img id="client_court_prot" onerror="imgError(this);" />
- <img id="client_court_pro" onerror="imgError(this);" />
+ <img id="client_court_def" data-error="img" />
+ <img id="client_court_deft" data-error="img" />
+ <img id="client_court_wit" data-error="img" />
+ <img id="client_court_prot" data-error="img" />
+ <img id="client_court_pro" data-error="img" />
</div>
<div
id="client_def_pair_char"
class="client_char"
alt="Paired character"
>
- <img id="client_def_pair_img" onerror="charError(this);" />
+ <img id="client_def_pair_img" data-error="char" />
</div>
<div id="client_def_char" class="client_char" alt="Character">
- <img id="client_def_char_img" onerror="charError(this);" />
+ <img id="client_def_char_img" data-error="char" />
</div>
<div
id="client_wit_pair_char"
class="client_char"
alt="Paired character"
>
- <img id="client_wit_pair_img" onerror="charError(this);" />
+ <img id="client_wit_pair_img" data-error="char" />
</div>
<div id="client_wit_char" class="client_char" alt="Character">
- <img id="client_wit_char_img" onerror="charError(this);" />
+ <img id="client_wit_char_img" data-error="char" />
</div>
<div
id="client_pro_pair_char"
class="client_char"
alt="Paired character"
>
- <img id="client_pro_pair_img" onerror="charError(this);" />
+ <img id="client_pro_pair_img" data-error="char" />
</div>
<div id="client_pro_char" class="client_char" alt="Character">
- <img id="client_pro_char_img" onerror="charError(this);" />
+ <img id="client_pro_char_img" data-error="char" />
</div>
<img id="client_def_bench" class="client_bench" />
<img id="client_wit_bench" class="client_bench" />
<img id="client_pro_bench" class="client_bench" />
</div>
<div id="client_classicview">
- <img id="client_court_classic" onerror="imgError(this);" />
+ <img id="client_court_classic" data-error="img" />
<div
id="client_pair_char"
class="client_char"
alt="Paired character"
>
- <img id="client_pair_img" onerror="charError(this);" />
+ <img id="client_pair_img" data-error="char" />
</div>
<div id="client_char" class="client_char" alt="Character">
- <img id="client_char_img" onerror="charError(this);" />
+ <img id="client_char_img" data-error="char" />
</div>
<img id="client_bench_classic" class="client_bench" />
</div>
@@ -202,13 +201,13 @@
id="client_evi"
src=""
alt="Character Evidence"
- onerror="imgError(this);"
+ data-error="img"
/>
<img
id="client_testimony"
alt="Testimony overlay"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="
- onerror="imgError(this);"
+ data-error="img"
/>
<img
id="client_shout"
@@ -236,16 +235,13 @@
</div>
</div>
</div>
- <form onsubmit="return false">
<input
id="client_inputbox"
class="long"
type="text"
- onkeypress="onEnter(event)"
placeholder="Say something&hellip;"
autocomplete="off"
/>
- </form>
<div id="client_bars">
<span id="client_defense_hp" class="health-box">
<div class="health-bar"></div>
@@ -268,7 +264,8 @@
id="button_1"
alt="Hold it!"
class="client_button"
- onclick="toggleShout(1)"
+ data-action="toggle-shout"
+ data-id="1"
>
Hold it!
</button>
@@ -276,7 +273,8 @@
id="button_2"
alt="Objection!"
class="client_button"
- onclick="toggleShout(2)"
+ data-action="toggle-shout"
+ data-id="2"
>
Objection!
</button>
@@ -284,7 +282,8 @@
id="button_3"
alt="Take That!"
class="client_button"
- onclick="toggleShout(3)"
+ data-action="toggle-shout"
+ data-id="3"
>
Take That!
</button>
@@ -292,8 +291,9 @@
id="button_4"
alt="Custom"
class="client_button"
- onclick="toggleShout(4)"
style="display: none"
+ data-action="toggle-shout"
+ data-id="4"
>
Custom
</button>
@@ -328,8 +328,8 @@
id="button_flip"
alt="Flip"
class="client_button"
- onclick="toggleEffect(this)"
style="display: none"
+ data-action="toggle-effect"
>
Flip
</button>
@@ -337,7 +337,7 @@
id="button_flash"
alt="Flash"
class="client_button"
- onclick="toggleEffect(this)"
+ data-action="toggle-effect"
>
Flash
</button>
@@ -345,8 +345,8 @@
id="button_shake"
alt="Shake"
class="client_button"
- onclick="toggleEffect(this)"
style="display: none"
+ data-action="toggle-effect"
>
Shake
</button>
@@ -358,7 +358,6 @@
<select
id="role_select"
name="role_select"
- onchange="changeRoleOOC()"
>
<option value="">Default</option>
<option value="def">Defense</option>
@@ -376,7 +375,6 @@
id="char_change"
alt="Change"
class="client_button hover_button"
- onclick="changeCharacter()"
>
Change Character
</button>
@@ -384,7 +382,6 @@
id="char_random"
alt="Random"
class="client_button hover_button"
- onclick="randomCharacterOOC()"
>
Random Character
</button>
@@ -395,7 +392,6 @@
id="button_toggle_pairing"
alt="Pairing"
class="client_button"
- onclick="toggleElement('pairing_settings')"
>
Pairing
</button>
@@ -442,7 +438,6 @@
id="pair_reset"
type="button"
value="Reset"
- onclick="resetOffset()"
/>
</span>
</span>
@@ -457,7 +452,6 @@
id="showname"
name="showname"
type="checkbox"
- onclick="showname_click()"
checked
/>
<br />
@@ -496,7 +490,6 @@
<span id="judge_action" style="display: none">
<span
id="menu_wt"
- onclick="initWT()"
class="judge_button"
style="color: blue"
><i>Witness<br />Testimony</i></span
@@ -504,7 +497,6 @@
<span
id="menu_ce"
- onclick="initCE()"
class="judge_button"
style="color: red"
><i>Cross<br />Examination</i></span
@@ -512,7 +504,6 @@
<span
id="menu_nguilty"
- onclick="notguilty()"
class="judge_button"
style="
color: white;
@@ -530,7 +521,6 @@
<span
id="menu_guilty"
- onclick="guilty()"
class="judge_button"
style="color: black; font-family: serif; font-size: 1.5em"
>Guilty</span
@@ -538,20 +528,20 @@
<br />
<span style="display: inline-block; vertical-align: middle">
- <span id="menu_rhpd" onclick="redHPD()" class="healthchange_button">
+ <span id="menu_rhpd" class="healthchange_button">
&#8863;
</span>
<span style="font-size: 1.25em">Defense</span>
- <span id="menu_ahpd" onclick="addHPD()" class="healthchange_button">
+ <span id="menu_ahpd" class="healthchange_button">
&#8862;
</span>
</span>
<span style="display: inline-block; vertical-align: middle">
- <span id="menu_ahpp" onclick="addHPP()" class="healthchange_button">
+ <span id="menu_ahpp" class="healthchange_button">
&#8862;
</span>
<span style="font-size: 1.25em">Prosecution</span>
- <span id="menu_rhpp" onclick="redHPP()" class="healthchange_button">
+ <span id="menu_rhpp" class="healthchange_button">
&#8863;
</span>
</span>
@@ -567,7 +557,6 @@
<select
name="mute_select"
id="mute_select"
- onchange="mutelist_click(event)"
></select>
</span>
</fieldset>
@@ -579,23 +568,23 @@
<div id="client_menu">
<div id="client_menu_buttons">
<div class="hrtext">Main Menu</div>
- <span id="menu_1" onclick="toggleMenu(1)" class="menu_button active">
+ <span id="menu_1" data-action="toggle-menu" data-id="1" class="menu_button active">
<b class="menu_icon">&#128205;</b>
<div class="menu_text">Areas</div>
</span>
- <span id="menu_2" onclick="toggleMenu(2)" class="menu_button">
+ <span id="menu_2" data-action="toggle-menu" data-id="2" class="menu_button">
<b class="menu_icon">&#128188;</b>
<div class="menu_text">Evidence</div>
</span>
- <span id="menu_3" onclick="toggleMenu(3)" class="menu_button">
+ <span id="menu_3" data-action="toggle-menu" data-id="3" class="menu_button">
<b class="menu_icon">&#128295;</b>
<div class="menu_text">Settings</div>
</span>
- <span id="menu_4" onclick="toggleMenu(4)" class="menu_button">
+ <span id="menu_4" data-action="toggle-menu" data-id="4" class="menu_button">
<b class="menu_icon">&#10067;</b>
<div class="menu_text">About</div>
</span>
- <span id="menu_cm" onclick="callMod()" class="menu_button">
+ <span id="menu_cm" class="menu_button">
<b class="menu_icon">&#128680;</b>
<div class="menu_text" style="color: #ce2727">Call Mod</div>
</span>
@@ -615,7 +604,7 @@
id="bg_preview"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="
alt="Background Preview"
- onerror="imgError(this);"
+ data-error="img"
/>
</span>
<span style="display: inline-block">
@@ -624,7 +613,6 @@
<select
id="bg_select"
name="bg_select"
- onchange="updateBackgroundPreview()"
style="margin-top: 10px"
></select>
<br />
@@ -640,7 +628,6 @@
id="bg_change"
alt="Change"
class="client_button hover_button"
- onclick="changeBackgroundOOC()"
>
Change
</button>
@@ -686,7 +673,7 @@
class="evi_icon"
src=""
alt="Evidence Icon"
- onerror="imgError(this);"
+ data-error="img"
/>
<div id="evi_options">
<input
@@ -700,7 +687,6 @@
<select
id="evi_select"
name="evi_select"
- onchange="updateEvidenceIcon()"
></select>
<input
id="evi_filename"
@@ -725,7 +711,6 @@
id="evi_add"
alt="Add Evidence"
class="client_button hover_button"
- onclick="addEvidence()"
>
Add
</button>
@@ -733,7 +718,6 @@
id="evi_edit"
alt="Edit Evidence"
class="client_button hover_button inactive"
- onclick="editEvidence()"
>
Edit
</button>
@@ -741,7 +725,6 @@
id="evi_cancel"
alt="Cancel Evidence"
class="client_button hover_button inactive"
- onclick="cancelEvidence()"
>
Cancel
</button>
@@ -749,7 +732,6 @@
id="evi_del"
alt="Remove Evidence"
class="client_button hover_button inactive"
- onclick="deleteEvidence()"
>
Remove
</button>
@@ -762,7 +744,7 @@
id="button_present"
alt="Present"
class="client_button"
- onclick="toggleEffect(this)"
+ data-action="toggle-effect"
>
Present
</button>
@@ -781,30 +763,26 @@
max="1"
value="1"
step="0.01"
- onchange="changeMusicVolume()"
/>
<p>SFX</p>
<audio
id="client_sfxaudio"
- onvolumechange="changeSFXVolume()"
- onerror="opusCheck(this)"
+ data-error="opus-check"
controls
></audio>
<p>Shouts</p>
<audio
id="client_shoutaudio"
- onvolumechange="changeShoutVolume()"
- onerror="opusCheck(this)"
+ data-error="opus-check"
controls
></audio>
<p>Testimony/Guilty</p>
<audio
id="client_testimonyaudio"
- onvolumechange="changeTestimonyVolume()"
- onerror="opusCheck(this)"
+ data-error="opus-check"
controls
></audio>
@@ -817,7 +795,6 @@
max="1"
value="1"
step="0.01"
- onchange="changeBlipVolume()"
/>
<br />
<br />
@@ -825,7 +802,6 @@
<select
id="client_themeselect"
name="client_themeselect"
- onchange="reloadTheme()"
>
<option value="default" selected>Default</option>
<option value="classic">Classic</option>
@@ -840,7 +816,6 @@
<select
id="client_chatboxselect"
name="client_chatboxselect"
- onchange="setChatbox(this.value)"
>
<option value="dynamic" selected>Use characters</option>
<option value="aa">AA</option>
@@ -883,10 +858,9 @@
<select
id="client_iniselect"
name="client_iniselect"
- onchange="updateIniswap()"
></select>
<input id="client_ininame" name="client_ininame" />
- <button id="client_inichange" onclick="iniedit()">Change</button>
+ <button id="client_inichange">Change</button>
<br />
<br />
<p>Pan-tilt (experimental)</p>
@@ -895,7 +869,6 @@
type="checkbox"
id="client_pantilt"
name="client_pantilt"
- onclick="switchPanTilt()"
/>
<br />
<br />
@@ -905,7 +878,6 @@
type="checkbox"
id="client_hdviewport"
name="client_hdviewport"
- onclick="switchAspectRatio()"
/>
<br />
<label for="client_hdviewport_offset">Offset chatbox:</label>
@@ -913,7 +885,6 @@
type="checkbox"
id="client_hdviewport_offset"
id="client_hdviewport_offset"
- onclick="switchChatOffset()"
/>
<br />
<br />
@@ -925,7 +896,6 @@
rows="4"
cols="10"
placeholder="Put 1 callword per line here"
- onchange="changeCallwords()"
></textarea>
<br />
<br />
@@ -941,7 +911,7 @@
</fieldset>
<br />
<br />
- <button id="client_disconnect" onclick="DisconnectButton()">
+ <button id="client_disconnect">
Disconnect
</button>
</span>
@@ -993,7 +963,6 @@
<input
id="client_oocinputbox"
type="text"
- onkeypress="onOOCEnter(event)"
/>
</span>
<span
@@ -1004,7 +973,6 @@
id="client_replaygo"
style="width: 25%"
type="button"
- onclick="onReplayGo(event)"
value="GO"
/>
<input
@@ -1022,12 +990,10 @@
<input
id="client_musicsearch"
placeholder="Search"
- oninput="musiclist_filter(event)"
/>
<select
id="client_musiclist"
size="5"
- onchange="musiclist_click(event)"
></select>
</template>
diff --git a/webAO/client.ts b/webAO/client.ts
index f013f0a..7ad885f 100644
--- a/webAO/client.ts
+++ b/webAO/client.ts
@@ -23,6 +23,7 @@ import {
} from "./client/fetchLists";
import { ExMsgType, handleRegisterCredential, handleAssertCredential, AuthState } from "./ext_packet"
import { initWebAuthn } from "./auth"
+import "./dom-events"
const { ip: serverIP, connect, mode, theme, serverName, char: autoChar, area: autoArea } = queryParser();
export { autoChar, autoArea };
diff --git a/webAO/dom-events.ts b/webAO/dom-events.ts
new file mode 100644
index 0000000..dbb0002
--- /dev/null
+++ b/webAO/dom-events.ts
@@ -0,0 +1,226 @@
+import { sendPasskeyLoginRequest } from "./ext_packet";
+import { pickChar } from "./dom/pickChar";
+import { ReconnectButton } from "./dom/reconnectButton";
+import { toggleShout } from "./dom/toggleShout";
+import { toggleEffect } from "./dom/toggleEffect";
+import { changeCharacter } from "./dom/changeCharacter";
+import { randomCharacterOOC } from "./dom/randomCharacterOOC";
+import { toggleElement } from "./dom/toggleElement";
+import { resetOffset } from "./dom/resetOffset";
+import { showname_click } from "./dom/showNameClick";
+import { initWT } from "./dom/initWT";
+import { initCE } from "./dom/initCE";
+import { notguilty } from "./dom/notGuilty";
+import { guilty } from "./dom/guilty";
+import { redHPD } from "./dom/redHPD";
+import { addHPD } from "./dom/addHPD";
+import { redHPP } from "./dom/redHPP";
+import { addHPP } from "./dom/addHPP";
+import { toggleMenu } from "./dom/toggleMenu";
+import { callMod } from "./dom/callMod";
+import { changeBackgroundOOC } from "./dom/changeBackgroundOOC";
+import { addEvidence } from "./dom/addEvidence";
+import { editEvidence } from "./dom/editEvidence";
+import { cancelEvidence } from "./dom/cancelEvidence";
+import { deleteEvidence } from "./dom/deleteEvidence";
+import { iniedit } from "./dom/iniEdit";
+import { switchPanTilt } from "./dom/switchPanTilt";
+import { switchAspectRatio } from "./dom/switchAspectRatio";
+import { switchChatOffset } from "./dom/switchChatOffset";
+import { DisconnectButton } from "./dom/disconnectButton";
+import { onReplayGo } from "./dom/onReplayGo";
+import { chartable_filter } from "./dom/charTableFilter";
+import { musiclist_filter } from "./dom/musicListFilter";
+import { musiclist_click } from "./dom/musicListClick";
+import { changeRoleOOC } from "./dom/changeRoleOOC";
+import { mutelist_click } from "./dom/muteListClick";
+import { updateBackgroundPreview } from "./dom/updateBackgroundPreview";
+import { updateEvidenceIcon } from "./dom/updateEvidenceIcon";
+import { changeMusicVolume } from "./dom/changeMusicVolume";
+import { changeBlipVolume } from "./dom/changeBlipVolume";
+import { setChatbox } from "./dom/setChatbox";
+import { reloadTheme } from "./dom/reloadTheme";
+import { updateIniswap } from "./dom/updateIniswap";
+import { changeCallwords } from "./dom/changeCallwords";
+import { imgError } from "./dom/imgError";
+import { charError } from "./dom/charError";
+import { opusCheck } from "./dom/opusCheck";
+import { onEnter } from "./dom/onEnter";
+import { onOOCEnter } from "./dom/onOOCEnter";
+import {
+ changeSFXVolume,
+ changeShoutVolume,
+ changeTestimonyVolume,
+} from "./dom/changeVolume";
+
+document.addEventListener("click", (e: MouseEvent) => {
+ if (!(e.target instanceof HTMLElement)) {
+ return;
+ }
+ const button = e.target.closest("[data-action]");
+ if (!(button instanceof HTMLElement)) {
+ return;
+ }
+
+ switch (button.dataset.action) {
+ case "pick-char":
+ pickChar(Number(button.dataset.id));
+ break;
+ case "toggle-shout":
+ toggleShout(Number(button.dataset.id));
+ break;
+ case "toggle-effect":
+ toggleEffect(button);
+ break;
+ case "toggle-menu":
+ toggleMenu(Number(button.dataset.id));
+ break;
+ default:
+ break;
+ }
+});
+
+document
+ .getElementById("client_reconnect")
+ .addEventListener("click", () => ReconnectButton());
+document
+ .getElementById("char_change")
+ .addEventListener("click", changeCharacter);
+document
+ .getElementById("char_random")
+ .addEventListener("click", () => randomCharacterOOC());
+document
+ .getElementById("button_toggle_pairing")
+ .addEventListener("click", () => toggleElement("pairing_settings"));
+document.getElementById("pair_reset").addEventListener("click", resetOffset);
+document.getElementById("showname").addEventListener("click", showname_click);
+document.getElementById("menu_wt").addEventListener("click", () => initWT());
+document.getElementById("menu_ce").addEventListener("click", () => initCE());
+document
+ .getElementById("menu_nguilty")
+ .addEventListener("click", () => notguilty());
+document
+ .getElementById("menu_guilty")
+ .addEventListener("click", () => guilty());
+document.getElementById("menu_rhpd").addEventListener("click", () => redHPD());
+document.getElementById("menu_ahpd").addEventListener("click", () => addHPD());
+document.getElementById("menu_rhpp").addEventListener("click", () => redHPP());
+document.getElementById("menu_ahpp").addEventListener("click", () => addHPP());
+document.getElementById("menu_cm").addEventListener("click", () => callMod());
+document
+ .getElementById("bg_change")
+ .addEventListener("click", () => changeBackgroundOOC());
+document
+ .getElementById("evi_add")
+ .addEventListener("click", () => addEvidence());
+document
+ .getElementById("evi_edit")
+ .addEventListener("click", () => editEvidence());
+document
+ .getElementById("evi_cancel")
+ .addEventListener("click", () => cancelEvidence());
+document
+ .getElementById("evi_del")
+ .addEventListener("click", () => deleteEvidence());
+document
+ .getElementById("client_inichange")
+ .addEventListener("click", () => iniedit());
+document
+ .getElementById("client_pantilt")
+ .addEventListener("click", () => switchPanTilt());
+document
+ .getElementById("client_hdviewport")
+ .addEventListener("click", () => switchAspectRatio());
+document
+ .getElementById("client_hdviewport_offset")
+ .addEventListener("click", () => switchChatOffset());
+document
+ .getElementById("client_disconnect")
+ .addEventListener("click", () => DisconnectButton());
+document
+ .getElementById("client_replaygo")
+ .addEventListener("click", onReplayGo);
+
+document
+ .getElementById("client_charactersearch")
+ .addEventListener("input", chartable_filter);
+document
+ .getElementById("client_musicsearch")
+ .addEventListener("input", musiclist_filter);
+
+document
+ .getElementById("client_musiclist")
+ .addEventListener("change", musiclist_click);
+document
+ .getElementById("role_select")
+ .addEventListener("change", () => changeRoleOOC());
+document
+ .getElementById("mute_select")
+ .addEventListener("change", mutelist_click);
+document
+ .getElementById("bg_select")
+ .addEventListener("change", () => updateBackgroundPreview());
+document
+ .getElementById("evi_select")
+ .addEventListener("change", () => updateEvidenceIcon());
+document
+ .getElementById("client_mvolume")
+ .addEventListener("change", () => changeMusicVolume());
+document
+ .getElementById("client_bvolume")
+ .addEventListener("change", () => changeBlipVolume());
+document
+ .getElementById("client_themeselect")
+ .addEventListener("change", () => reloadTheme());
+const chatboxselect = document.getElementById(
+ "client_chatboxselect",
+) as HTMLSelectElement;
+chatboxselect.addEventListener("change", () => setChatbox(chatboxselect.value));
+document
+ .getElementById("client_iniselect")
+ .addEventListener("change", () => updateIniswap());
+document
+ .getElementById("client_callwords")
+ .addEventListener("change", () => changeCallwords());
+
+document.addEventListener(
+ "error",
+ (e: Event) => {
+ const target = e.target;
+ if (!(target instanceof HTMLElement)) {
+ return;
+ }
+
+ switch (target.dataset.error) {
+ case "img":
+ imgError(target as HTMLImageElement);
+ break;
+ case "char":
+ charError(target as HTMLImageElement);
+ break;
+ case "opus-check":
+ opusCheck(target as HTMLAudioElement);
+ break;
+ default:
+ break;
+ }
+ },
+ true,
+);
+
+document
+ .getElementById("client_inputbox")
+ .addEventListener("keypress", onEnter);
+document
+ .getElementById("client_oocinputbox")
+ .addEventListener("keypress", onOOCEnter);
+
+document
+ .getElementById("client_sfxaudio")
+ .addEventListener("volumechange", changeSFXVolume);
+document
+ .getElementById("client_shoutaudio")
+ .addEventListener("volumechange", changeShoutVolume);
+document
+ .getElementById("client_testimonyaudio")
+ .addEventListener("volumechange", changeTestimonyVolume);
diff --git a/webAO/packets/handlers/handleSI.ts b/webAO/packets/handlers/handleSI.ts
index 369806b..c2a018e 100644
--- a/webAO/packets/handlers/handleSI.ts
+++ b/webAO/packets/handlers/handleSI.ts
@@ -22,9 +22,9 @@ export const handleSI = (args: string[]) => {
demothing.className = "demothing";
demothing.loading = "lazy";
demothing.id = `demo_${i}`;
- const demoonclick = document.createAttribute("onclick");
- demoonclick.value = `pickChar(${i})`;
- demothing.setAttributeNode(demoonclick);
+
+ demothing.dataset.action = "pick-char";
+ demothing.dataset.id = String(i);
document.getElementById("client_chartable")!.appendChild(demothing);
}