chore (tsc): tsc checks

This commit is contained in:
MickaelK 2024-07-19 01:21:22 +10:00
parent 8332752838
commit 9a45dc000a
60 changed files with 220 additions and 135 deletions

View file

@ -1,6 +1,7 @@
import { toHref } from "../lib/skeleton/router.js"; import { toHref } from "../lib/skeleton/router.js";
import { animate, slideYOut, slideYIn, opacityOut } from "../lib/animate.js"; import { animate, slideYOut, slideYIn, opacityOut } from "../lib/animate.js";
import { forwardURLParams } from "../lib/path.js"; import { forwardURLParams } from "../lib/path.js";
import assert from "../lib/assert.js";
import { loadCSS } from "../helpers/loader.js"; import { loadCSS } from "../helpers/loader.js";
import { extractPath, isDir, isNativeFileUpload } from "../pages/filespage/helper.js"; import { extractPath, isDir, isNativeFileUpload } from "../pages/filespage/helper.js";
@ -64,15 +65,14 @@ class ComponentBreadcrumb extends window.HTMLElement {
const tasks = []; const tasks = [];
for (let i=0; i<nToAnimate; i++) { for (let i=0; i<nToAnimate; i++) {
const n = previousChunks.length - i - 1; const n = previousChunks.length - i - 1;
const $chunk = this.querySelector(`.component_path-element.n${n}`); const $chunk = assert.type(this.querySelector(`.component_path-element.n${n}`), window.HTMLElement);
if (!$chunk) throw new Error("component::breadcrumb.js - assertion failed - empty element");
tasks.push(animate($chunk, { time: 100, keyframes: slideYOut(-10) })); tasks.push(animate($chunk, { time: 100, keyframes: slideYOut(-10) }));
} }
await Promise.all(tasks); await Promise.all(tasks);
} }
// STEP2: setup the actual content // STEP2: setup the actual content
this.querySelector(`[data-bind="path"]`).innerHTML = pathChunks.map((chunk, idx) => { assert.type(this.querySelector(`[data-bind="path"]`), window.HTMLElement).innerHTML = pathChunks.map((chunk, idx) => {
const label = idx === 0 ? (window.CONFIG.name || "Filestash") : chunk; const label = idx === 0 ? (window.CONFIG.name || "Filestash") : chunk;
const link = pathChunks.slice(0, idx + 1).join("/") + "/"; const link = pathChunks.slice(0, idx + 1).join("/") + "/";
const limitSize = (word, highlight = false) => { const limitSize = (word, highlight = false) => {
@ -130,8 +130,7 @@ class ComponentBreadcrumb extends window.HTMLElement {
const nToAnimate = pathChunks.length - previousChunks.length; const nToAnimate = pathChunks.length - previousChunks.length;
for (let i=0; i<nToAnimate; i++) { for (let i=0; i<nToAnimate; i++) {
const n = pathChunks.length - i - 1; const n = pathChunks.length - i - 1;
const $chunk = this.querySelector(`.component_path-element.n${n}`); const $chunk = assert.type(this.querySelector(`.component_path-element.n${n}`), window.HTMLElement);
if (!$chunk) throw new Error("component::breadcrumb.js - assertion failed - empty element");
await animate($chunk, { time: 100, keyframes: slideYIn(-5) }); await animate($chunk, { time: 100, keyframes: slideYIn(-5) });
} }
} }
@ -141,9 +140,8 @@ class ComponentBreadcrumb extends window.HTMLElement {
let state = this.hasAttribute("indicator"); let state = this.hasAttribute("indicator");
if (state && this.getAttribute("indicator") !== "false") state = true; if (state && this.getAttribute("indicator") !== "false") state = true;
const $indicator = this.querySelector(`[data-bind="path"]`) let $indicator = assert.type(this.querySelector(`[data-bind="path"]`), window.HTMLElement);
.lastChild $indicator = assert.type($indicator.lastChild.querySelector("span"), window.HTMLElement);
.querySelector("span");
if (state) { if (state) {
$indicator.style.opacity = 1; $indicator.style.opacity = 1;

View file

@ -1,6 +1,7 @@
import { createElement, createRender } from "../lib/skeleton/index.js"; import { createElement, createRender } from "../lib/skeleton/index.js";
import { navigate, fromHref } from "../lib/skeleton/router.js"; import { navigate, fromHref } from "../lib/skeleton/router.js";
import rxjs, { effect } from "../lib/rx.js"; import rxjs, { effect } from "../lib/rx.js";
import assert from "../lib/assert.js";
import { onDestroy } from "../lib/skeleton/lifecycle.js"; import { onDestroy } from "../lib/skeleton/lifecycle.js";
import { animate, slideYOut } from "../lib/animate.js"; import { animate, slideYOut } from "../lib/animate.js";
import { qs } from "../lib/dom.js"; import { qs } from "../lib/dom.js";
@ -9,7 +10,7 @@ import { init as initBreadcrumb } from "../components/breadcrumb.js";
import ctrlSidebar, { init as initSidebar } from "./sidebar.js"; import ctrlSidebar, { init as initSidebar } from "./sidebar.js";
export default function(ctrl) { export default function(ctrl) {
const urlToPath = (pathname = "") => decodeURIComponent(pathname.split("/").filter((chunk, i) => i !== 1).join("/")); const urlToPath = (pathname = "") => decodeURIComponent(pathname.split("/").filter((_, i) => i !== 1).join("/"));
const $page = createElement(` const $page = createElement(`
<div class="component_filemanager_shell" style="flex-direction:row"> <div class="component_filemanager_shell" style="flex-direction:row">
<div data-bind="sidebar"></div> <div data-bind="sidebar"></div>
@ -42,7 +43,9 @@ export default function(ctrl) {
// feature3: key shortcut // feature3: key shortcut
const regexStartFiles = new RegExp("^/files/.+"); const regexStartFiles = new RegExp("^/files/.+");
effect(rxjs.fromEvent(window, "keydown").pipe( effect(rxjs.fromEvent(window, "keydown").pipe(
rxjs.filter((e) => regexStartFiles.test(fromHref(location.pathname)) && e.keyCode === 8 && document.activeElement.nodeName !== "INPUT"), // backspace in filemanager rxjs.filter((e) => regexStartFiles.test(fromHref(location.pathname)) &&
e.keyCode === 8 &&
assert.type(document.activeElement, window.HTMLElement).nodeName !== "INPUT"), // backspace in filemanager
rxjs.tap(() => { rxjs.tap(() => {
const p = location.pathname.replace(new RegExp("/$"), "").split("/"); const p = location.pathname.replace(new RegExp("/$"), "").split("/");
p.pop(); p.pop();

View file

@ -1,5 +1,6 @@
import { createFragment } from "../lib/skeleton/index.js"; import { createFragment } from "../lib/skeleton/index.js";
import { animate, slideYIn } from "../lib/animate.js"; import { animate, slideYIn } from "../lib/animate.js";
import assert from "../lib/assert.js";
import { loadCSS } from "../helpers/loader.js"; import { loadCSS } from "../helpers/loader.js";
await loadCSS(import.meta.url, "./dropdown.css"); await loadCSS(import.meta.url, "./dropdown.css");
@ -75,9 +76,9 @@ export default class ComponentDropdown extends HTMLDivElement {
`)); `));
const setActive = () => this.classList.toggle("active"); const setActive = () => this.classList.toggle("active");
this.querySelector(".dropdown_button").onclick = () => { assert.type(this.querySelector(".dropdown_button"), window.HTMLElement).onclick = () => {
setActive(); setActive();
animate(this.querySelector(".dropdown_container"), { animate(assert.type(this.querySelector(".dropdown_container"), window.HTMLElement), {
time: 100, time: 100,
keyframes: slideYIn(2), keyframes: slideYIn(2),
}); });

View file

@ -1,4 +1,5 @@
import { loadCSS } from "../helpers/loader.js"; import { loadCSS } from "../helpers/loader.js";
import assert from "../lib/assert.js";
export default class ComponentFab extends window.HTMLButtonElement { export default class ComponentFab extends window.HTMLButtonElement {
constructor() { constructor() {
@ -9,7 +10,7 @@ export default class ComponentFab extends window.HTMLButtonElement {
async render($icon) { async render($icon) {
await loadCSS(import.meta.url, "./fab.css"); await loadCSS(import.meta.url, "./fab.css");
this.querySelector(".content").replaceChildren($icon); assert.type(this.querySelector(".content"), window.HTMLElement).replaceChildren($icon);
} }
} }

View file

@ -6,7 +6,7 @@ import { qs, qsa } from "../lib/dom.js";
import { CSS } from "../helpers/loader.js"; import { CSS } from "../helpers/loader.js";
export function createModal(opts) { export function createModal(opts) {
const $dom = document.body.querySelector("component-modal"); const $dom = assert.type(qs(document.body, "component-modal"), window.HTMLElement);
assert.type($dom, ModalComponent); assert.type($dom, ModalComponent);
return ($node, fn) => $dom.trigger($node, { onQuit: fn, ...opts }); return ($node, fn) => $dom.trigger($node, { onQuit: fn, ...opts });
@ -35,14 +35,14 @@ const $modal = createElement(`
`); `);
class ModalComponent extends window.HTMLElement { class ModalComponent extends window.HTMLElement {
trigger($node, { withButtonsLeft = null, withButtonsRight = null, targetHeight = null, onQuit = (a) => Promise.resolve(a) }) { trigger($node, { withButtonsLeft = null, withButtonsRight = null, targetHeight = 0, onQuit = (a) => Promise.resolve(a) }) {
const close$ = new rxjs.Subject(); const close$ = new rxjs.Subject();
// feature: build the dom // feature: build the dom
qs($modal, `[data-bind="body"]`).replaceChildren($node); qs($modal, `[data-bind="body"]`).replaceChildren($node);
this.replaceChildren($modal); this.replaceChildren($modal);
qsa($modal, ".component_popup > div.buttons > button").forEach(($button, i) => { qsa($modal, ".component_popup > div.buttons > button").forEach(($button, i) => {
assert.truthy(i >= 0 & i <= 2); assert.truthy(i >= 0 && i <= 2);
let currentLabel = null; let currentLabel = null;
let buttonIndex = null; let buttonIndex = null;
if (i === 0) { if (i === 0) {

View file

@ -18,7 +18,7 @@ const mv = (from, to) => withVirtualLayer(
); );
export default async function ctrlSidebar(render, nRestart = 0) { export default async function ctrlSidebar(render, nRestart = 0) {
if (new URL(location).searchParams.get("nav") === "false") return; if (new URL(location.toString()).searchParams.get("nav") === "false") return;
else if (document.body.clientWidth < 850) return; else if (document.body.clientWidth < 850) return;
const $page = render(createElement(` const $page = render(createElement(`

View file

@ -1,9 +1,11 @@
export function loadScript(url: string): Promise<string>; export function loadScript(url: string): Promise<string>;
export function CSS(baseURL: string, ...arrayOfFilenames: string[]): Promise<string>;
export function loadSingleCSS(baseURL: string, filename: string): Promise<string>;
export function loadJS(baseURL: string, path: string, opts?: object): Promise<string>; export function loadJS(baseURL: string, path: string, opts?: object): Promise<string>;
export function loadCSS(baseURL: string, ...arrayOfFilenames: string[]): Promise<string>;
export function loadCSSInline(baseURL: string, filename: string): Promise<string>;
export function CSS(baseURL: string, ...arrayOfFilenames: string[]): Promise<string>;
export function init(): Promise<void>; export function init(): Promise<void>;

View file

@ -12,8 +12,8 @@ export async function loadJS(baseURL, path, opts = {}) {
if (document.head.querySelector(`[src="${link.toString()}"]`)) return Promise.resolve(); if (document.head.querySelector(`[src="${link.toString()}"]`)) return Promise.resolve();
document.head.appendChild($script); document.head.appendChild($script);
return new Promise((done) => { return new Promise((done) => {
$script.onload = done; $script.onload = () => done();
$script.onerror = done; $script.onerror = () => done();
}); });
} }

View file

@ -10,6 +10,8 @@ type AnimationFrames = {
transform?: string; transform?: string;
opacity?: number; opacity?: number;
height?: string; height?: string;
width?: string;
offset?: number;
}; };
export function transition($node: HTMLElement, opts?: TransitionEnter | TransitionLeave): HTMLElement; export function transition($node: HTMLElement, opts?: TransitionEnter | TransitionLeave): HTMLElement;
@ -17,6 +19,10 @@ export function transition($node: HTMLElement, opts?: TransitionEnter | Transiti
export function animate($node: HTMLElement | null, opts: { export function animate($node: HTMLElement | null, opts: {
time: number; time: number;
keyframes: AnimationFrames[]; keyframes: AnimationFrames[];
easing?: string;
fill?: string;
onExit?: () => void;
onEnter?: () => void;
}): Promise<void>; }): Promise<void>;
export function slideXIn(dist: number): AnimationFrames[]; export function slideXIn(dist: number): AnimationFrames[];

View file

@ -1,15 +1,14 @@
export default class assert { export default class assert {
static type(object, type, msg) { static type(object, type, msg) {
if (object instanceof type) return; if (!(object instanceof type)) throw new TypeError(msg || `assertion failed - unexpected type for ${object.toString()}`);
throw new Error(msg || `assertion failed - unexpected type for ${object.toString()}`); return object;
} }
static truthy(object, msg) { static truthy(object, msg) {
if (object) return; if (!object) throw new TypeError(msg || `assertion failed - object is not truthy`);
throw new Error(msg || `assertion failed - object is not truthy`);
} }
static fail(object, msg) { static fail(object, msg) {
throw new Error(msg || `assertion failed - ${object}`); throw new TypeError(msg || `assertion failed - ${object}`);
} }
} }

View file

@ -37,7 +37,7 @@ class ChromecastManager {
// } // }
createRequest(mediaInfo, authorization) { createRequest(mediaInfo, authorization) {
if (!authorization) Promise.error(new Error("Invalid account")); if (!authorization) Promise.reject(new Error("Invalid account"));
// TODO: it would be much much nicer to set the authorization in an HTTP header // TODO: it would be much much nicer to set the authorization in an HTTP header
// but this would require to create a custom web receiver app, setup accounts on // but this would require to create a custom web receiver app, setup accounts on

View file

@ -1,5 +1,5 @@
export function qs($node: HTMLElement, selector: string); export function qs($node: HTMLElement | DocumentFragment, selector: string);
export function qsa($node: HTMLElement, selector: string); export function qsa($node: HTMLElement | DocumentFragment, selector: string);
export function safe(str: string): string; export function safe(str: string): string;

View file

@ -1 +1,3 @@
export function gid(prefix: string): string; export function gid(prefix: string): string;
export function randomString(size: number): string;

View file

@ -7,11 +7,12 @@ import {
switchMapTo, switchMap, switchMapTo, switchMap,
BehaviorSubject, Subject, ReplaySubject, BehaviorSubject, Subject, ReplaySubject,
pipe, share, toArray, distinctUntilChanged, from, pipe, share, toArray, distinctUntilChanged, from,
combineLatest, shareReplay, repeat, interval, merge, combineLatest, shareReplay, race, repeat, interval, merge,
debounceTime, delay, concatMap, distinct, scan, throwError, debounceTime, debounce, delay, concatMap, distinct, scan, throwError,
} from "./vendor/rxjs.min.js"; zip, animationFrames, retry, forkJoin, skip, takeUntil, timer,
} from "./vendor/rxjs/rxjs.min.js";
import * as rxajax from "./vendor/rxjs-ajax.min.js"; import * as rxajax from "./vendor/rxjs/rxjs-ajax.min.js";
declare const rxjs: { declare const rxjs: {
of: typeof of, of: typeof of,
@ -31,6 +32,8 @@ declare const rxjs: {
fromEvent: typeof fromEvent, fromEvent: typeof fromEvent,
delay: typeof delay, delay: typeof delay,
concatMap: typeof concatMap, concatMap: typeof concatMap,
animationFrames: typeof animationFrames,
debounce: typeof debounce,
debounceTime: typeof debounceTime, debounceTime: typeof debounceTime,
throwError: typeof throwError, throwError: typeof throwError,
interval: typeof interval, interval: typeof interval,
@ -48,6 +51,13 @@ declare const rxjs: {
pipe: typeof pipe, pipe: typeof pipe,
share: typeof share, share: typeof share,
scan: typeof scan, scan: typeof scan,
race: typeof race,
retry: typeof retry,
zip: typeof zip,
forkJoin: typeof forkJoin,
skip: typeof skip,
takeUntil: typeof takeUntil,
timer: typeof timer,
}; };
export default rxjs; export default rxjs;
@ -65,3 +75,5 @@ export function stateMutation($node: HTMLElement, attr: string);
export function preventDefault(): typeof tap; export function preventDefault(): typeof tap;
export function onClick($node: HTMLElement): typeof fromEvent; export function onClick($node: HTMLElement): typeof fromEvent;
export function onLoad($node: HTMLElement): typeof fromEvent;

View file

@ -5,6 +5,8 @@ export default function($root: HTMLElement | null, routes: object, opts: object)
export function createElement(str: string): HTMLElement; export function createElement(str: string): HTMLElement;
export function createFragment(str: string): DocumentFragment;
export function createRender($parent: HTMLElement | null): (HTMLElement) => void; export function createRender($parent: HTMLElement | null): (HTMLElement) => void;
export function nop(): void export function nop(): void

View file

@ -1,5 +1,5 @@
export function settingsGet(initialValues, prefix = "") { export function settingsGet(initialValues, prefix = "") {
const raw = JSON.parse(localStorage.getItem("settings")) || {}; const raw = JSON.parse(localStorage.getItem("settings") || "{}") || {};
const currentSettings = {}; const currentSettings = {};
Object.keys(initialValues).forEach((key) => { Object.keys(initialValues).forEach((key) => {
const settingsKey = prefix ? `${prefix}_${key}` : key; const settingsKey = prefix ? `${prefix}_${key}` : key;
@ -10,7 +10,7 @@ export function settingsGet(initialValues, prefix = "") {
} }
export function settingsSave(currentValues, prefix = "") { export function settingsSave(currentValues, prefix = "") {
const raw = JSON.parse(localStorage.getItem("settings")) || {}; const raw = JSON.parse(localStorage.getItem("settings") || "{}") || {};
Object.keys(currentValues).forEach((key) => { Object.keys(currentValues).forEach((key) => {
const settingsKey = prefix ? `${prefix}_${key}` : key; const settingsKey = prefix ? `${prefix}_${key}` : key;
raw[settingsKey] = currentValues[key]; raw[settingsKey] = currentValues[key];

View file

@ -1,3 +1,4 @@
// @ts-nocheck
function getDefaultExportFromCjs (x) { function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
} }

View file

@ -1,3 +1,4 @@
// @ts-nocheck
import { import {
BufferGeometryLoader, BufferGeometryLoader,
FileLoader, FileLoader,

View file

@ -1,3 +1,4 @@
// @ts-nocheck
import { import {
DataTextureLoader, DataTextureLoader,
DataUtils, DataUtils,

View file

@ -1,3 +1,4 @@
// @ts-nocheck
import { import {
AmbientLight, AmbientLight,
AnimationClip, AnimationClip,

View file

@ -1,3 +1,4 @@
// @ts-nocheck
import { import {
AnimationClip, AnimationClip,
Bone, Bone,

View file

@ -1,3 +1,4 @@
// @ts-nocheck
import { import {
BufferGeometry, BufferGeometry,
FileLoader, FileLoader,

View file

@ -1,3 +1,4 @@
// @ts-nocheck
import { import {
EventDispatcher, EventDispatcher,
MOUSE, MOUSE,

View file

@ -1,3 +1,4 @@
// @ts-nocheck
import { import {
BufferAttribute, BufferAttribute,
BufferGeometry, BufferGeometry,

View file

@ -1,3 +1,4 @@
// @ts-nocheck
import { import {
Curve, Curve,
Vector3, Vector3,

View file

@ -1,3 +1,4 @@
// @ts-nocheck
import { import {
Vector3, Vector3,
Vector4 Vector4

View file

@ -1,3 +1,4 @@
// @ts-nocheck
/*! /*!
fflate - fast JavaScript compression/decompression fflate - fast JavaScript compression/decompression
<https://101arrowz.github.io/fflate> <https://101arrowz.github.io/fflate>

View file

@ -1,3 +1,4 @@
// @ts-nocheck
/** /**
* @license * @license
* Copyright 2010-2023 Three.js Authors * Copyright 2010-2023 Three.js Authors

View file

@ -1,3 +1,4 @@
// @ts-nocheck
import { import {
BufferAttribute, BufferAttribute,
BufferGeometry, BufferGeometry,

View file

@ -25,13 +25,13 @@ export async function init() {
selectedLanguage = "zh_tw"; selectedLanguage = "zh_tw";
break; break;
default: default:
const userLanguage = navigator.language.split("-")[0]; const userLanguage = navigator.language.split("-")[0] || "";
const idx = [ const idx = [
"az", "be", "bg", "ca", "cs", "da", "de", "el", "es", "et", "az", "be", "bg", "ca", "cs", "da", "de", "el", "es", "et",
"eu", "fi", "fr", "gl", "hr", "hu", "id", "is", "it", "ja", "eu", "fi", "fr", "gl", "hr", "hu", "id", "is", "it", "ja",
"ka", "ko", "lt", "lv", "mn", "nb", "nl", "pl", "pt", "ro", "ka", "ko", "lt", "lv", "mn", "nb", "nl", "pl", "pt", "ro",
"ru", "sk", "sl", "sr", "sv", "th", "tr", "uk", "vi", "zh", "ru", "sk", "sl", "sr", "sv", "th", "tr", "uk", "vi", "zh",
].indexOf(navigator.language.split("-")[0]); ].indexOf(userLanguage);
if (idx !== -1) { if (idx !== -1) {
selectedLanguage = userLanguage; selectedLanguage = userLanguage;
} }

View file

@ -3,7 +3,7 @@ import { toHref } from "../../lib/skeleton/router.js";
import rxjs, { effect, applyMutation, applyMutations, preventDefault, onClick } from "../../lib/rx.js"; import rxjs, { effect, applyMutation, applyMutations, preventDefault, onClick } from "../../lib/rx.js";
import ajax from "../../lib/ajax.js"; import ajax from "../../lib/ajax.js";
import { qs, qsa, safe } from "../../lib/dom.js"; import { qs, qsa, safe } from "../../lib/dom.js";
import { animate, slideYIn, transition } from "../../lib/animate.js"; import { animate, slideYIn, transition, opacityIn } from "../../lib/animate.js";
import { createForm } from "../../lib/form.js"; import { createForm } from "../../lib/form.js";
import { settings_get, settings_put } from "../../lib/settings.js"; import { settings_get, settings_put } from "../../lib/settings.js";
import t from "../../locales/index.js"; import t from "../../locales/index.js";
@ -48,7 +48,7 @@ export default async function(render) {
rxjs.map((conns) => conns.map(({ label, n }) => createElement(`<button data-current="${n}">${safe(label)}</button>`))), rxjs.map((conns) => conns.map(({ label, n }) => createElement(`<button data-current="${n}">${safe(label)}</button>`))),
applyMutations($nav, "appendChild"), applyMutations($nav, "appendChild"),
rxjs.tap((conns = []) => { if (conns.length > 1) $nav.classList.remove("hidden"); }), rxjs.tap((conns = []) => { if (conns.length > 1) $nav.classList.remove("hidden"); }),
rxjs.tap(() => animate($nav)), rxjs.tap(() => animate($nav, { time: 250, keyframes: opacityIn() })),
)); ));
// feature2: select a default storage among all the available ones // feature2: select a default storage among all the available ones

View file

@ -20,7 +20,7 @@ export default function(render) {
const setState = (newState) => state$.next(newState); const setState = (newState) => state$.next(newState);
effect(state$.asObservable().pipe(rxjs.mergeMap(({ step, ...state }) => { effect(state$.asObservable().pipe(rxjs.mergeMap(({ step, ...state }) => {
if (step === null) return verify(render, { shareID, setState }); if (step === null) return verify(render, { shareID, setState, body: null });
else if (step === "password") return ctrlPassword(render, { shareID, setState }); else if (step === "password") return ctrlPassword(render, { shareID, setState });
else if (step === "email") return ctrlEmail(render, { shareID, setState }); else if (step === "email") return ctrlEmail(render, { shareID, setState });
else if (step === "code") return ctrlEmailCodeVerification(render, { shareID, setState }); else if (step === "code") return ctrlEmailCodeVerification(render, { shareID, setState });
@ -81,7 +81,7 @@ function ctrlEmailCodeVerification(render, { shareID, setState }) {
return ctrlAbstract(render, { shareID, setState, $page }); return ctrlAbstract(render, { shareID, setState, $page });
} }
function verify(render, { shareID, setState, body }) { function verify(_, { shareID, setState, body }) {
return ajax({ return ajax({
method: "POST", method: "POST",
url: `api/share/${shareID}/proof`, url: `api/share/${shareID}/proof`,
@ -115,7 +115,7 @@ function ctrlAbstract(render, { shareID, setState, $page }) {
applyMutation(qs($page, "component-icon"), "setAttribute"), applyMutation(qs($page, "component-icon"), "setAttribute"),
// STEP2: attempt to login // STEP2: attempt to login
rxjs.map(() => ({ type: qs($page, "input").name, value: qs($page, "input").value })), rxjs.map(() => ({ type: qs($page, "input").name, value: qs($page, "input").value })),
rxjs.switchMap((creds) => verify(render, { body: creds, shareID, setState }).pipe(rxjs.catchError((err) => { rxjs.switchMap((creds) => verify(render, { shareID, body: creds, setState }).pipe(rxjs.catchError((err) => {
if (err instanceof AjaxError) { if (err instanceof AjaxError) {
switch (err.code()) { switch (err.code()) {
case "INTERNAL_SERVER_ERROR": case "INTERNAL_SERVER_ERROR":

View file

@ -55,7 +55,7 @@ export default WithShell(async function(render) {
)); ));
// feature: cleanup up the design when navbar is not there // feature: cleanup up the design when navbar is not there
effect(rxjs.of(new URL(location).searchParams.get("nav")).pipe( effect(rxjs.of(new URL(location.toString()).searchParams.get("nav")).pipe(
rxjs.filter((value) => value === "false"), rxjs.filter((value) => value === "false"),
rxjs.tap(() => { rxjs.tap(() => {
$page.parentElement.style.border = "none"; $page.parentElement.style.border = "none";

View file

@ -1,11 +1,11 @@
import { getSession } from "../../model/session.js"; import { getSession } from "../../model/session.js";
class ICache { class ICache {
async get(path) { throw new Error("NOT_IMPLEMENTED"); } async get() { throw new Error("NOT_IMPLEMENTED"); }
async store(path) { throw new Error("NOT_IMPLEMENTED"); } async store() { throw new Error("NOT_IMPLEMENTED"); }
async remove(path, exact = true) { throw new Error("NOT_IMPLEMENTED"); } async remove() { throw new Error("NOT_IMPLEMENTED"); }
async update(path, fn) { async update(path, fn) {
const data = await this.get(path); const data = await this.get(path);
@ -70,7 +70,7 @@ class IndexDBCache extends ICache {
request.onsuccess = (e) => { request.onsuccess = (e) => {
done(e.target.result); done(e.target.result);
}; };
request.onerror = (e) => err(new Error("INDEXEDDB_NOT_SUPPORTED")); request.onerror = () => err(new Error("INDEXEDDB_NOT_SUPPORTED"));
}); });
} }
@ -79,9 +79,9 @@ class IndexDBCache extends ICache {
const tx = db.transaction(this.FILE_PATH, "readonly"); const tx = db.transaction(this.FILE_PATH, "readonly");
const store = tx.objectStore(this.FILE_PATH); const store = tx.objectStore(this.FILE_PATH);
const query = store.get(this._key(path)); const query = store.get(this._key(path));
return await new Promise((done, error) => { return await new Promise((done) => {
query.onsuccess = (e) => done(query.result || null); query.onsuccess = (e) => done(query.result || null);
query.onerror = () => done(); query.onerror = () => done(null);
}); });
} }
@ -123,7 +123,7 @@ class IndexDBCache extends ICache {
cursor.continue(); cursor.continue();
return; return;
} }
done(); done(null);
}; };
request.onerror = err; request.onerror = err;
}); });
@ -131,7 +131,7 @@ class IndexDBCache extends ICache {
const req = store.delete(key); const req = store.delete(key);
return await new Promise((done, err) => { return await new Promise((done, err) => {
req.onsuccess = () => done(); req.onsuccess = () => done(null);
req.onerror = err; req.onerror = err;
}); });
} }

View file

@ -1,6 +1,7 @@
import { createElement, createRender, onDestroy } from "../../lib/skeleton/index.js"; import { createElement, createRender, onDestroy } from "../../lib/skeleton/index.js";
import { animate, slideYIn } from "../../lib/animate.js"; import { animate, slideYIn } from "../../lib/animate.js";
import rxjs, { effect, preventDefault } from "../../lib/rx.js"; import rxjs, { effect, preventDefault } from "../../lib/rx.js";
import assert from "../../lib/assert.js";
import { loadCSS } from "../../helpers/loader.js"; import { loadCSS } from "../../helpers/loader.js";
import { qs } from "../../lib/dom.js"; import { qs } from "../../lib/dom.js";
import { ApplicationError } from "../../lib/error.js"; import { ApplicationError } from "../../lib/error.js";
@ -175,7 +176,7 @@ export default async function(render) {
BLOCK_SIZE, COLUMN_PER_ROW, FILE_HEIGHT, BLOCK_SIZE, COLUMN_PER_ROW, FILE_HEIGHT,
MARGIN, MARGIN,
currentState, currentState,
height, setHeight, /* height, */ setHeight,
$list, $list,
}) => rxjs.fromEvent($page.closest(".scroll-y"), "scroll", { passive: true }).pipe( }) => rxjs.fromEvent($page.closest(".scroll-y"), "scroll", { passive: true }).pipe(
rxjs.map((e) => { rxjs.map((e) => {
@ -261,7 +262,10 @@ export default async function(render) {
rxjs.tap(() => clearSelection()), rxjs.tap(() => clearSelection()),
)); ));
effect(rxjs.fromEvent(window, "keydown").pipe( effect(rxjs.fromEvent(window, "keydown").pipe(
rxjs.filter((e) => e.key === "a" && (e.ctrlKey || e.metaKey) && (files$.value || []).length > 0 && document.activeElement.tagName !== "INPUT"), rxjs.filter((e) => e.key === "a" &&
(e.ctrlKey || e.metaKey) &&
(files$.value || []).length > 0 &&
assert.type(document.activeElement, window.HTMLElement).tagName !== "INPUT"),
preventDefault(), preventDefault(),
rxjs.tap(() => { rxjs.tap(() => {
clearSelection(); clearSelection();
@ -283,7 +287,7 @@ export default async function(render) {
}); });
}), }),
)); ));
effect(getSelection$().pipe(rxjs.tap((a) => { effect(getSelection$().pipe(rxjs.tap(() => {
for (const $thing of $page.querySelectorAll(".component_thing")) { for (const $thing of $page.querySelectorAll(".component_thing")) {
const checked = isSelected(parseInt($thing.getAttribute("data-n"))); const checked = isSelected(parseInt($thing.getAttribute("data-n")));
$thing.classList.add(checked ? "selected" : "not-selected"); $thing.classList.add(checked ? "selected" : "not-selected");
@ -310,7 +314,7 @@ function renderEmpty(render, base64Icon) {
<p class="label">${t("There is nothing here")}</p> <p class="label">${t("There is nothing here")}</p>
</div> </div>
`); `);
animate(render($page), { keyframes: slideYIn(5) }); animate(render($page), { time: 250, keyframes: slideYIn(5) });
} }
export function init() { export function init() {

View file

@ -66,7 +66,7 @@ export default async function(render) {
return null; return null;
}), }),
rxjs.filter((val) => val), rxjs.filter((val) => val),
rxjs.tap(async({ img, alt, targetName }) => { rxjs.tap(async({ img, alt }) => {
$icon.setAttribute("src", `data:image/svg+xml;base64,${img}`); $icon.setAttribute("src", `data:image/svg+xml;base64,${img}`);
$icon.setAttribute("alt", alt); $icon.setAttribute("alt", alt);
$input.value = ""; $input.value = "";

View file

@ -169,7 +169,7 @@ function componentLeft(render, { $scroll }) {
)); ));
effect(getSelection$().pipe( effect(getSelection$().pipe(
rxjs.filter((selections) => lengthSelection() > 1), rxjs.filter(() => lengthSelection() > 1),
rxjs.map(() => render(createFragment(` rxjs.map(() => render(createFragment(`
<a target="_blank" ${generateLinkAttributes(expandSelection())}><button data-action="download"> <a target="_blank" ${generateLinkAttributes(expandSelection())}><button data-action="download">
${t("Download")} ${t("Download")}
@ -184,7 +184,7 @@ function componentLeft(render, { $scroll }) {
return rxjs.from(componentDelete( return rxjs.from(componentDelete(
createModal(modalOpt), createModal(modalOpt),
"remove", "remove",
)).pipe(rxjs.mergeMap((val) => { )).pipe(rxjs.mergeMap(() => {
clearSelection(); clearSelection();
return rm(...paths); return rm(...paths);
})); }));
@ -217,8 +217,8 @@ function componentRight(render) {
default: throw new Error("NOT_IMPLEMENTED"); default: throw new Error("NOT_IMPLEMENTED");
} }
}; };
const defaultSort = () => { const defaultSort = (sort) => { // TODO
return `<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,${ICONS.SORT}" alt="sort" />`; return `<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,${ICONS.SORT}" alt="${sort}" />`;
}; };
effect(getSelection$().pipe( effect(getSelection$().pipe(
rxjs.filter((selections) => selections.length === 0), rxjs.filter((selections) => selections.length === 0),
@ -320,7 +320,7 @@ function componentRight(render) {
rxjs.filter((e) => (e.ctrlKey || e.metaKey) && e.key === "f"), rxjs.filter((e) => (e.ctrlKey || e.metaKey) && e.key === "f"),
preventDefault(), preventDefault(),
), ),
).pipe(rxjs.map(($el) => qs($page, "input").classList.contains("hidden"))), ).pipe(rxjs.map(() => qs($page, "input").classList.contains("hidden"))),
escape$.pipe(rxjs.mapTo(false)), escape$.pipe(rxjs.mapTo(false)),
).pipe( ).pipe(
rxjs.takeUntil(getSelection$().pipe(rxjs.skip(1))), rxjs.takeUntil(getSelection$().pipe(rxjs.skip(1))),
@ -379,12 +379,12 @@ function componentRight(render) {
effect(getSelection$().pipe( effect(getSelection$().pipe(
rxjs.filter((selections) => selections.length >= 1), rxjs.filter((selections) => selections.length >= 1),
rxjs.map((selections) => render(createFragment(` rxjs.map(() => render(createFragment(`
<button data-bind="clear"> <button data-bind="clear">
${lengthSelection()} <component-icon name="close"></component-icon> ${lengthSelection()} <component-icon name="close"></component-icon>
</button> </button>
`))), `))),
rxjs.mergeMap(($page) => onClick($page, `[data-bind="clear"]`).pipe( rxjs.mergeMap(($page) => onClick(qs($page, `[data-bind="clear"]`)).pipe(
rxjs.tap(() => clearSelection()), rxjs.tap(() => clearSelection()),
rxjs.takeUntil(getSelection$().pipe(rxjs.skip(1))), rxjs.takeUntil(getSelection$().pipe(rxjs.skip(1))),
)), )),

View file

@ -97,7 +97,7 @@
content: ")"; content: ")";
} }
.component_upload .stats_content .file_row .file_state { .component_upload .stats_content .file_row .file_state {
width: 40px; width: 50px;
padding: 3px 0; padding: 3px 0;
} }
.component_upload .stats_content .file_row .file_control { .component_upload .stats_content .file_row .file_control {

View file

@ -21,13 +21,13 @@ export default async function(render) {
effect(getPermission().pipe( effect(getPermission().pipe(
rxjs.filter(() => calculatePermission(currentPath(), "new-file")), rxjs.filter(() => calculatePermission(currentPath(), "new-file")),
rxjs.tap((a) => { rxjs.tap(() => {
const $page = createFragment(` const $page = createFragment(`
<div is="component_filezone"></div> <div is="component_filezone"></div>
<div is="component_upload_fab"></div> <div is="component_upload_fab"></div>
`); `);
componentFilezone(createRender($page.children[0]), { workers$ }); componentFilezone(createRender(assert.type($page.children[0], window.HTMLElement)), { workers$ });
componentUploadFAB(createRender($page.children[1]), { workers$ }); componentUploadFAB(createRender(assert.type($page.children[1], window.HTMLElement)), { workers$ });
render($page); render($page);
}), }),
)); ));
@ -61,7 +61,7 @@ function componentUploadFAB(render, { workers$ }) {
function componentFilezone(render, { workers$ }) { function componentFilezone(render, { workers$ }) {
const selector = `[data-bind="filemanager-children"]`; const selector = `[data-bind="filemanager-children"]`;
const $target = document.body.querySelector(selector); const $target = assert.type(document.body.querySelector(selector), window.HTMLElement);
$target.ondragenter = (e) => { $target.ondragenter = (e) => {
if (!isNativeFileUpload(e)) return; if (!isNativeFileUpload(e)) return;
@ -178,7 +178,7 @@ function componentUploadQueue(render, { workers$ }) {
if (new Date() - last <= 500) return; if (new Date() - last <= 500) return;
last = new Date(); last = new Date();
const speed = workersSpeed.reduce((acc, el) => acc + el, 0); const speed = workersSpeed.reduce((acc, el) => acc + el, 0);
const $speed = $page.firstElementChild.nextElementSibling.firstElementChild; const $speed = assert.type($page.firstElementChild.nextElementSibling.firstElementChild, window.HTMLElement);
$speed.textContent = formatSpeed(speed); $speed.textContent = formatSpeed(speed);
}; };
}(new Array(MAX_WORKERS).fill(0))); }(new Array(MAX_WORKERS).fill(0)));
@ -207,7 +207,7 @@ function componentUploadQueue(render, { workers$ }) {
$close.removeEventListener("click", cancel); $close.removeEventListener("click", cancel);
break; break;
case "error": case "error":
const $retry = $iconRetry.cloneNode(true); const $retry = assert.type($iconRetry.cloneNode(true), window.HTMLElement);
updateDOMGlobalTitle($page, t("Error")); updateDOMGlobalTitle($page, t("Error"));
updateDOMGlobalSpeed(nworker, 0); updateDOMGlobalSpeed(nworker, 0);
updateDOMTaskProgress($task, t("Error")); updateDOMTaskProgress($task, t("Error"));
@ -237,7 +237,7 @@ function componentUploadQueue(render, { workers$ }) {
} }
const $task = qs($page, `[data-path="${task.path}"]`); const $task = qs($page, `[data-path="${task.path}"]`);
const exec = task.exec({ const exec = task.exec({
error: (err) => updateDOMWithStatus($task, { status: "error", nworker }), error: () => updateDOMWithStatus($task, { status: "error", nworker, exec: null }),
progress: (progress) => updateDOMTaskProgress($task, formatPercent(progress)), progress: (progress) => updateDOMTaskProgress($task, formatPercent(progress)),
speed: (speed) => { speed: (speed) => {
updateDOMTaskSpeed($task, speed); updateDOMTaskSpeed($task, speed);
@ -251,7 +251,7 @@ function componentUploadQueue(render, { workers$ }) {
} catch (err) { } catch (err) {
updateDOMWithStatus($task, { exec, status: "error", nworker }); updateDOMWithStatus($task, { exec, status: "error", nworker });
} }
updateTotal.incrementCompleted(1); updateTotal.incrementCompleted();
task.done = true; task.done = true;
if (tasks.length === 0 && // no remaining tasks if (tasks.length === 0 && // no remaining tasks
@ -340,7 +340,7 @@ function workerImplFile({ error, progress, speed }) {
return; return;
} }
virtual.afterSuccess(); virtual.afterSuccess();
done(); done(null);
}; };
this.xhr.onerror = function(e) { this.xhr.onerror = function(e) {
err(new AjaxError("failed", e, "FAILED")); err(new AjaxError("failed", e, "FAILED"));
@ -360,7 +360,7 @@ function workerImplDirectory({ error, progress }) {
} }
cancel() { cancel() {
this.xhr.abort(); if (this.xhr instanceof XMLHttpRequest) this.xhr.abort();
} }
run({ virtual, path }) { run({ virtual, path }) {
@ -397,7 +397,7 @@ function workerImplDirectory({ error, progress }) {
return; return;
} }
virtual.afterSuccess(); virtual.afterSuccess();
done(); done(null);
}; };
this.xhr.send(null); this.xhr.send(null);
}); });
@ -418,7 +418,7 @@ async function processFiles(filelist) {
if (file.size % 4096 !== 0) { if (file.size % 4096 !== 0) {
return Promise.resolve("file"); return Promise.resolve("file");
} }
return new Promise((done, err) => { return new Promise((done) => {
const reader = new window.FileReader(); const reader = new window.FileReader();
const tid = setTimeout(() => reader.abort(), 1000); const tid = setTimeout(() => reader.abort(), 1000);
reader.onload = () => done("file"); reader.onload = () => done("file");
@ -460,7 +460,7 @@ async function processFiles(filelist) {
}; };
break; break;
default: default:
assert.fail(`NOT_SUPPORTED type="${type}"`, type); assert.fail(type, `NOT_SUPPORTED type="${type}"`);
} }
task.virtual.before(); task.virtual.before();
tasks.push(task); tasks.push(task);
@ -476,7 +476,7 @@ async function processItems(itemList) {
while (queue.length > 0) { while (queue.length > 0) {
const entry = queue.shift(); const entry = queue.shift();
const path = basepath + entry.fullPath.substring(1); const path = basepath + entry.fullPath.substring(1);
let task = null; let task = {};
if (entry === null) continue; if (entry === null) continue;
else if (entry.isFile) { else if (entry.isFile) {
const entrySize = await new Promise((done) => entry.getMetadata(({ size }) => done(size))); const entrySize = await new Promise((done) => entry.getMetadata(({ size }) => done(size)));

View file

@ -44,12 +44,12 @@ function renderDesktop(render, removeLabel) {
return ret.toPromise(); return ret.toPromise();
} }
function renderMobile(render, removeLabel) { function renderMobile(_, removeLabel) {
return new Promise((done) => { return new Promise((done) => {
const value = window.prompt(t("Confirm by typing") + ": " + removeLabel, ""); const value = window.prompt(t("Confirm by typing") + ": " + removeLabel, "");
if (value !== removeLabel) { if (value !== removeLabel) {
return; return;
} }
done(); done(null);
}); });
} }

View file

@ -48,7 +48,7 @@ function renderDesktop(render, filename) {
return ret.toPromise(); return ret.toPromise();
} }
function renderMobile(render, filename) { function renderMobile(_, filename) {
return new Promise((done) => { return new Promise((done) => {
const value = window.prompt(t("Rename as"), filename); const value = window.prompt(t("Rename as"), filename);
if (!value || value === filename) { if (!value || value === filename) {

View file

@ -42,7 +42,7 @@ export default function(render, { path }) {
}; };
// feature: select // feature: select
const toggle = (val) => rxjs.mergeMap(($button) => { const toggle = (val) => rxjs.mergeMap(() => {
state.form = {}; state.form = {};
role$.next(role$.value === val ? null : val); role$.next(role$.value === val ? null : val);
return rxjs.EMPTY; return rxjs.EMPTY;
@ -150,7 +150,7 @@ async function ctrlExistingShare(render, { load, remove, all, formLinks }) {
copyToClipboard(link); copyToClipboard(link);
notification.info(t("The link was copied in the clipboard")); notification.info(t("The link was copied in the clipboard"));
}); });
qs($share, `[alt="delete"]`).onclick = async(e) => { qs($share, `[alt="delete"]`).onclick = async() => {
$share.remove(); $share.remove();
length -= 1; length -= 1;
if (length === 0) $content.replaceChildren(createElement(` if (length === 0) $content.replaceChildren(createElement(`
@ -158,7 +158,7 @@ async function ctrlExistingShare(render, { load, remove, all, formLinks }) {
`)); `));
await remove(shareObj); await remove(shareObj);
}; };
qs($share, `[alt="edit"]`).onclick = (e) => load(shareObj); qs($share, `[alt="edit"]`).onclick = () => load(shareObj);
$fragment.appendChild($share); $fragment.appendChild($share);
}); });
$content.replaceChildren($fragment); $content.replaceChildren($fragment);
@ -229,7 +229,7 @@ async function ctrlCreateShare(render, { save, formState }) {
}; };
const tmpl = formTmpl({ const tmpl = formTmpl({
renderNode: () => createElement("<div></div>"), renderNode: () => createElement("<div></div>"),
renderLeaf: ({ format, label, type }) => { renderLeaf: ({ label, type }) => {
if (type !== "enable") return createElement("<label></label>"); if (type !== "enable") return createElement("<label></label>");
const title = const title =
label === "users_enable" label === "users_enable"
@ -273,7 +273,7 @@ async function ctrlCreateShare(render, { save, formState }) {
// sync editable custom link input with link id // sync editable custom link input with link id
effect(rxjs.fromEvent(qs($form, `[name="url"]`), "keyup").pipe(rxjs.tap((e) => { effect(rxjs.fromEvent(qs($form, `[name="url"]`), "keyup").pipe(rxjs.tap((e) => {
id = e.target.value.replaceAll(" ", "-").replace(new RegExp("[^A-Za-z\-]"), ""); id = e.target.value.replaceAll(" ", "-").replace(new RegExp("[^A-Za-z\-]"), "");
qs($form.closest(".component_share"), `input[name="create"]`).value = `${location.origin}${toHref("/s/" + id)}`; qs(assert.type($form.closest(".component_share"), window.HTMLElement), `input[name="create"]`).value = `${location.origin}${toHref("/s/" + id)}`;
}))); })));
// feature: create a shared link // feature: create a shared link
@ -281,7 +281,8 @@ async function ctrlCreateShare(render, { save, formState }) {
effect(onClick(qs($page, ".shared-link")).pipe( effect(onClick(qs($page, ".shared-link")).pipe(
rxjs.first(), rxjs.first(),
rxjs.switchMap(async() => { rxjs.switchMap(async() => {
const body = [...new FormData(document.querySelector(".component_share form"))].reduce((acc, [key, value]) => { const body = [...new FormData(assert.type(qs(document.body, ".component_share form"), window.HTMLFormElement))]
.reduce((acc, [key, value]) => {
if (value && key.slice(-7) !== "_enable") acc[key] = value; if (value && key.slice(-7) !== "_enable") acc[key] = value;
return acc; return acc;
}, { id }); }, { id });

View file

@ -76,7 +76,7 @@ export const mv = (from, to) => ajax({
handleError, handleError,
); );
export const save = (path) => rxjs.of(null).pipe(rxjs.delay(1000)); export const save = () => rxjs.of(null).pipe(rxjs.delay(1000));
export const ls = (path) => { export const ls = (path) => {
const lsFromCache = (path) => rxjs.from(fscache().get(path)); const lsFromCache = (path) => rxjs.from(fscache().get(path));
@ -101,7 +101,7 @@ export const ls = (path) => {
rxjs.merge( rxjs.merge(
rxjs.of(null), rxjs.of(null),
rxjs.merge(rxjs.of(null), rxjs.fromEvent(window, "keydown").pipe( // "r" shorcut rxjs.merge(rxjs.of(null), rxjs.fromEvent(window, "keydown").pipe( // "r" shorcut
rxjs.filter((e) => e.keyCode === 82 && document.activeElement.tagName !== "INPUT"), rxjs.filter((e) => e.keyCode === 82 && assert.type(document.activeElement, window.HTMLElement).tagName !== "INPUT"),
)).pipe(rxjs.switchMap(() => lsFromHttp(path))), )).pipe(rxjs.switchMap(() => lsFromHttp(path))),
), ),
).pipe( ).pipe(

View file

@ -27,11 +27,6 @@ const mutationFiles$ = new rxjs.BehaviorSubject({
// "/home/": [{ name: "test", fn: (file) => file, ...] // "/home/": [{ name: "test", fn: (file) => file, ...]
}); });
window.debug = () => {
console.log("VIRTUAL", JSON.stringify(virtualFiles$.value, null, 4));
console.log("MUTATION", JSON.stringify(mutationFiles$.value));
};
class IVirtualLayer { class IVirtualLayer {
before() { throw new Error("NOT_IMPLEMENTED"); } before() { throw new Error("NOT_IMPLEMENTED"); }
async afterSuccess() { throw new Error("NOT_IMPLEMENTED"); } async afterSuccess() { throw new Error("NOT_IMPLEMENTED"); }
@ -73,7 +68,7 @@ export function touch(path) {
hooks.mutation.emit({ op: "touch", path: basepath }); hooks.mutation.emit({ op: "touch", path: basepath });
} }
async afterError(err, caught) { async afterError() {
statePop(virtualFiles$, basepath, filename); statePop(virtualFiles$, basepath, filename);
return rxjs.of(fscache().remove(basepath)).pipe( return rxjs.of(fscache().remove(basepath)).pipe(
rxjs.mergeMap(() => rxjs.EMPTY), rxjs.mergeMap(() => rxjs.EMPTY),
@ -110,7 +105,7 @@ export function mkdir(path) {
hooks.mutation.emit({ op: "mkdir", path: basepath }); hooks.mutation.emit({ op: "mkdir", path: basepath });
} }
async afterError(err, caught) { async afterError() {
statePop(virtualFiles$, basepath, dirname); statePop(virtualFiles$, basepath, dirname);
return rxjs.of(fscache().remove(basepath)).pipe( return rxjs.of(fscache().remove(basepath)).pipe(
rxjs.mergeMap(() => rxjs.EMPTY), rxjs.mergeMap(() => rxjs.EMPTY),

View file

@ -56,7 +56,7 @@ export function createThing({
name = "", name = "",
type = "N/A", type = "N/A",
time = 0, time = 0,
path = null, path = "",
size = 0, size = 0,
loading = false, loading = false,
link = "", link = "",
@ -67,8 +67,7 @@ export function createThing({
}) { }) {
const [, ext] = formatFile(name); const [, ext] = formatFile(name);
const mime = TYPES.MIME[ext.toLowerCase()]; const mime = TYPES.MIME[ext.toLowerCase()];
const $thing = $tmpl.cloneNode(true); const $thing = assert.type($tmpl.cloneNode(true), window.HTMLElement);
assert.type($thing, window.HTMLElement);
// you might wonder why don't we use querySelector to nicely get the dom nodes? Well, // you might wonder why don't we use querySelector to nicely get the dom nodes? Well,
// we're in the hot path, better performance here is critical to get 60FPS. // we're in the hot path, better performance here is critical to get 60FPS.

View file

@ -0,0 +1,5 @@
interface Window {
WaveSurfer: {
create: (options: any) => any;
};
}

View file

@ -18,7 +18,7 @@ const STATUS_PLAYING = "PLAYING";
const STATUS_PAUSED = "PAUSED"; const STATUS_PAUSED = "PAUSED";
// const STATUS_BUFFERING = "BUFFERING"; // const STATUS_BUFFERING = "BUFFERING";
export default function(render, { mime }) { export default function(render) {
const $page = createElement(` const $page = createElement(`
<div class="component_audioplayer"> <div class="component_audioplayer">
<component-menubar></component-menubar> <component-menubar></component-menubar>

View file

@ -36,7 +36,7 @@ export default async function(render) {
const id = setInterval(() => { const id = setInterval(() => {
if (/download=yes/.test(document.cookie)) return; if (/download=yes/.test(document.cookie)) return;
clearInterval(id); clearInterval(id);
done(); done(null);
}, 200); }, 200);
})), })),
rxjs.tap(() => setLoading(false)), rxjs.tap(() => setLoading(false)),

View file

@ -0,0 +1,5 @@
interface Window {
ePub: {
Book: new (options: any) => any;
};
}

View file

@ -1,6 +1,7 @@
import { createElement, onDestroy } from "../../lib/skeleton/index.js"; import { createElement, onDestroy } from "../../lib/skeleton/index.js";
import rxjs, { effect } from "../../lib/rx.js"; import rxjs, { effect } from "../../lib/rx.js";
import { qs } from "../../lib/dom.js"; import { qs } from "../../lib/dom.js";
import assert from "../../lib/assert.js";
import { loadJS, loadCSS } from "../../helpers/loader.js"; import { loadJS, loadCSS } from "../../helpers/loader.js";
import { createLoader } from "../../components/loader.js"; import { createLoader } from "../../components/loader.js";
import ctrlError from "../ctrl_error.js"; import ctrlError from "../ctrl_error.js";
@ -46,7 +47,7 @@ export default function(render) {
book.open(getDownloadUrl()); book.open(getDownloadUrl());
await new Promise((done) => rendition.hooks.render.register(() => { await new Promise((done) => rendition.hooks.render.register(() => {
rendition$.next(rendition); rendition$.next(rendition);
done(); done(null);
})); }));
}), }),
removeLoader, removeLoader,
@ -59,7 +60,7 @@ export default function(render) {
effect(setup$.pipe( effect(setup$.pipe(
rxjs.mergeMap(() => rxjs.merge( rxjs.mergeMap(() => rxjs.merge(
rxjs.fromEvent(document, "keydown"), rxjs.fromEvent(document, "keydown"),
rendition$.pipe(rxjs.mergeMap(() => rxjs.fromEvent(qs(document, "iframe").contentDocument.body, "keydown"))), rendition$.pipe(rxjs.mergeMap(() => rxjs.fromEvent(assert.type(qs(document.body, "iframe"), window.HTMLElement).contentDocument.body, "keydown"))),
)), )),
rxjs.map((e) => { rxjs.map((e) => {
switch (e.code) { switch (e.code) {

View file

@ -0,0 +1,9 @@
interface Window {
CodeMirror: {
(element: HTMLElement, options: any): any;
__mode: string;
commands: {
save: (editor: any) => void;
};
};
}

View file

@ -85,7 +85,7 @@ export default async function(render, { acl$ }) {
editor.getWrapperElement().setAttribute("mode", mode); editor.getWrapperElement().setAttribute("mode", mode);
if (!("ontouchstart" in window)) editor.focus(); if (!("ontouchstart" in window)) editor.focus();
if (config["editor"] === "emacs") editor.addKeyMap({ if (config["editor"] === "emacs") editor.addKeyMap({
"Ctrl-X Ctrl-C": (cm) => window.history.back(), "Ctrl-X Ctrl-C": () => window.history.back(),
}); });
onDestroy(() => editor.clearHistory()); onDestroy(() => editor.clearHistory());
$dom.menubar().classList.remove("hidden"); $dom.menubar().classList.remove("hidden");
@ -133,7 +133,7 @@ export default async function(render, { acl$ }) {
// feature4: save // feature4: save
effect(setup$.pipe( effect(setup$.pipe(
rxjs.mergeMap((editor) => new rxjs.Observable((observer) => { rxjs.mergeMap(() => new rxjs.Observable((observer) => {
window.CodeMirror.commands.save = (cm) => observer.next(cm); window.CodeMirror.commands.save = (cm) => observer.next(cm);
})), })),
rxjs.mergeMap((cm) => { rxjs.mergeMap((cm) => {
@ -152,7 +152,7 @@ export default async function(render, { acl$ }) {
// feature5: save on exit // feature5: save on exit
effect(setup$.pipe( effect(setup$.pipe(
rxjs.tap((cm) => window.history.block = async(href) => { rxjs.tap((cm) => window.history.block = async() => {
const block = qs(document.body, "component-breadcrumb").hasAttribute("indicator"); const block = qs(document.body, "component-breadcrumb").hasAttribute("indicator");
if (block === false) return false; if (block === false) return false;
const userAction = await new Promise((done) => { const userAction = await new Promise((done) => {
@ -213,11 +213,13 @@ export function init() {
function loadMode(ext) { function loadMode(ext) {
let mode = "text"; let mode = "text";
let before = Promise.resolve(); let before = Promise.resolve(null);
if (ext === "org" || ext === "org_archive") { if (ext === "org" || ext === "org_archive") {
mode = "orgmode"; mode = "orgmode";
before = loadJS(import.meta.url, "../../lib/vendor/codemirror/addon/fold/xml-fold.js").then(() => loadJS(import.meta.url, "../../lib/vendor/codemirror/addon/edit/matchtags.js")); before = loadJS(import.meta.url, "../../lib/vendor/codemirror/addon/fold/xml-fold.js")
.then(() => loadJS(import.meta.url, "../../lib/vendor/codemirror/addon/edit/matchtags.js"))
.then(() => Promise.resolve(null));
} else if (ext === "sh") mode = "shell"; } else if (ext === "sh") mode = "shell";
else if (ext === "py") mode = "python"; else if (ext === "py") mode = "python";
else if (ext === "html" || ext === "htm") { else if (ext === "html" || ext === "htm") {
@ -226,7 +228,7 @@ function loadMode(ext) {
loadJS(import.meta.url, "../../lib/vendor/codemirror/mode/xml/xml.js"), loadJS(import.meta.url, "../../lib/vendor/codemirror/mode/xml/xml.js"),
loadJS(import.meta.url, "../../lib/vendor/codemirror/mode/javascript/javascript.js"), loadJS(import.meta.url, "../../lib/vendor/codemirror/mode/javascript/javascript.js"),
loadJS(import.meta.url, "../../lib/vendor/codemirror/mode/css/css.js"), loadJS(import.meta.url, "../../lib/vendor/codemirror/mode/css/css.js"),
]); ]).then(() => Promise.resolve(null));
} else if (ext === "css") mode = "css"; } else if (ext === "css") mode = "css";
else if (ext === "less" || ext === "scss" || ext === "sass") mode = "sass"; else if (ext === "less" || ext === "scss" || ext === "sass") mode = "sass";
else if (ext === "js" || ext === "json") mode = "javascript"; else if (ext === "js" || ext === "json") mode = "javascript";
@ -237,7 +239,7 @@ function loadMode(ext) {
loadJS(import.meta.url, "../../lib/vendor/codemirror/mode/xml/xml.js"), loadJS(import.meta.url, "../../lib/vendor/codemirror/mode/xml/xml.js"),
loadJS(import.meta.url, "../../lib/vendor/codemirror/mode/javascript/javascript.js"), loadJS(import.meta.url, "../../lib/vendor/codemirror/mode/javascript/javascript.js"),
loadJS(import.meta.url, "../../lib/vendor/codemirror/mode/css/css.js"), loadJS(import.meta.url, "../../lib/vendor/codemirror/mode/css/css.js"),
]); ]).then(() => Promise.resolve(null));
} else if (ext === "elm") mode = "elm"; } else if (ext === "elm") mode = "elm";
else if (ext === "erl") mode = "erlang"; else if (ext === "erl") mode = "erlang";
else if (ext === "go") mode = "go"; else if (ext === "go") mode = "go";
@ -248,14 +250,14 @@ function loadMode(ext) {
loadJS(import.meta.url, "../../lib/vendor/codemirror/mode/gfm/gfm.js"), loadJS(import.meta.url, "../../lib/vendor/codemirror/mode/gfm/gfm.js"),
loadJS(import.meta.url, "../../lib/vendor/codemirror/mode/yaml/yaml.js"), loadJS(import.meta.url, "../../lib/vendor/codemirror/mode/yaml/yaml.js"),
loadJS(import.meta.url, "../../lib/vendor/codemirror/addon/mode/overlay.js"), loadJS(import.meta.url, "../../lib/vendor/codemirror/addon/mode/overlay.js"),
]); ]).then(() => Promise.resolve(null));
} else if (ext === "pl" || ext === "pm") mode = "perl"; } else if (ext === "pl" || ext === "pm") mode = "perl";
else if (ext === "clj") mode = "clojure"; else if (ext === "clj") mode = "clojure";
else if (ext === "el" || ext === "lisp" || ext === "cl" || else if (ext === "el" || ext === "lisp" || ext === "cl" ||
ext === "emacs") mode = "commonlisp"; ext === "emacs") mode = "commonlisp";
else if (ext === "dockerfile") { else if (ext === "dockerfile") {
mode = "dockerfile"; mode = "dockerfile";
before = loadJS(import.meta.url, "../../lib/vendor/codemirror/addon/mode/simple.js"); before = loadJS(import.meta.url, "../../lib/vendor/codemirror/addon/mode/simple.js").then(() => Promise.resolve(null));
} else if (ext === "R") mode = "r"; } else if (ext === "R") mode = "r";
else if (ext === "makefile") mode = "cmake"; else if (ext === "makefile") mode = "cmake";
else if (ext === "rb") mode = "ruby"; else if (ext === "rb") mode = "ruby";
@ -275,7 +277,7 @@ function loadMode(ext) {
return before.then(() => loadJS(import.meta.url, `./application_editor/${mode}.js`, { type: "module" })) return before.then(() => loadJS(import.meta.url, `./application_editor/${mode}.js`, { type: "module" }))
.catch(() => loadJS(import.meta.url, "./application_editor/text.js", { type: "module" })) .catch(() => loadJS(import.meta.url, "./application_editor/text.js", { type: "module" }))
.then((module) => Promise.resolve(mode)); .then(() => Promise.resolve(mode));
} }
function loadKeybinding(editor) { function loadKeybinding(editor) {

View file

@ -78,7 +78,7 @@ export default function(render) {
), ),
).pipe( ).pipe(
rxjs.map((originalState) => { rxjs.map((originalState) => {
const smod = (key, value) => value || undefined; const smod = (_, value) => value || undefined;
return JSON.stringify(formObjToJSON(originalState), smod) !== JSON.stringify(formState(), smod); return JSON.stringify(formObjToJSON(originalState), smod) !== JSON.stringify(formState(), smod);
}), }),
rxjs.mergeMap(async(isSaveButtonVisible) => { rxjs.mergeMap(async(isSaveButtonVisible) => {

View file

@ -1,5 +1,6 @@
import { createElement } from "../../lib/skeleton/index.js"; import { createElement } from "../../lib/skeleton/index.js";
import rxjs, { effect } from "../../lib/rx.js"; import rxjs, { effect } from "../../lib/rx.js";
import assert from "../../lib/assert.js";
export default function(render) { export default function(render) {
const $component = createElement(` const $component = createElement(`
@ -9,6 +10,6 @@ export default function(render) {
effect(rxjs.fromEvent(document, "keypress").pipe( effect(rxjs.fromEvent(document, "keypress").pipe(
rxjs.filter((e) => e.key === "i"), rxjs.filter((e) => e.key === "i"),
rxjs.tap(() => $component.parentElement.classList.toggle("open")), rxjs.tap(() => assert.type($component.parentElement, window.HTMLElement).classList.toggle("open")),
)); ));
} }

View file

@ -0,0 +1,11 @@
interface Window {
pdfjsLib: {
getDocument: (url: string) => { promise: Promise<any> };
GlobalWorkerOptions: {
workerSrc: string;
};
// Add other properties and methods of pdfjsLib as needed
};
env?: string;
chrome: object;
}

View file

@ -4,9 +4,8 @@ import { animate, slideYIn } from "../../lib/animate.js";
import { loadCSS, loadJS } from "../../helpers/loader.js"; import { loadCSS, loadJS } from "../../helpers/loader.js";
import { qs, qsa } from "../../lib/dom.js"; import { qs, qsa } from "../../lib/dom.js";
import { settings_get, settings_put } from "../../lib/settings.js"; import { settings_get, settings_put } from "../../lib/settings.js";
import assert from "../../lib/assert.js";
import { ApplicationError } from "../../lib/error.js"; import { ApplicationError } from "../../lib/error.js";
import assert from "../../lib/assert.js";
import Hls from "../../lib/vendor/hlsjs/hls.js"; import Hls from "../../lib/vendor/hlsjs/hls.js";
import ctrlError from "../ctrl_error.js"; import ctrlError from "../ctrl_error.js";
@ -170,14 +169,14 @@ export default function(render, { mime }) {
$video.appendChild($source); $video.appendChild($source);
return [{ ...sources[i], type: "video/mp4" }]; return [{ ...sources[i], type: "video/mp4" }];
} }
hls.loadSource(sources[i].src, sources[i].type); hls.loadSource(sources[i].src);
} }
hls.attachMedia($video); hls.attachMedia($video);
return sources; return sources;
}), }),
rxjs.mergeMap((sources) => rxjs.merge( rxjs.mergeMap((sources) => rxjs.merge(
rxjs.fromEvent($video, "loadeddata"), rxjs.fromEvent($video, "loadeddata"),
...[...qsa($page, "source")].map(($source) => rxjs.fromEvent($source, "error").pipe(rxjs.tap((err) => { ...[...qsa($page, "source")].map(($source) => rxjs.fromEvent($source, "error").pipe(rxjs.tap(() => {
throw new ApplicationError("NOT_SUPPORTED", JSON.stringify({ mime, sources }, null, 2)); throw new ApplicationError("NOT_SUPPORTED", JSON.stringify({ mime, sources }, null, 2));
}))), }))),
)), )),
@ -361,7 +360,7 @@ export default function(render, { mime }) {
if ($video.buffered.length !== $container.children.length) { if ($video.buffered.length !== $container.children.length) {
$container.innerHTML = ""; $container.innerHTML = "";
const $fragment = document.createDocumentFragment(); const $fragment = document.createDocumentFragment();
Array.apply(null, { length: $video.buffered.length }) Array.from({ length: $video.buffered.length })
.map(() => $fragment.appendChild(createElement(` .map(() => $fragment.appendChild(createElement(`
<div className="progress-buffer" style=""></div> <div className="progress-buffer" style=""></div>
`))); `)));

View file

@ -1,7 +1,7 @@
export function formatTimecode(seconds) { export function formatTimecode(seconds) {
return String(parseInt(seconds / 60)).padStart(2, "0") + return String(Math.floor(seconds / 60)).padStart(2, "0") +
":" + ":" +
String(parseInt(seconds % 60)).padStart(2, "0"); String(Math.floor(seconds % 60)).padStart(2, "0");
} }
// TODO: abstract setVolume, setSeek and setStatus // TODO: abstract setVolume, setSeek and setStatus

View file

@ -20,12 +20,13 @@ export default class ComponentMenubar extends window.HTMLElement {
</div> </div>
`; `;
if (new URLSearchParams(location.search).get("nav") === "false") { if (new URLSearchParams(location.search).get("nav") === "false") {
this.firstElementChild.classList.add("inherit-width"); const $container = assert.type(this.firstElementChild, window.HTMLElement);
$container.classList.add("inherit-width");
} }
} }
async connectedCallback() { async connectedCallback() {
const $title = this.querySelector(".titlebar"); const $title = assert.type(this.querySelector(".titlebar"), window.HTMLElement);
this.timeoutID = setTimeout(() => animate($title, { this.timeoutID = setTimeout(() => animate($title, {
time: 250, time: 250,
keyframes: slideYIn(2), keyframes: slideYIn(2),
@ -38,7 +39,7 @@ export default class ComponentMenubar extends window.HTMLElement {
} }
render(buttons) { render(buttons) {
const $item = this.querySelector(".action-item"); const $item = assert.type(this.querySelector(".action-item"), window.HTMLElement);
for (let i=buttons.length-1; i>=0; i--) { for (let i=buttons.length-1; i>=0; i--) {
$item.appendChild(buttons[i]); $item.appendChild(buttons[i]);
} }

12
public/global.d.ts vendored Normal file
View file

@ -0,0 +1,12 @@
interface Window {
overrides: {
[key: string]: any;
"xdg-open"?: (mime: string) => void;
};
CONFIG: Config;
}
interface Config {
[key: string]: any;
thumbnailer: string[];
}

View file

@ -25,15 +25,16 @@
"target": "es2022", "target": "es2022",
"typeRoots": [] "typeRoots": []
}, },
"include": ["assets/boot/*.js", "assets/pages/*.js"], "include": [
"assets/boot/*.js",
"assets/pages/*.js",
"global.d.ts"
],
"exclude": [ "exclude": [
"**/*.test.js", "**/*.test.js",
"assets/worker/sw_cache.js", "assets/worker/sw_cache.js",
"coverage", "coverage",
"vite.config.js", "vite.config.js",
"jest.setup.js", "jest.setup.js"
"assets/lib/vendor/**",
"assets/lib/vendor/hlsjs/hls.js",
"assets/lib/vendor/three/*.js"
] ]
} }