improve typing
This commit is contained in:
parent
094ae74b05
commit
13ca78419b
10 changed files with 79 additions and 65 deletions
|
@ -1,4 +1,4 @@
|
|||
import { f32ToRound10, isPlainObject, Song, SongMode } from "./common.ts";
|
||||
import { f32ToRound10, isPlainObject, Song, SongMode, StrongPartial } from "./common.ts";
|
||||
import elements from "./elements.ts";
|
||||
import { fromUrlData, setUrlData } from "./url/mod.ts";
|
||||
import { CodeEditor } from "./code-editor/mod.ts";
|
||||
|
@ -18,7 +18,7 @@ if (globalThis.loadLibrary !== false) {
|
|||
}
|
||||
|
||||
handleWindowResize(true);
|
||||
document.defaultView.addEventListener("resize", handleWindowResize);
|
||||
document.defaultView.addEventListener("resize", () => handleWindowResize(false));
|
||||
|
||||
let volume: number;
|
||||
let timeUnit: TimeUnit;
|
||||
|
@ -127,7 +127,6 @@ function handleMessage(e: MessageEvent<any>) {
|
|||
}
|
||||
}
|
||||
}
|
||||
let saveData = null;
|
||||
|
||||
function refreshCode() {
|
||||
if (audioWorklet) {
|
||||
|
@ -138,7 +137,7 @@ function handleWindowResize(force?: boolean) {
|
|||
autoSizeCanvas(force);
|
||||
}
|
||||
function autoSizeCanvas(force: boolean = false) {
|
||||
if (!elements.canvas.dataset.forcedWidth) {
|
||||
if (!elements.canvas.dataset["forcedWidth"]) {
|
||||
const innerWidth = window.innerWidth;
|
||||
// 768 is halfway between 512 and 1024, 3 added for outline
|
||||
if (innerWidth >= 768 + 3) {
|
||||
|
@ -179,7 +178,7 @@ function getSong(): Song {
|
|||
return { code: codeEditor.getCode(), ...songData };
|
||||
}
|
||||
|
||||
function setSong(songData?: Partial<Song>, play = true) {
|
||||
function setSong(songData?: StrongPartial<Song>, play = true) {
|
||||
let code, sampleRate, mode;
|
||||
if (songData) {
|
||||
({ code, sampleRate, mode } = songData);
|
||||
|
@ -193,7 +192,7 @@ function setSong(songData?: Partial<Song>, play = true) {
|
|||
togglePlay(true);
|
||||
}
|
||||
}
|
||||
function applyPlaybackMode(playbackMode) {
|
||||
function applyPlaybackMode(playbackMode: SongMode) {
|
||||
setPlaybackMode(playbackMode);
|
||||
elements.playbackMode.value = playbackMode;
|
||||
}
|
||||
|
@ -230,7 +229,7 @@ function resetTime() {
|
|||
elements.canvasTogglePlay.classList.add("canvas-toggleplay-show");
|
||||
}
|
||||
}
|
||||
function setByteSample(value, send = true, clear = false) {
|
||||
function setByteSample(value: number, send = true, clear = false) {
|
||||
if (audioWorklet && isFinite(value)) {
|
||||
byteSample = value;
|
||||
updateCounterValue();
|
||||
|
@ -240,7 +239,7 @@ function setByteSample(value, send = true, clear = false) {
|
|||
osc.moveTimeCursor(byteSample, playSpeed > 0);
|
||||
}
|
||||
}
|
||||
function setPlaybackMode(playbackMode) {
|
||||
function setPlaybackMode(playbackMode: SongMode) {
|
||||
if (audioWorklet) {
|
||||
songData.mode = playbackMode;
|
||||
if (globalThis.useUrlData !== false) {
|
||||
|
@ -252,7 +251,8 @@ function setPlaybackMode(playbackMode) {
|
|||
function setSampleRate(sampleRate: number) {
|
||||
if (audioWorklet) {
|
||||
const rate = Math.min(Math.max(Math.fround(sampleRate), 2), 2 ** 24 - 1);
|
||||
elements.sampleRate.value = f32ToRound10(rate);
|
||||
// implicit cast
|
||||
elements.sampleRate.value = f32ToRound10(rate) as unknown as string;
|
||||
songData.sampleRate = rate;
|
||||
// TODO if funcbeat change time so that sec stays the same
|
||||
audioWorklet.port.postMessage({ songData, updateSampleRatio: true });
|
||||
|
@ -290,9 +290,10 @@ function timeCursorShouldBeVisible(): boolean {
|
|||
}
|
||||
|
||||
function updateCounterValue() {
|
||||
elements.timeValue.placeholder = convertToUnit(byteSample);
|
||||
// implicit cast
|
||||
elements.timeValue.placeholder = convertToUnit(byteSample) as unknown as string;
|
||||
}
|
||||
function convertFromUnit(value, unit = timeUnit) {
|
||||
function convertFromUnit(value: number, unit = timeUnit) {
|
||||
switch (unit) {
|
||||
case "t":
|
||||
return value;
|
||||
|
@ -309,7 +310,7 @@ function convertToUnit(value: number, unit = timeUnit) {
|
|||
return (value / songData.sampleRate).toFixed(3);
|
||||
}
|
||||
}
|
||||
function setTimeUnit(value: number, userInput = true) {
|
||||
function setTimeUnit(value?: number | TimeUnit, userInput = true) {
|
||||
if (value !== undefined) {
|
||||
if (typeof value === "number") {
|
||||
timeUnit = timeUnits[value];
|
||||
|
@ -352,7 +353,8 @@ function changeScale(amount: number) {
|
|||
function setVolume(save = true, value?: number) {
|
||||
if (value !== undefined) {
|
||||
volume = value;
|
||||
elements.volume.value = volume;
|
||||
// implicit cast
|
||||
elements.volume.value = volume as unknown as string;
|
||||
}
|
||||
volume = elements.volume.valueAsNumber;
|
||||
|
||||
|
@ -373,22 +375,23 @@ function loadDefaultSettings() {
|
|||
setTimeUnit(undefined, false);
|
||||
}
|
||||
function loadSettings(): Settings {
|
||||
if (localStorage.settings && globalThis.useLocalStorage !== false) {
|
||||
if (globalThis.useLocalStorage !== false && localStorage["settings"]) {
|
||||
let settings: {
|
||||
drawSettings: typeof osc.settings;
|
||||
volume: typeof volume;
|
||||
timeUnit: typeof timeUnit;
|
||||
drawSettings?: typeof osc.settings;
|
||||
volume?: typeof volume;
|
||||
timeUnit?: typeof timeUnit;
|
||||
};
|
||||
try {
|
||||
settings = JSON.parse(localStorage.settings);
|
||||
// FIXME: this should be validated
|
||||
settings = JSON.parse(localStorage["settings"]);
|
||||
} catch (err) {
|
||||
console.error("Couldn't load settings!", localStorage.settings);
|
||||
console.error("Couldn't load settings!", localStorage["settings"]);
|
||||
localStorage.clear();
|
||||
loadDefaultSettings();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Object.hasOwnProperty.call(settings, "drawSettings")) {
|
||||
if (settings.drawSettings) {
|
||||
osc.settings = settings.drawSettings;
|
||||
} else {
|
||||
osc.settings.scale = 5;
|
||||
|
@ -396,7 +399,7 @@ function loadSettings(): Settings {
|
|||
|
||||
setVolume(false, settings.volume);
|
||||
|
||||
if (Object.hasOwnProperty.call(settings, "timeUnit")) {
|
||||
if (settings.timeUnit) {
|
||||
setTimeUnit(settings.timeUnit, false);
|
||||
} else {
|
||||
setTimeUnit(undefined, false);
|
||||
|
@ -407,7 +410,7 @@ function loadSettings(): Settings {
|
|||
}
|
||||
function saveSettings() {
|
||||
if (globalThis.useLocalStorage !== false) {
|
||||
localStorage.settings = JSON.stringify({
|
||||
localStorage["settings"] = JSON.stringify({
|
||||
drawSettings: osc.settings,
|
||||
volume,
|
||||
timeUnit,
|
||||
|
|
|
@ -18,6 +18,9 @@ class CodeEditor {
|
|||
const codemirror = c.replace(editCallback, textarea);
|
||||
this.getCode = () => codemirror.state.doc.toString();
|
||||
this.setCode = v =>
|
||||
// codemirror type is wrong
|
||||
// TODO pull request fix
|
||||
// @ts-ignore
|
||||
codemirror.dispatch({
|
||||
changes: { from: 0, to: codemirror.state.doc.length, insert: v },
|
||||
});
|
||||
|
|
|
@ -19,8 +19,8 @@ function initTextarea(editCallback: () => void, textarea: HTMLTextAreaElement) {
|
|||
let line = 0;
|
||||
for (let c = 0; ; line++) {
|
||||
c += lines[line].length;
|
||||
if (c > char) 1;
|
||||
break;
|
||||
if (c > char)
|
||||
break;
|
||||
}
|
||||
return line;
|
||||
};
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
type StrongPartial<T> = { [P in keyof T]?: T[P] | undefined };
|
||||
|
||||
function f32ToRound10(value: number): number {
|
||||
// this is stupid but it works for numbers less
|
||||
// than 10 digits on one side of the decimal point
|
||||
|
@ -33,4 +35,4 @@ type Song = {
|
|||
mode: SongMode;
|
||||
};
|
||||
|
||||
export { f32ToRound10, isPlainObject, SmallSongMode, SongMode, Song };
|
||||
export { StrongPartial, f32ToRound10, isPlainObject, SmallSongMode, SongMode, Song };
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
export default {
|
||||
main: document.getElementsByTagName("main")[0],
|
||||
error: document.getElementById("error")!,
|
||||
canvas: document.getElementById("canvas-main")!, // this is the only shared element
|
||||
canvas: document.getElementById("canvas-main") as HTMLCanvasElement, // this is the only shared element
|
||||
timeCursor: document.getElementById("canvas-timecursor")!,
|
||||
|
||||
timeUnit: document.getElementById("control-time-unit")!,
|
||||
timeUnitLabel: document.getElementById("control-time-unit-label")!,
|
||||
timeValue: document.getElementById("control-time-value")!,
|
||||
timeValue: document.getElementById("control-time-value") as HTMLInputElement,
|
||||
|
||||
scaleUp: document.getElementById("control-scaleup")!,
|
||||
scaleDown: document.getElementById("control-scaledown")!,
|
||||
scaleUp: document.getElementById("control-scaleup") as HTMLButtonElement,
|
||||
scaleDown: document.getElementById("control-scaledown") as HTMLButtonElement,
|
||||
|
||||
playbackMode: document.getElementById("control-song-mode")!,
|
||||
sampleRate: document.getElementById("control-sample-rate")!,
|
||||
volume: document.getElementById("control-volume")!,
|
||||
playbackMode: document.getElementById("control-song-mode") as HTMLSelectElement,
|
||||
sampleRate: document.getElementById("control-sample-rate") as HTMLInputElement,
|
||||
volume: document.getElementById("control-volume") as HTMLInputElement,
|
||||
|
||||
canvasTogglePlay: document.getElementById("canvas-toggleplay")!,
|
||||
|
||||
|
|
30
src/embed.ts
30
src/embed.ts
|
@ -7,27 +7,27 @@ const nameids: [string, (show: boolean) => void, ...string[]][] = [
|
|||
[
|
||||
"timeControls",
|
||||
show => {
|
||||
elements.controls.dataset.timeControlsDisabled = !show as unknown as string;
|
||||
elements.canvasContainer.dataset.disabled = !show as unknown as string;
|
||||
elements.controls.dataset["timeControlsDisabled"] = !show as unknown as string;
|
||||
elements.canvasContainer.dataset["disabled"] = !show as unknown as string;
|
||||
},
|
||||
"canvas-toggleplay",
|
||||
],
|
||||
[
|
||||
"playbackControls",
|
||||
show => {
|
||||
elements.controls.dataset.playbackControlsDisabled = !show as unknown as string;
|
||||
elements.controls.dataset["playbackControlsDisabled"] = !show as unknown as string;
|
||||
},
|
||||
],
|
||||
[
|
||||
"viewControls",
|
||||
show => {
|
||||
elements.controls.dataset.viewControlsDisabled = !show as unknown as string;
|
||||
elements.controls.dataset["viewControlsDisabled"] = !show as unknown as string;
|
||||
},
|
||||
],
|
||||
[
|
||||
"songControls",
|
||||
show => {
|
||||
elements.controls.dataset.songControlsDisabled = !show as unknown as string;
|
||||
elements.controls.dataset["songControlsDisabled"] = !show as unknown as string;
|
||||
},
|
||||
],
|
||||
["error", () => {}, "error"],
|
||||
|
@ -41,9 +41,9 @@ window.addEventListener(
|
|||
if (isPlainObject(e.data)) {
|
||||
const data: { [index: string]: any } = e.data;
|
||||
// show/hide elements
|
||||
if (isPlainObject(data.show)) {
|
||||
if (isPlainObject(data["show"])) {
|
||||
for (const [name, fn, ...ids] of nameids) {
|
||||
const show: { [index: string]: any } = data.show;
|
||||
const show: { [index: string]: any } = data["show"];
|
||||
if (show[name] !== undefined) {
|
||||
if (show[name]) {
|
||||
fn(true);
|
||||
|
@ -60,21 +60,21 @@ window.addEventListener(
|
|||
}
|
||||
}
|
||||
|
||||
if (data.forceScopeWidth !== undefined && elements.canvas) {
|
||||
if (typeof data.forceScopeWidth === "number") {
|
||||
elements.canvas.dataset.forcedWidth = true as unknown as string;
|
||||
setCanvasWidth(data.forceScopeWidth);
|
||||
if (data["forceScopeWidth"] !== undefined && elements.canvas) {
|
||||
if (typeof data["forceScopeWidth"] === "number") {
|
||||
elements.canvas.dataset["forcedWidth"] = true as unknown as string;
|
||||
setCanvasWidth(data["forceScopeWidth"]);
|
||||
} else {
|
||||
delete elements.canvas.dataset.forcedWidth;
|
||||
delete elements.canvas.dataset["forcedWidth"];
|
||||
autoSizeCanvas();
|
||||
}
|
||||
}
|
||||
|
||||
if (data.getSong) {
|
||||
if (data["getSong"]) {
|
||||
window.parent.postMessage({ song: getSong() }, "*");
|
||||
}
|
||||
if (isPlainObject(data.setSong)) {
|
||||
setSong(data.setSong, false);
|
||||
if (isPlainObject(data["setSong"])) {
|
||||
setSong(data["setSong"], false);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -53,11 +53,11 @@ function createEntryElem(entry: Entry) {
|
|||
) as HTMLElement[];
|
||||
if (songElems.length) {
|
||||
for (const elem of songElems) {
|
||||
const songData = elem.dataset.songData ? JSON.parse(elem.dataset.songData) : {};
|
||||
const songData = elem.dataset["songData"] ? JSON.parse(elem.dataset["songData"]) : {};
|
||||
|
||||
const onclick = elem.dataset.hasOwnProperty("codeFile")
|
||||
? () =>
|
||||
fetch(`/library/${elem.dataset.codeFile}`)
|
||||
fetch(`/library/${elem.dataset["codeFile"]}`)
|
||||
.then(response => response.text())
|
||||
.then(code => setSong(Object.assign(songData, { code })))
|
||||
: () => setSong(Object.assign({ code: elem.innerText }, songData));
|
||||
|
@ -233,9 +233,8 @@ function createEntryElem(entry: Entry) {
|
|||
|
||||
if (entry.children) {
|
||||
let childrenElem = document.createElement("ul");
|
||||
for (let i = 0, len = entry.children.length; i < len; ++i) {
|
||||
let childEntry = entry.children[i];
|
||||
childrenElem.append(createEntryElem(childEntry));
|
||||
for (const child of entry.children) {
|
||||
childrenElem.append(createEntryElem(child));
|
||||
}
|
||||
entryElem.append(childrenElem);
|
||||
}
|
||||
|
|
|
@ -28,10 +28,14 @@ function into89(data: Uint8Array, bitIndex = 0): string {
|
|||
while (true) {
|
||||
//const byteIndex = bitIndex >> 3;
|
||||
//const bitOffset = bitIndex & 7;
|
||||
let bigint = 0n;
|
||||
// collects 123 bits of data
|
||||
let chunkInt = 0n;
|
||||
for (let i = 0; i < 123; i++) {
|
||||
if (bitIndex >> 3 < data.length) {
|
||||
bigint += BigInt(!!(data[bitIndex >> 3] & (1 << (bitIndex & 7)))) << BigInt(i);
|
||||
// if bitIndex is negative, this will implicitly cast
|
||||
const currentByte = data[bitIndex >> 3] as number;
|
||||
const bitOfByte = 1 << (bitIndex & 7);
|
||||
chunkInt += BigInt(!!(currentByte & bitOfByte)) << BigInt(i);
|
||||
bitIndex++;
|
||||
} else {
|
||||
// length kept being wrong, i gave up on it
|
||||
|
@ -45,17 +49,17 @@ function into89(data: Uint8Array, bitIndex = 0): string {
|
|||
// bigint /= 89n;
|
||||
//}
|
||||
//// unreachable
|
||||
while (bigint) {
|
||||
coded += tochar[Number(bigint % 89n)];
|
||||
bigint /= 89n;
|
||||
while (chunkInt) {
|
||||
coded += tochar[Number(chunkInt % 89n)];
|
||||
chunkInt /= 89n;
|
||||
}
|
||||
return coded;
|
||||
}
|
||||
}
|
||||
//bigint = BigInt.asUintN(123, bigint << BigInt(bitOffset));
|
||||
for (let j = 0; j < 19; j++) {
|
||||
coded += tochar[Number(bigint % 89n)];
|
||||
bigint /= 89n;
|
||||
coded += tochar[Number(chunkInt % 89n)];
|
||||
chunkInt /= 89n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,13 +86,13 @@ function from89(coded: string, bitIndex = 0 /*, byteUp = true*/): Uint8Array {
|
|||
if (coded.length) {
|
||||
const chunks = coded.match(/.{1,19}/g) as RegExpMatchArray;
|
||||
for (const chunk of chunks) {
|
||||
let bigint = 0n;
|
||||
let chunkInt = 0n;
|
||||
for (const letter of chunk.split("").reverse()) {
|
||||
bigint *= 89n;
|
||||
bigint += fromchar[letter];
|
||||
chunkInt *= 89n;
|
||||
chunkInt += fromchar[letter];
|
||||
}
|
||||
for (let i = 123; i; i--) {
|
||||
let bit = Number(bigint & 1n);
|
||||
const bit = Number(chunkInt & 1n);
|
||||
currentByte |= bit << (bitIndex & 7);
|
||||
if ((bitIndex & 7) === 7) {
|
||||
data[bitIndex >> 3] = currentByte;
|
||||
|
@ -101,7 +105,7 @@ function from89(coded: string, bitIndex = 0 /*, byteUp = true*/): Uint8Array {
|
|||
}
|
||||
return data;
|
||||
}
|
||||
bigint >>= 1n;
|
||||
chunkInt >>= 1n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ async function fromUrlData(hash: string): Promise<Song | undefined> {
|
|||
let compressedCode;
|
||||
if (v === "6") {
|
||||
data = from89(hash.substring(2), 12);
|
||||
// if data[1] is undefined the implicit cast to 0 is fine
|
||||
data[1] = (data[1] >> 4) | 0x40;
|
||||
compressedCode = data.subarray(6);
|
||||
} else {
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
"strict": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
// as far as i can tell there is no way to define a type that indicates a
|
||||
// valid index for some value. this makes this pretty much impossible to use
|
||||
//"noUncheckedIndexedAccess": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"types": ["jest"]
|
||||
|
|
Loading…
Reference in a new issue