mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-05-17 05:14:49 -07:00
kibo: part 1
This commit is contained in:
88
Cargo.lock
generated
88
Cargo.lock
generated
@@ -639,9 +639,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.16"
|
||||
version = "1.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
|
||||
checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@@ -1789,9 +1789,9 @@ checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564"
|
||||
|
||||
[[package]]
|
||||
name = "oxc"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e398ac9650c77d6a43e7f5ed5315c3cae33fd1f84666acd0a55719c8da1555b3"
|
||||
checksum = "d6f2d8bbd880aaaf838456ce101c59d926a762b6a891ef91402794e9dc8d2c2c"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
@@ -1832,9 +1832,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxc_allocator"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36dd2ba553043fd1cf1f92fb4d685a9a58afcc4f2692e85ebf84e242e6492909"
|
||||
checksum = "fe07aea78e1e1a860d92cfe1b712f81ba10960dee847c6231fa4e9b0665ec5ff"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"bumpalo",
|
||||
@@ -1846,9 +1846,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxc_ast"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd0607fb00e5f2413b5a99b36eff638b7db57e69149e98ac693d2aaa500d1d26"
|
||||
checksum = "062ec80f1ed9471bc05f57bd481bd4921285373b57018f3028aed49cb3ac353f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cow-utils",
|
||||
@@ -1863,9 +1863,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxc_ast_macros"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf0d4b6faf22509c64f78d4a0a0bb760f6ba34fe7acdcb421b57fdc32482867e"
|
||||
checksum = "41e79130c01eaddff0274d504404f80d88835ed70fcc1e91f9c9fd42fd718202"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1874,9 +1874,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxc_ast_visit"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "421e72f280ed323f63ad7ca5e4700577ca51f7946fdc9868baeb7e23eeb1c6b1"
|
||||
checksum = "1c0476717291544c614de9ffc1c34c29b06025008c2e604505e67248234725ca"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
@@ -1886,9 +1886,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxc_cfg"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c213a97298278d4f9a7e3a4e6bce0c5eba416aed5b291a6296bbb8c26ca1e65"
|
||||
checksum = "243535bf553c8d399f20a392eabb6eff5990818eac8519a930a1c345497a8ea7"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"itertools",
|
||||
@@ -1901,9 +1901,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxc_codegen"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a61b0ca6f9d8e9fb6a5a5390ecae8bbd98283cfb38d24ce77979b77ab28fc65"
|
||||
checksum = "4ebcbed8d477c4b9142c895a762be0afd16bd0838f64237e2c006fd9f8ec7e1a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cow-utils",
|
||||
@@ -1922,15 +1922,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxc_data_structures"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a47c43341de573281bc1883b4cdb36dd9c8c11e56a7d6fda0b8335471add52d2"
|
||||
checksum = "e4c79e0d91ca11d4add13d94f802096564babb2c609956a8e19eb6f83b7f0fb1"
|
||||
|
||||
[[package]]
|
||||
name = "oxc_diagnostics"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f5ce8bc7ebc0fa2c2bd94d526a92636e8622f879a9dd9a41b6c77c12a2b2408"
|
||||
checksum = "b127d339db14984b22bf4255a2583c1be2cd709b1b14f64e3ce4cb5fe87c699d"
|
||||
dependencies = [
|
||||
"cow-utils",
|
||||
"oxc-miette",
|
||||
@@ -1938,9 +1938,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxc_ecmascript"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ce89c6c2764fa6ad1f929a91e09614943fe7a25df1d288d38acae0302581b8f"
|
||||
checksum = "522b7c4d6db500536be627e1d3952cf26705328e77a4d819a2543c2392b702eb"
|
||||
dependencies = [
|
||||
"cow-utils",
|
||||
"num-bigint",
|
||||
@@ -1952,9 +1952,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxc_estree"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a4f605c6f6460d2cb659bb1c2273244ebf8c07bae4155274fa2461d7e0ec35"
|
||||
checksum = "7190d1db8c149324345b14588f24a318712498fde1741513c3a129731ef6b4f9"
|
||||
|
||||
[[package]]
|
||||
name = "oxc_index"
|
||||
@@ -1964,9 +1964,9 @@ checksum = "2fa07b0cfa997730afed43705766ef27792873fdf5215b1391949fec678d2392"
|
||||
|
||||
[[package]]
|
||||
name = "oxc_mangler"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73d8be639f09e7c62e4503a5cbe3a802d78265c490b09ebaa1fb905d5b9d8bb0"
|
||||
checksum = "cddde40dd8422c56c07eecd370d5c221626afb5cb5966824c1884a8b929305cb"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"itertools",
|
||||
@@ -1981,9 +1981,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxc_minifier"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea781d79be82fd4e1cd94fe6a6da4517edf075cc604d52f9785a0153157736d8"
|
||||
checksum = "513dd618e9016cb00cc52e547bcbdb160a0379025206f8cce14287c1c624b57a"
|
||||
dependencies = [
|
||||
"cow-utils",
|
||||
"oxc_allocator",
|
||||
@@ -2003,9 +2003,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxc_parser"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b633fe51f19c4da6f3cd6fd0885e3a9e12f19317487a477fea0f73892713083"
|
||||
checksum = "afda2c4a47704ff4c68990248be9f693f606626cff1c6d1760034638b54c5413"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cow-utils",
|
||||
@@ -2026,9 +2026,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxc_regular_expression"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b35b1a9babb738e4d99cfc0ef8ad242806d261de028b400a3205afdf7a9a1c03"
|
||||
checksum = "a4354d5b5f48d53cc0d4d000425ec062b12e9b3fbcf395e765064c8eab113921"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast_macros",
|
||||
@@ -2042,9 +2042,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxc_semantic"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78fcfacfa8a2bb020326c37011b86f423c41e776516c197e6c162ac85b6a1e7a"
|
||||
checksum = "e1346102f3550e6b0417fe84f8fb397f976c351e21bcb98b75ff018cc3877bd3"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"oxc_allocator",
|
||||
@@ -2078,9 +2078,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxc_span"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c2940d9a7446ddbe554bf0aa3cd111e6bf7c2dd29460da6673cde9b1c7be77f"
|
||||
checksum = "77b073cb1349f33e04d821e4fd1e51c860a010d74fea0b4660504cb05a87968a"
|
||||
dependencies = [
|
||||
"compact_str",
|
||||
"oxc-miette",
|
||||
@@ -2091,9 +2091,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxc_syntax"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd9d42c1d620a1da919ec6b56c4476054d4d2c71423c08effbc3a0519c516b61"
|
||||
checksum = "488a2404fca5b741255b8b875e8f6515f5b08df6046a0767b2368d6182e055cb"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cow-utils",
|
||||
@@ -2112,9 +2112,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxc_traverse"
|
||||
version = "0.61.0"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f389b5904bc2294fe53ee6b0f6b3b15e395b71079168d8e8204626c4aede3c32"
|
||||
checksum = "02102521f55df8330e9393d425e746e21d9badc5a78650769e645db40102f3c0"
|
||||
dependencies = [
|
||||
"compact_str",
|
||||
"itoa",
|
||||
@@ -3473,18 +3473,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "7.2.3"
|
||||
version = "7.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3051792fbdc2e1e143244dc28c60f73d8470e93f3f9cbd0ead44da5ed802722"
|
||||
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
|
||||
dependencies = [
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.14+zstd.1.5.7"
|
||||
version = "2.0.15+zstd.1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fb060d4926e4ac3a3ad15d864e99ceb5f343c6b34f5bd6d81ae6ed417311be5"
|
||||
checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
|
||||
@@ -31,7 +31,7 @@ pub use stores::*;
|
||||
pub use vecs::*;
|
||||
|
||||
const SNAPSHOT_BLOCK_RANGE: usize = 1000;
|
||||
const COLLISIONS_CHECKED_UP_TO: u32 = 870_000;
|
||||
const COLLISIONS_CHECKED_UP_TO: u32 = 888_000;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Indexer {
|
||||
|
||||
@@ -162,17 +162,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// impl<I, T> Deref for StorableVec<I, T> {
|
||||
// type Target = brk_vec::StorableVec<I, T>;
|
||||
// fn deref(&self) -> &Self::Target {
|
||||
// &self.vec
|
||||
// }
|
||||
// }
|
||||
// impl<I, T> DerefMut for StorableVec<I, T> {
|
||||
// fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
// &mut self.vec
|
||||
// }
|
||||
// }
|
||||
impl<I, T> Clone for StorableVec<I, T>
|
||||
where
|
||||
I: StoredIndex,
|
||||
|
||||
@@ -21,7 +21,7 @@ color-eyre = { workspace = true }
|
||||
jiff = { workspace = true }
|
||||
log = { workspace = true }
|
||||
minreq = { workspace = true }
|
||||
oxc = { version = "0.61.0", features = ["codegen", "minifier"] }
|
||||
oxc = { version = "0.61.1", features = ["codegen", "minifier"] }
|
||||
serde = { workspace = true }
|
||||
tokio = { version = "1.44.1", features = ["full"] }
|
||||
tower-http = { version = "0.6.2", features = ["compression-full"] }
|
||||
|
||||
@@ -1518,7 +1518,6 @@
|
||||
"
|
||||
>希望</small
|
||||
>
|
||||
希望.お金
|
||||
</a>
|
||||
<small style="display: block">
|
||||
<strong>Bitcoin</strong> is
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @import { DeepPartial, ChartOptions, IChartApi, IHorzScaleBehavior, WhitespaceData, SingleValueData, ISeriesApi, Time, LogicalRange, SeriesType, BaselineStyleOptions, SeriesOptionsCommon, createChart as CreateClassicChart, createChartEx as CreateCustomChart } from "./v4.2.2/types"
|
||||
*/
|
||||
|
||||
export default import("./v4.2.2/script.js").then((lightweightCharts) => {
|
||||
const ids = {
|
||||
from: "from",
|
||||
to: "to",
|
||||
@@ -16,7 +17,6 @@ const ids = {
|
||||
},
|
||||
};
|
||||
|
||||
export default import("./v4.2.2/script.js").then((lightweightCharts) => {
|
||||
const createClassicChart = /** @type {CreateClassicChart} */ (
|
||||
lightweightCharts.createChart
|
||||
);
|
||||
@@ -357,6 +357,7 @@ export default import("./v4.2.2/script.js").then((lightweightCharts) => {
|
||||
* @param {TimeScale} param0.scale
|
||||
* @param {"static" | "moveable"} param0.kind
|
||||
* @param {Utilities} param0.utils
|
||||
* @param {Constants} param0.consts
|
||||
* @param {Owner | null} [param0.owner]
|
||||
* @param {CreatePaneParameters[]} [param0.config]
|
||||
*/
|
||||
@@ -368,6 +369,7 @@ export default import("./v4.2.2/script.js").then((lightweightCharts) => {
|
||||
kind,
|
||||
scale,
|
||||
config,
|
||||
consts,
|
||||
utils,
|
||||
owner: _owner,
|
||||
}) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* @param {Signals} args.signals
|
||||
* @param {Utilities} args.utils
|
||||
* @param {Datasets} args.datasets
|
||||
* @param {Constants} args.consts
|
||||
* @param {WebSockets} args.webSockets
|
||||
* @param {Elements} args.elements
|
||||
*/
|
||||
@@ -19,6 +20,7 @@ export function init({
|
||||
selected,
|
||||
signals,
|
||||
utils,
|
||||
consts,
|
||||
webSockets,
|
||||
}) {
|
||||
console.log("init chart state");
|
||||
@@ -43,6 +45,7 @@ export function init({
|
||||
id: "chart",
|
||||
scale: scale(),
|
||||
kind: "moveable",
|
||||
consts,
|
||||
utils,
|
||||
});
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {Colors} args.colors
|
||||
* @param {Consts} args.consts
|
||||
* @param {Constants} args.consts
|
||||
* @param {LightweightCharts} args.lightweightCharts
|
||||
* @param {Signals} args.signals
|
||||
* @param {Utilities} args.utils
|
||||
|
||||
@@ -68,34 +68,35 @@ function initPackages() {
|
||||
ufuzzy: importPackage("ufuzzy"),
|
||||
};
|
||||
}
|
||||
const packages = initPackages();
|
||||
/**
|
||||
* @typedef {Awaited<ReturnType<typeof packages.lightweightCharts>>} LightweightCharts
|
||||
* @typedef {ReturnType<typeof initPackages>} Packages
|
||||
* @typedef {Awaited<ReturnType<Packages["lightweightCharts"]>>} LightweightCharts
|
||||
* @typedef {ReturnType<LightweightCharts['createChart']>} Chart
|
||||
*/
|
||||
|
||||
const options = import("./options.js");
|
||||
|
||||
const utils = {
|
||||
function createUtils() {
|
||||
/**
|
||||
* @param {string} serialized
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isSerializedBooleanTrue(serialized) {
|
||||
function isSerializedBooleanTrue(serialized) {
|
||||
return serialized === "true" || serialized === "1";
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} ms
|
||||
*/
|
||||
sleep(ms) {
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
},
|
||||
yield() {
|
||||
return this.sleep(0);
|
||||
},
|
||||
array: {
|
||||
}
|
||||
|
||||
function next() {
|
||||
return sleep(0);
|
||||
}
|
||||
|
||||
const array = {
|
||||
/**
|
||||
* @param {number} start
|
||||
* @param {number} end
|
||||
@@ -108,8 +109,9 @@ const utils = {
|
||||
}
|
||||
return range;
|
||||
},
|
||||
},
|
||||
dom: {
|
||||
};
|
||||
|
||||
const dom = {
|
||||
/**
|
||||
* @param {string} id
|
||||
* @returns {HTMLElement}
|
||||
@@ -121,8 +123,9 @@ const utils = {
|
||||
},
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {Elements} elements
|
||||
*/
|
||||
queryOrCreateMetaElement(name) {
|
||||
queryOrCreateMetaElement(name, elements) {
|
||||
let meta = /** @type {HTMLMetaElement | null} */ (
|
||||
window.document.querySelector(`meta[name="${name}"]`)
|
||||
);
|
||||
@@ -289,9 +292,10 @@ const utils = {
|
||||
},
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {Elements} elements
|
||||
* @param {boolean} [targetBlank]
|
||||
*/
|
||||
open(url, targetBlank) {
|
||||
open(url, elements, targetBlank) {
|
||||
console.log(`open: ${url}`);
|
||||
const a = window.document.createElement("a");
|
||||
elements.body.append(a);
|
||||
@@ -322,7 +326,8 @@ const utils = {
|
||||
link.type = "text/css";
|
||||
link.rel = "stylesheet";
|
||||
link.media = "screen,print";
|
||||
elements.head.appendChild(link);
|
||||
const head = window.document.getElementsByTagName("head")[0];
|
||||
head.appendChild(link);
|
||||
return link;
|
||||
},
|
||||
/**
|
||||
@@ -362,7 +367,7 @@ const utils = {
|
||||
|
||||
choices.forEach((choice) => {
|
||||
const inputValue = choice.toLowerCase();
|
||||
const { label } = utils.dom.createLabeledInput({
|
||||
const { label } = this.createLabeledInput({
|
||||
inputId: `${id}-${choice.toLowerCase()}`,
|
||||
inputName: id,
|
||||
inputValue,
|
||||
@@ -476,7 +481,7 @@ const utils = {
|
||||
const min = "2011-01-01";
|
||||
const minDate = new Date(min);
|
||||
const maxDate = new Date();
|
||||
const max = utils.date.toString(maxDate);
|
||||
const max = date.toString(maxDate);
|
||||
input.min = min;
|
||||
input.max = max;
|
||||
|
||||
@@ -484,8 +489,8 @@ const utils = {
|
||||
|
||||
signals.createEffect(
|
||||
() => {
|
||||
const date = signal();
|
||||
return date ? utils.date.toString(date) : "";
|
||||
const dateSignal = signal();
|
||||
return dateSignal ? date.toString(dateSignal) : "";
|
||||
},
|
||||
(value) => {
|
||||
if (stateValue !== value) {
|
||||
@@ -663,8 +668,9 @@ const utils = {
|
||||
div.classList.add(`shadow-${position}`);
|
||||
return div;
|
||||
},
|
||||
},
|
||||
url: {
|
||||
};
|
||||
|
||||
const url = {
|
||||
chartParamsWhitelist: ["from", "to"],
|
||||
/**
|
||||
* @param {string} pathname
|
||||
@@ -736,7 +742,7 @@ const utils = {
|
||||
const parameter = this.readParam(key);
|
||||
|
||||
if (parameter) {
|
||||
return utils.isSerializedBooleanTrue(parameter);
|
||||
return isSerializedBooleanTrue(parameter);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -767,20 +773,23 @@ const utils = {
|
||||
pathnameToSelectedId() {
|
||||
return window.document.location.pathname.substring(1);
|
||||
},
|
||||
},
|
||||
locale: {
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {number} [digits]
|
||||
* @param {Intl.NumberFormatOptions} [options]
|
||||
*/
|
||||
numberToUSFormat(value, digits, options) {
|
||||
function numberToUSFormat(value, digits, options) {
|
||||
return value.toLocaleString("en-us", {
|
||||
...options,
|
||||
minimumFractionDigits: digits,
|
||||
maximumFractionDigits: digits,
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
const locale = {
|
||||
numberToUSFormat,
|
||||
/** @param {number} value */
|
||||
numberToShortUSFormat(value) {
|
||||
const absoluteValue = Math.abs(value);
|
||||
@@ -788,15 +797,15 @@ const utils = {
|
||||
if (isNaN(value)) {
|
||||
return "";
|
||||
} else if (absoluteValue < 10) {
|
||||
return utils.locale.numberToUSFormat(value, 3);
|
||||
return numberToUSFormat(value, 3);
|
||||
} else if (absoluteValue < 100) {
|
||||
return utils.locale.numberToUSFormat(value, 2);
|
||||
return numberToUSFormat(value, 2);
|
||||
} else if (absoluteValue < 1_000) {
|
||||
return utils.locale.numberToUSFormat(value, 1);
|
||||
return numberToUSFormat(value, 1);
|
||||
} else if (absoluteValue < 100_000) {
|
||||
return utils.locale.numberToUSFormat(value, 0);
|
||||
return numberToUSFormat(value, 0);
|
||||
} else if (absoluteValue < 1_000_000) {
|
||||
return `${utils.locale.numberToUSFormat(value / 1_000, 1)}K`;
|
||||
return `${numberToUSFormat(value / 1_000, 1)}K`;
|
||||
} else if (absoluteValue >= 9_000_000_000_000_000) {
|
||||
return "Inf.";
|
||||
}
|
||||
@@ -810,24 +819,25 @@ const utils = {
|
||||
const modulused = log % 3;
|
||||
|
||||
if (modulused === 0) {
|
||||
return `${utils.locale.numberToUSFormat(
|
||||
return `${numberToUSFormat(
|
||||
value / (1_000_000 * 1_000 ** letterIndex),
|
||||
3,
|
||||
)}${letter}`;
|
||||
} else if (modulused === 1) {
|
||||
return `${utils.locale.numberToUSFormat(
|
||||
return `${numberToUSFormat(
|
||||
value / (1_000_000 * 1_000 ** letterIndex),
|
||||
2,
|
||||
)}${letter}`;
|
||||
} else {
|
||||
return `${utils.locale.numberToUSFormat(
|
||||
return `${numberToUSFormat(
|
||||
value / (1_000_000 * 1_000 ** letterIndex),
|
||||
1,
|
||||
)}${letter}`;
|
||||
}
|
||||
},
|
||||
},
|
||||
storage: {
|
||||
};
|
||||
|
||||
const storage = {
|
||||
/**
|
||||
* @param {string} key
|
||||
*/
|
||||
@@ -844,7 +854,7 @@ const utils = {
|
||||
readBool(key) {
|
||||
const saved = this.read(key);
|
||||
if (saved) {
|
||||
return utils.isSerializedBooleanTrue(saved);
|
||||
return isSerializedBooleanTrue(saved);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
@@ -869,8 +879,9 @@ const utils = {
|
||||
remove(key) {
|
||||
this.write(key, undefined);
|
||||
},
|
||||
},
|
||||
serde: {
|
||||
};
|
||||
|
||||
const serde = {
|
||||
number: {
|
||||
/**
|
||||
* @param {number} v
|
||||
@@ -890,7 +901,7 @@ const utils = {
|
||||
* @param {Date} v
|
||||
*/
|
||||
serialize(v) {
|
||||
return utils.date.toString(v);
|
||||
return date.toString(v);
|
||||
},
|
||||
/**
|
||||
* @param {string} v
|
||||
@@ -919,8 +930,9 @@ const utils = {
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
formatters: {
|
||||
};
|
||||
|
||||
const formatters = {
|
||||
dollars: new Intl.NumberFormat("en-US", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
@@ -932,8 +944,9 @@ const utils = {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}),
|
||||
},
|
||||
date: {
|
||||
};
|
||||
|
||||
const date = {
|
||||
ONE_DAY_IN_MS: 1000 * 60 * 60 * 24,
|
||||
todayUTC() {
|
||||
const today = new Date();
|
||||
@@ -991,8 +1004,9 @@ const utils = {
|
||||
differenceBetween(date1, date2) {
|
||||
return Math.abs(date1.valueOf() - date2.valueOf()) / this.ONE_DAY_IN_MS;
|
||||
},
|
||||
},
|
||||
color: {
|
||||
};
|
||||
|
||||
const color = {
|
||||
/**
|
||||
*
|
||||
* @param {readonly [number, number, number, number, number, number, number, number, number]} A
|
||||
@@ -1086,14 +1100,15 @@ const utils = {
|
||||
});
|
||||
return `#${r}${g}${b}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @template {(...args: any[]) => any} F
|
||||
* @param {F} callback
|
||||
* @param {number} [wait=250]
|
||||
*/
|
||||
debounce(callback, wait = 250) {
|
||||
function debounce(callback, wait = 250) {
|
||||
/** @type {number | undefined} */
|
||||
let timeoutId;
|
||||
/** @type {Parameters<F>} */
|
||||
@@ -1110,45 +1125,69 @@ const utils = {
|
||||
}, wait);
|
||||
}
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {VoidFunction} callback
|
||||
* @param {number} [timeout = 1]
|
||||
*/
|
||||
runWhenIdle(callback, timeout = 1) {
|
||||
function runWhenIdle(callback, timeout = 1) {
|
||||
if ("requestIdleCallback" in window) {
|
||||
requestIdleCallback(callback);
|
||||
} else {
|
||||
setTimeout(callback, timeout);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Date} oldest
|
||||
* @param {Date} youngest
|
||||
* @returns {number}
|
||||
*/
|
||||
getNumberOfDaysBetweenTwoDates(oldest, youngest) {
|
||||
function getNumberOfDaysBetweenTwoDates(oldest, youngest) {
|
||||
const ONE_DAY_IN_MS = 1000 * 60 * 60 * 24;
|
||||
return Math.round(
|
||||
Math.abs((youngest.getTime() - oldest.getTime()) / consts.ONE_DAY_IN_MS),
|
||||
Math.abs((youngest.getTime() - oldest.getTime()) / ONE_DAY_IN_MS),
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} s
|
||||
*/
|
||||
function stringToId(s) {
|
||||
return s.replace(/\W/g, " ").trim().replace(/ +/g, "-").toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TimeScale} scale
|
||||
* @param {number} id
|
||||
*/
|
||||
chunkIdToIndex(scale, id) {
|
||||
return scale === "date"
|
||||
? id - 2009
|
||||
: Math.floor(id / consts.HEIGHT_CHUNK_SIZE);
|
||||
},
|
||||
/**
|
||||
* @param {string} s
|
||||
*/
|
||||
stringToId(s) {
|
||||
return s.replace(/\W/g, " ").trim().replace(/ +/g, "-").toLowerCase();
|
||||
},
|
||||
function chunkIdToIndex(scale, id) {
|
||||
const HEIGHT_CHUNK_SIZE = 10_000;
|
||||
return scale === "date" ? id - 2009 : Math.floor(id / HEIGHT_CHUNK_SIZE);
|
||||
}
|
||||
|
||||
return {
|
||||
isSerializedBooleanTrue,
|
||||
sleep,
|
||||
next,
|
||||
array,
|
||||
dom,
|
||||
url,
|
||||
locale,
|
||||
storage,
|
||||
serde,
|
||||
formatters,
|
||||
date,
|
||||
color,
|
||||
debounce,
|
||||
runWhenIdle,
|
||||
getNumberOfDaysBetweenTwoDates,
|
||||
chunkIdToIndex,
|
||||
stringToId,
|
||||
};
|
||||
/** @typedef {typeof utils} Utilities */
|
||||
}
|
||||
/** @typedef {ReturnType<typeof createUtils>} Utilities */
|
||||
|
||||
function initEnv() {
|
||||
const standalone =
|
||||
@@ -1175,8 +1214,7 @@ function initEnv() {
|
||||
localhost: window.location.hostname === "localhost",
|
||||
};
|
||||
}
|
||||
const env = initEnv();
|
||||
/** @typedef {typeof env} Env */
|
||||
/** @typedef {ReturnType<typeof initEnv>} Env */
|
||||
|
||||
function createConstants() {
|
||||
const ONE_SECOND_IN_MS = 1_000;
|
||||
@@ -1207,152 +1245,62 @@ function createConstants() {
|
||||
MEDIUM_WIDTH,
|
||||
};
|
||||
}
|
||||
const consts = createConstants();
|
||||
/** @typedef {typeof consts} Consts */
|
||||
/** @typedef {ReturnType<typeof createConstants>} Constants */
|
||||
|
||||
const ids = /** @type {const} */ ({
|
||||
function createIds() {
|
||||
return /** @type {const} */ ({
|
||||
selectedId: `selected-id`,
|
||||
asideSelectorLabel: `aside-selector-label`,
|
||||
checkedFrameSelectorLabel: "checked-frame-selector-label",
|
||||
});
|
||||
/** @typedef {typeof ids} Ids */
|
||||
}
|
||||
/** @typedef {ReturnType<typeof createIds>} Ids */
|
||||
|
||||
const elements = {
|
||||
/**
|
||||
* @param {Ids} ids
|
||||
*/
|
||||
function getElements(ids) {
|
||||
/**
|
||||
* @param {string} id
|
||||
*/
|
||||
function getElementById(id) {
|
||||
const element = window.document.getElementById(id);
|
||||
if (!element) throw `Element with id = "${id}" should exist`;
|
||||
return element;
|
||||
}
|
||||
|
||||
return {
|
||||
head: window.document.getElementsByTagName("head")[0],
|
||||
body: window.document.body,
|
||||
main: utils.dom.getElementById("main"),
|
||||
aside: utils.dom.getElementById("aside"),
|
||||
asideLabel: utils.dom.getElementById(ids.asideSelectorLabel),
|
||||
navLabel: utils.dom.getElementById(`nav-selector-label`),
|
||||
searchLabel: utils.dom.getElementById(`search-selector-label`),
|
||||
search: utils.dom.getElementById("search"),
|
||||
nav: utils.dom.getElementById("nav"),
|
||||
navHeader: utils.dom.getElementById("nav-header"),
|
||||
main: getElementById("main"),
|
||||
aside: getElementById("aside"),
|
||||
asideLabel: getElementById(ids.asideSelectorLabel),
|
||||
navLabel: getElementById(`nav-selector-label`),
|
||||
searchLabel: getElementById(`search-selector-label`),
|
||||
search: getElementById("search"),
|
||||
nav: getElementById("nav"),
|
||||
navHeader: getElementById("nav-header"),
|
||||
searchInput: /** @type {HTMLInputElement} */ (
|
||||
utils.dom.getElementById("search-input")
|
||||
getElementById("search-input")
|
||||
),
|
||||
searchSmall: utils.dom.getElementById("search-small"),
|
||||
searchResults: utils.dom.getElementById("search-results"),
|
||||
selectors: utils.dom.getElementById("frame-selectors"),
|
||||
searchSmall: getElementById("search-small"),
|
||||
searchResults: getElementById("search-results"),
|
||||
selectors: getElementById("frame-selectors"),
|
||||
style: getComputedStyle(window.document.documentElement),
|
||||
charts: utils.dom.getElementById("charts"),
|
||||
simulation: utils.dom.getElementById("simulation"),
|
||||
livePrice: utils.dom.getElementById("live-price"),
|
||||
moscowTime: utils.dom.getElementById("moscow-time"),
|
||||
charts: getElementById("charts"),
|
||||
simulation: getElementById("simulation"),
|
||||
livePrice: getElementById("live-price"),
|
||||
moscowTime: getElementById("moscow-time"),
|
||||
};
|
||||
/** @typedef {typeof elements} Elements */
|
||||
|
||||
const urlSelected = utils.url.pathnameToSelectedId();
|
||||
|
||||
function initFrameSelectors() {
|
||||
const children = Array.from(elements.selectors.children);
|
||||
|
||||
/** @type {HTMLElement | undefined} */
|
||||
let focusedFrame = undefined;
|
||||
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const element = children[i];
|
||||
|
||||
switch (element.tagName) {
|
||||
case "LABEL": {
|
||||
element.addEventListener("click", () => {
|
||||
const inputId = element.getAttribute("for");
|
||||
|
||||
if (!inputId) {
|
||||
console.log(element, element.getAttribute("for"));
|
||||
throw "Input id in label not found";
|
||||
}
|
||||
|
||||
const input = window.document.getElementById(inputId);
|
||||
|
||||
if (!input || !("value" in input)) {
|
||||
throw "Not input or no value";
|
||||
}
|
||||
|
||||
const frame = window.document.getElementById(
|
||||
/** @type {string} */ (input.value),
|
||||
);
|
||||
|
||||
if (!frame) {
|
||||
console.log(input.value);
|
||||
throw "Frame element doesn't exist";
|
||||
}
|
||||
|
||||
if (frame === focusedFrame) {
|
||||
return;
|
||||
}
|
||||
|
||||
frame.hidden = false;
|
||||
if (focusedFrame) {
|
||||
focusedFrame.hidden = true;
|
||||
}
|
||||
focusedFrame = frame;
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elements.asideLabel.click();
|
||||
|
||||
// When going from mobile view to desktop view, if selected frame was open, go to the nav frame
|
||||
new IntersectionObserver((entries) => {
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
if (
|
||||
!entries[i].isIntersecting &&
|
||||
entries[i].target === elements.asideLabel &&
|
||||
focusedFrame == elements.aside
|
||||
) {
|
||||
elements.navLabel.click();
|
||||
}
|
||||
}
|
||||
}).observe(elements.asideLabel);
|
||||
|
||||
function setAsideParent() {
|
||||
const { clientWidth } = window.document.documentElement;
|
||||
const { aside, body, main } = elements;
|
||||
if (clientWidth >= consts.MEDIUM_WIDTH) {
|
||||
aside.parentElement !== body && body.append(aside);
|
||||
} else {
|
||||
aside.parentElement !== main && main.append(aside);
|
||||
}
|
||||
}
|
||||
|
||||
setAsideParent();
|
||||
|
||||
window.addEventListener("resize", setAsideParent);
|
||||
}
|
||||
initFrameSelectors();
|
||||
|
||||
function createKeyDownEventListener() {
|
||||
window.document.addEventListener("keydown", (event) => {
|
||||
switch (event.key) {
|
||||
case "Escape": {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
elements.navLabel.click();
|
||||
break;
|
||||
}
|
||||
case "/": {
|
||||
if (window.document.activeElement === elements.searchInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
elements.searchLabel.click();
|
||||
elements.searchInput.focus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
createKeyDownEventListener();
|
||||
/** @typedef {ReturnType<typeof getElements>} Elements */
|
||||
|
||||
/**
|
||||
* @param {Accessor<boolean>} dark
|
||||
* @param {Elements} elements
|
||||
* @param {Utilities} utils
|
||||
*/
|
||||
function createColors(dark) {
|
||||
function createColors(dark, elements, utils) {
|
||||
/**
|
||||
* @param {string} color
|
||||
*/
|
||||
@@ -1568,8 +1516,10 @@ function createColors(dark) {
|
||||
|
||||
/**
|
||||
* @param {Signals} signals
|
||||
* @param {Constants} consts
|
||||
* @param {Utilities} utils
|
||||
*/
|
||||
function createDatasets(signals) {
|
||||
function createDatasets(signals, consts, utils) {
|
||||
/** @type {Map<DatePath, ResourceDataset<"date">>} */
|
||||
const date = new Map();
|
||||
/** @type {Map<HeightPath, ResourceDataset<"height">>} */
|
||||
@@ -1932,8 +1882,9 @@ function createDatasets(signals) {
|
||||
|
||||
/**
|
||||
* @param {Signals} signals
|
||||
* @param {Utilities} utils
|
||||
*/
|
||||
function initWebSockets(signals) {
|
||||
function initWebSockets(signals, utils) {
|
||||
/**
|
||||
* @template T
|
||||
* @param {(callback: (value: T) => void) => WebSocket} creator
|
||||
@@ -2072,13 +2023,129 @@ function initWebSockets(signals) {
|
||||
}
|
||||
/** @typedef {ReturnType<typeof initWebSockets>} WebSockets */
|
||||
|
||||
function main() {
|
||||
const packages = initPackages();
|
||||
const options = import("./options.js");
|
||||
const env = initEnv();
|
||||
const consts = createConstants();
|
||||
const utils = createUtils();
|
||||
const ids = createIds();
|
||||
const elements = getElements(ids);
|
||||
|
||||
function initFrameSelectors() {
|
||||
const children = Array.from(elements.selectors.children);
|
||||
|
||||
/** @type {HTMLElement | undefined} */
|
||||
let focusedFrame = undefined;
|
||||
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const element = children[i];
|
||||
|
||||
switch (element.tagName) {
|
||||
case "LABEL": {
|
||||
element.addEventListener("click", () => {
|
||||
const inputId = element.getAttribute("for");
|
||||
|
||||
if (!inputId) {
|
||||
console.log(element, element.getAttribute("for"));
|
||||
throw "Input id in label not found";
|
||||
}
|
||||
|
||||
const input = window.document.getElementById(inputId);
|
||||
|
||||
if (!input || !("value" in input)) {
|
||||
throw "Not input or no value";
|
||||
}
|
||||
|
||||
const frame = window.document.getElementById(
|
||||
/** @type {string} */ (input.value),
|
||||
);
|
||||
|
||||
if (!frame) {
|
||||
console.log(input.value);
|
||||
throw "Frame element doesn't exist";
|
||||
}
|
||||
|
||||
if (frame === focusedFrame) {
|
||||
return;
|
||||
}
|
||||
|
||||
frame.hidden = false;
|
||||
if (focusedFrame) {
|
||||
focusedFrame.hidden = true;
|
||||
}
|
||||
focusedFrame = frame;
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elements.asideLabel.click();
|
||||
|
||||
// When going from mobile view to desktop view, if selected frame was open, go to the nav frame
|
||||
new IntersectionObserver((entries) => {
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
if (
|
||||
!entries[i].isIntersecting &&
|
||||
entries[i].target === elements.asideLabel &&
|
||||
focusedFrame == elements.aside
|
||||
) {
|
||||
elements.navLabel.click();
|
||||
}
|
||||
}
|
||||
}).observe(elements.asideLabel);
|
||||
|
||||
function setAsideParent() {
|
||||
const { clientWidth } = window.document.documentElement;
|
||||
const { aside, body, main } = elements;
|
||||
if (clientWidth >= consts.MEDIUM_WIDTH) {
|
||||
aside.parentElement !== body && body.append(aside);
|
||||
} else {
|
||||
aside.parentElement !== main && main.append(aside);
|
||||
}
|
||||
}
|
||||
|
||||
setAsideParent();
|
||||
|
||||
window.addEventListener("resize", setAsideParent);
|
||||
}
|
||||
initFrameSelectors();
|
||||
|
||||
function createKeyDownEventListener() {
|
||||
window.document.addEventListener("keydown", (event) => {
|
||||
switch (event.key) {
|
||||
case "Escape": {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
elements.navLabel.click();
|
||||
break;
|
||||
}
|
||||
case "/": {
|
||||
if (window.document.activeElement === elements.searchInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
elements.searchLabel.click();
|
||||
elements.searchInput.focus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
createKeyDownEventListener();
|
||||
|
||||
packages.signals().then((signals) =>
|
||||
options.then(({ initOptions }) => {
|
||||
function initDark() {
|
||||
const preferredColorSchemeMatchMedia = window.matchMedia(
|
||||
"(prefers-color-scheme: dark)",
|
||||
);
|
||||
const dark = signals.createSignal(preferredColorSchemeMatchMedia.matches);
|
||||
const dark = signals.createSignal(
|
||||
preferredColorSchemeMatchMedia.matches,
|
||||
);
|
||||
preferredColorSchemeMatchMedia.addEventListener(
|
||||
"change",
|
||||
({ matches }) => {
|
||||
@@ -2129,9 +2196,9 @@ packages.signals().then((signals) =>
|
||||
}
|
||||
createFetchLastValuesWhenNeededEffect();
|
||||
|
||||
const webSockets = initWebSockets(signals);
|
||||
const webSockets = initWebSockets(signals, utils);
|
||||
|
||||
const colors = createColors(dark);
|
||||
const colors = createColors(dark, elements, utils);
|
||||
|
||||
const options = initOptions({
|
||||
colors,
|
||||
@@ -2144,22 +2211,23 @@ packages.signals().then((signals) =>
|
||||
qrcode,
|
||||
});
|
||||
|
||||
function createWindowPopStateEvent() {
|
||||
window.addEventListener("popstate", (event) => {
|
||||
const urlSelected = utils.url.pathnameToSelectedId();
|
||||
const option = options.list.find((option) => urlSelected === option.id);
|
||||
if (option) {
|
||||
options.selected.set(option);
|
||||
}
|
||||
});
|
||||
}
|
||||
// const urlSelected = utils.url.pathnameToSelectedId();
|
||||
// function createWindowPopStateEvent() {
|
||||
// window.addEventListener("popstate", (event) => {
|
||||
// const urlSelected = utils.url.pathnameToSelectedId();
|
||||
// const option = options.list.find((option) => urlSelected === option.id);
|
||||
// if (option) {
|
||||
// options.selected.set(option);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// createWindowPopStateEvent();
|
||||
|
||||
function initSelected() {
|
||||
function initSelectedFrame() {
|
||||
console.log("selected: init");
|
||||
|
||||
const datasets = createDatasets(signals);
|
||||
const datasets = createDatasets(signals, consts, utils);
|
||||
|
||||
function createApplyOptionEffect() {
|
||||
const lastChartOption = signals.createSignal(
|
||||
@@ -2214,6 +2282,7 @@ packages.signals().then((signals) =>
|
||||
colors,
|
||||
datasets,
|
||||
elements,
|
||||
consts,
|
||||
lightweightCharts,
|
||||
selected: /** @type {any} */ (lastChartOption),
|
||||
signals,
|
||||
@@ -2249,6 +2318,7 @@ packages.signals().then((signals) =>
|
||||
lightweightCharts,
|
||||
signals,
|
||||
utils,
|
||||
consts,
|
||||
lastValues,
|
||||
}),
|
||||
),
|
||||
@@ -2304,7 +2374,9 @@ packages.signals().then((signals) =>
|
||||
const lightweightCharts = packages.lightweightCharts();
|
||||
const script = import("./moscow-time.js");
|
||||
|
||||
utils.dom.importStyleAndThen("/styles/moscow-time.css", () =>
|
||||
utils.dom.importStyleAndThen(
|
||||
"/styles/moscow-time.css",
|
||||
() =>
|
||||
script.then(({ init }) =>
|
||||
signals.runWithOwner(owner, () =>
|
||||
init({
|
||||
@@ -2431,13 +2503,15 @@ packages.signals().then((signals) =>
|
||||
details.open = true;
|
||||
i++;
|
||||
} catch {
|
||||
await utils.yield();
|
||||
await utils.next();
|
||||
}
|
||||
}
|
||||
|
||||
await utils.yield();
|
||||
await utils.next();
|
||||
|
||||
utils.dom.getElementById(`${selectedId}-nav-selector`).scrollIntoView({
|
||||
utils.dom
|
||||
.getElementById(`${selectedId}-nav-selector`)
|
||||
.scrollIntoView({
|
||||
behavior: "instant",
|
||||
block: "center",
|
||||
});
|
||||
@@ -2532,7 +2606,9 @@ packages.signals().then((signals) =>
|
||||
|
||||
function inputEvent() {
|
||||
signals.createRoot((_dispose) => {
|
||||
const needle = /** @type {string} */ (elements.searchInput.value);
|
||||
const needle = /** @type {string} */ (
|
||||
elements.searchInput.value
|
||||
);
|
||||
|
||||
dispose?.();
|
||||
|
||||
@@ -2695,3 +2771,5 @@ packages.signals().then((signals) =>
|
||||
initDesktopResizeBar();
|
||||
}),
|
||||
);
|
||||
}
|
||||
main();
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {Colors} args.colors
|
||||
* @param {Consts} args.consts
|
||||
* @param {Constants} args.consts
|
||||
* @param {Signals} args.signals
|
||||
* @param {Utilities} args.utils
|
||||
* @param {Options} args.options
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
* @import {AnySpecificSeriesBlueprint, SplitSeriesBlueprint} from '../packages/lightweight-charts/types';
|
||||
*/
|
||||
|
||||
const DATE_TO_PREFIX = "date-to-";
|
||||
const HEIGHT_TO_PREFIX = "height-to-";
|
||||
|
||||
function initGroups() {
|
||||
const xTermHolders = /** @type {const} */ ([
|
||||
{
|
||||
@@ -5204,8 +5201,8 @@ export function initOptions({
|
||||
if (!blueprint) return undefined;
|
||||
|
||||
const id = blueprint.datasetPath
|
||||
.replace(DATE_TO_PREFIX, "")
|
||||
.replace(HEIGHT_TO_PREFIX, "");
|
||||
.replace("date-to-", "")
|
||||
.replace("height-to-", "");
|
||||
|
||||
return /** @type {LastPath} */ (id);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* @param {Utilities} args.utils
|
||||
* @param {Datasets} args.datasets
|
||||
* @param {Elements} args.elements
|
||||
* @param {Constants} args.consts
|
||||
* @param {Signal<LastValues>} args.lastValues
|
||||
*/
|
||||
export function init({
|
||||
@@ -22,6 +23,7 @@ export function init({
|
||||
lightweightCharts,
|
||||
signals,
|
||||
utils,
|
||||
consts,
|
||||
lastValues,
|
||||
}) {
|
||||
const simulationElement = elements.simulation;
|
||||
@@ -31,6 +33,120 @@ export function init({
|
||||
const resultsElement = window.document.createElement("div");
|
||||
simulationElement.append(resultsElement);
|
||||
|
||||
function computeFrequencies() {
|
||||
const weekDays = [
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
"Sunday",
|
||||
];
|
||||
const maxDays = 28;
|
||||
|
||||
/** @param {number} day */
|
||||
function getOrdinalDay(day) {
|
||||
const rest = (day % 30) % 20;
|
||||
|
||||
return `${day}${
|
||||
rest === 1 ? "st" : rest === 2 ? "nd" : rest === 3 ? "rd" : "th"
|
||||
}`;
|
||||
}
|
||||
|
||||
/** @satisfies {([Frequency, Frequencies, Frequencies, Frequencies])} */
|
||||
const list = [
|
||||
{
|
||||
name: "Every day",
|
||||
value: "every-day",
|
||||
/** @param {Date} _ */
|
||||
isTriggerDay(_) {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Once a week",
|
||||
list: weekDays.map((day, index) => ({
|
||||
name: day,
|
||||
value: day.toLowerCase(),
|
||||
/** @param {Date} date */
|
||||
isTriggerDay(date) {
|
||||
let day = date.getUTCDay() - 1;
|
||||
if (day === -1) {
|
||||
day = 6;
|
||||
}
|
||||
return day === index;
|
||||
},
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Every two weeks",
|
||||
list: [...Array(Math.round(maxDays / 2)).keys()].map((day) => {
|
||||
const day1 = day + 1;
|
||||
const day2 = day + 15;
|
||||
|
||||
return {
|
||||
value: `${day1}+${day2}`,
|
||||
name: `The ${getOrdinalDay(day1)} and the ${getOrdinalDay(day2)}`,
|
||||
/** @param {Date} date */
|
||||
isTriggerDay(date) {
|
||||
const d = date.getUTCDate();
|
||||
return d === day1 || d === day2;
|
||||
},
|
||||
};
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Once a month",
|
||||
list: [...Array(maxDays).keys()].map((day) => {
|
||||
day++;
|
||||
|
||||
return {
|
||||
name: `The ${getOrdinalDay(day)}`,
|
||||
value: String(day),
|
||||
/** @param {Date} date */
|
||||
isTriggerDay(date) {
|
||||
const d = date.getUTCDate();
|
||||
return d === day;
|
||||
},
|
||||
};
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
/** @type {Record<string, Frequency>} */
|
||||
const idToFrequency = {};
|
||||
|
||||
list.forEach((anyFreq, index) => {
|
||||
if ("list" in anyFreq) {
|
||||
anyFreq.list?.forEach((freq) => {
|
||||
idToFrequency[freq.value] = freq;
|
||||
});
|
||||
} else {
|
||||
idToFrequency[anyFreq.value] = anyFreq;
|
||||
}
|
||||
});
|
||||
|
||||
const serde = {
|
||||
/**
|
||||
* @param {Frequency} v
|
||||
*/
|
||||
serialize(v) {
|
||||
return v.value;
|
||||
},
|
||||
/**
|
||||
* @param {string} v
|
||||
*/
|
||||
deserialize(v) {
|
||||
const freq = idToFrequency[v];
|
||||
if (!freq) throw "Freq not found";
|
||||
return freq;
|
||||
},
|
||||
};
|
||||
|
||||
return { list, serde };
|
||||
}
|
||||
|
||||
const frequencies = computeFrequencies();
|
||||
|
||||
const keyPrefix = "save-in-bitcoin";
|
||||
@@ -437,6 +553,7 @@ export function init({
|
||||
kind: "static",
|
||||
scale: "date",
|
||||
utils,
|
||||
consts,
|
||||
config: [
|
||||
{
|
||||
unit: "US Dollars",
|
||||
@@ -478,6 +595,7 @@ export function init({
|
||||
scale: "date",
|
||||
kind: "static",
|
||||
utils,
|
||||
consts,
|
||||
config: [
|
||||
{
|
||||
unit: "US Dollars",
|
||||
@@ -501,6 +619,7 @@ export function init({
|
||||
scale: "date",
|
||||
kind: "static",
|
||||
utils,
|
||||
consts,
|
||||
config: [
|
||||
{
|
||||
unit: "US Dollars",
|
||||
@@ -530,6 +649,7 @@ export function init({
|
||||
scale: "date",
|
||||
kind: "static",
|
||||
utils,
|
||||
consts,
|
||||
config: [
|
||||
{
|
||||
unit: "US Dollars",
|
||||
@@ -563,6 +683,7 @@ export function init({
|
||||
scale: "date",
|
||||
utils,
|
||||
owner,
|
||||
consts,
|
||||
config: [
|
||||
{
|
||||
unit: "Percentage",
|
||||
@@ -879,117 +1000,3 @@ export function init({
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** @param {number} day */
|
||||
function getOrdinalDay(day) {
|
||||
const rest = (day % 30) % 20;
|
||||
|
||||
return `${day}${
|
||||
rest === 1 ? "st" : rest === 2 ? "nd" : rest === 3 ? "rd" : "th"
|
||||
}`;
|
||||
}
|
||||
|
||||
function computeFrequencies() {
|
||||
const weekDays = [
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
"Sunday",
|
||||
];
|
||||
const maxDays = 28;
|
||||
|
||||
/** @satisfies {([Frequency, Frequencies, Frequencies, Frequencies])} */
|
||||
const list = [
|
||||
{
|
||||
name: "Every day",
|
||||
value: "every-day",
|
||||
/** @param {Date} _ */
|
||||
isTriggerDay(_) {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Once a week",
|
||||
list: weekDays.map((day, index) => ({
|
||||
name: day,
|
||||
value: day.toLowerCase(),
|
||||
/** @param {Date} date */
|
||||
isTriggerDay(date) {
|
||||
let day = date.getUTCDay() - 1;
|
||||
if (day === -1) {
|
||||
day = 6;
|
||||
}
|
||||
return day === index;
|
||||
},
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Every two weeks",
|
||||
list: [...Array(Math.round(maxDays / 2)).keys()].map((day) => {
|
||||
const day1 = day + 1;
|
||||
const day2 = day + 15;
|
||||
|
||||
return {
|
||||
value: `${day1}+${day2}`,
|
||||
name: `The ${getOrdinalDay(day1)} and the ${getOrdinalDay(day2)}`,
|
||||
/** @param {Date} date */
|
||||
isTriggerDay(date) {
|
||||
const d = date.getUTCDate();
|
||||
return d === day1 || d === day2;
|
||||
},
|
||||
};
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Once a month",
|
||||
list: [...Array(maxDays).keys()].map((day) => {
|
||||
day++;
|
||||
|
||||
return {
|
||||
name: `The ${getOrdinalDay(day)}`,
|
||||
value: String(day),
|
||||
/** @param {Date} date */
|
||||
isTriggerDay(date) {
|
||||
const d = date.getUTCDate();
|
||||
return d === day;
|
||||
},
|
||||
};
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
/** @type {Record<string, Frequency>} */
|
||||
const idToFrequency = {};
|
||||
|
||||
list.forEach((anyFreq, index) => {
|
||||
if ("list" in anyFreq) {
|
||||
anyFreq.list?.forEach((freq) => {
|
||||
idToFrequency[freq.value] = freq;
|
||||
});
|
||||
} else {
|
||||
idToFrequency[anyFreq.value] = anyFreq;
|
||||
}
|
||||
});
|
||||
|
||||
const serde = {
|
||||
/**
|
||||
* @param {Frequency} v
|
||||
*/
|
||||
serialize(v) {
|
||||
return v.value;
|
||||
},
|
||||
/**
|
||||
* @param {string} v
|
||||
*/
|
||||
deserialize(v) {
|
||||
const freq = idToFrequency[v];
|
||||
if (!freq) throw "Freq not found";
|
||||
return freq;
|
||||
},
|
||||
};
|
||||
|
||||
return { list, serde };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user