mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-06 16:32:31 +01:00
chore (maintenance): tsconfig setup
This commit is contained in:
parent
28b645fb19
commit
d6613c4452
67 changed files with 522 additions and 416 deletions
|
|
@ -11,7 +11,7 @@ export default async function main() {
|
||||||
window.dispatchEvent(new window.Event("pagechange"));
|
window.dispatchEvent(new window.Event("pagechange"));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
const msg = window.navigator.onLine === false ? "OFFLINE" : (err.message || "CAN'T LOAD");
|
const msg = window.navigator.onLine === false ? "OFFLINE" : (err instanceof Error && err.message) || "CAN'T LOAD";
|
||||||
report("boot::" + msg, err, location.href);
|
report("boot::" + msg, err, location.href);
|
||||||
$error(msg);
|
$error(msg);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ export default async function main() {
|
||||||
setup_translation(),
|
setup_translation(),
|
||||||
setup_xdg_open(),
|
setup_xdg_open(),
|
||||||
setup_device(),
|
setup_device(),
|
||||||
// setup_sw(), // TODO
|
|
||||||
setup_blue_death_screen(),
|
setup_blue_death_screen(),
|
||||||
setup_history(),
|
setup_history(),
|
||||||
setup_polyfill(),
|
setup_polyfill(),
|
||||||
|
|
@ -26,7 +25,7 @@ export default async function main() {
|
||||||
window.dispatchEvent(new window.Event("pagechange"));
|
window.dispatchEvent(new window.Event("pagechange"));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
const msg = window.navigator.onLine === false ? "OFFLINE" : (err.message || "CAN'T LOAD");
|
const msg = window.navigator.onLine === false ? "OFFLINE" : (err instanceof Error && err.message) || "CAN'T LOAD";
|
||||||
report(msg, err, location.href);
|
report(msg, err, location.href);
|
||||||
$error(msg);
|
$error(msg);
|
||||||
}
|
}
|
||||||
|
|
@ -69,23 +68,6 @@ async function setup_device() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setup_sw() { // eslint-disable-line no-unused-vars
|
|
||||||
if (!("serviceWorker" in window.navigator)) return;
|
|
||||||
|
|
||||||
if (window.navigator.userAgent.indexOf("Mozilla/") !== -1 &&
|
|
||||||
window.navigator.userAgent.indexOf("Firefox/") !== -1 &&
|
|
||||||
window.navigator.userAgent.indexOf("Gecko/") !== -1) {
|
|
||||||
// Firefox was acting weird with service worker so we disabled it
|
|
||||||
// see: https://github.com/mickael-kerjean/filestash/issues/255
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await window.navigator.serviceWorker.register("/sw_cache.js");
|
|
||||||
} catch (err) {
|
|
||||||
report("ServiceWorker registration failed", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setup_blue_death_screen() {
|
async function setup_blue_death_screen() {
|
||||||
window.onerror = function(msg, url, lineNo, colNo, error) {
|
window.onerror = function(msg, url, lineNo, colNo, error) {
|
||||||
report(msg, error, url, lineNo, colNo);
|
report(msg, error, url, lineNo, colNo);
|
||||||
|
|
@ -116,7 +98,7 @@ async function setup_history() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setup_title() {
|
async function setup_title() {
|
||||||
document.title = window.CONFIG.name || "Filestash";
|
document.title = window.CONFIG["name"] || "Filestash";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setup_polyfill() {
|
async function setup_polyfill() {
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@ const mv = (from, to) => withVirtualLayer(
|
||||||
mvVL(from, to),
|
mvVL(from, to),
|
||||||
);
|
);
|
||||||
|
|
||||||
class ComponentBreadcrumb extends window.HTMLElement {
|
class ComponentBreadcrumb extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
if (new window.URL(location.href).searchParams.get("nav") === "false") {
|
if (new URL(location.href).searchParams.get("nav") === "false") {
|
||||||
this.disabled = true;
|
this.disabled = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -65,15 +65,15 @@ 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 = assert.type(this.querySelector(`.component_path-element.n${n}`), window.HTMLElement);
|
const $chunk = assert.type(this.querySelector(`.component_path-element.n${n}`), HTMLElement);
|
||||||
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
|
||||||
assert.type(this.querySelector(`[data-bind="path"]`), window.HTMLElement).innerHTML = pathChunks.map((chunk, idx) => {
|
assert.type(this.querySelector(`[data-bind="path"]`), 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) => {
|
||||||
if (highlight === true && word.length > 30) {
|
if (highlight === true && word.length > 30) {
|
||||||
|
|
@ -130,7 +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 = assert.type(this.querySelector(`.component_path-element.n${n}`), window.HTMLElement);
|
const $chunk = assert.type(this.querySelector(`.component_path-element.n${n}`), HTMLElement);
|
||||||
await animate($chunk, { time: 100, keyframes: slideYIn(-5) });
|
await animate($chunk, { time: 100, keyframes: slideYIn(-5) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -140,8 +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;
|
||||||
|
|
||||||
let $indicator = assert.type(this.querySelector(`[data-bind="path"]`), window.HTMLElement);
|
let $indicator = assert.type(this.querySelector(`[data-bind="path"]`), HTMLElement);
|
||||||
$indicator = assert.type($indicator.lastChild.querySelector("span"), window.HTMLElement);
|
$indicator = assert.type($indicator.lastChild.querySelector("span"), HTMLElement);
|
||||||
|
|
||||||
if (state) {
|
if (state) {
|
||||||
$indicator.style.opacity = 1;
|
$indicator.style.opacity = 1;
|
||||||
|
|
@ -162,8 +162,9 @@ class ComponentBreadcrumb extends window.HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
setupDragDropTarget() {
|
setupDragDropTarget() {
|
||||||
this.querySelectorAll("a.label").forEach(($folder) => {
|
this.querySelectorAll("a.label").forEach(($elmnt) => {
|
||||||
const $path = $folder.closest(".component_path-element");
|
const $folder = assert.type($elmnt, HTMLElement);
|
||||||
|
const $path = assert.truthy($folder.closest(".component_path-element"));
|
||||||
$folder.ondrop = async(e) => {
|
$folder.ondrop = async(e) => {
|
||||||
$path.classList.remove("highlight");
|
$path.classList.remove("highlight");
|
||||||
const from = e.dataTransfer.getData("path");
|
const from = e.dataTransfer.getData("path");
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ export default function(ctrl) {
|
||||||
effect(rxjs.fromEvent(window, "keydown").pipe(
|
effect(rxjs.fromEvent(window, "keydown").pipe(
|
||||||
rxjs.filter((e) => regexStartFiles.test(fromHref(location.pathname)) &&
|
rxjs.filter((e) => regexStartFiles.test(fromHref(location.pathname)) &&
|
||||||
e.keyCode === 8 &&
|
e.keyCode === 8 &&
|
||||||
assert.type(document.activeElement, window.HTMLElement).nodeName !== "INPUT"), // backspace in filemanager
|
assert.type(document.activeElement, 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();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { animate, slideYIn } from "../lib/animate.js";
|
||||||
import assert from "../lib/assert.js";
|
import assert from "../lib/assert.js";
|
||||||
import { loadCSS } from "../helpers/loader.js";
|
import { loadCSS } from "../helpers/loader.js";
|
||||||
|
|
||||||
export default class ComponentDropdown extends HTMLDivElement {
|
export default class ComponentDropdown extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.render();
|
this.render();
|
||||||
|
|
@ -78,9 +78,9 @@ export default class ComponentDropdown extends HTMLDivElement {
|
||||||
`));
|
`));
|
||||||
|
|
||||||
const setActive = () => this.classList.toggle("active");
|
const setActive = () => this.classList.toggle("active");
|
||||||
assert.type(this.querySelector(".dropdown_button"), window.HTMLElement).onclick = () => {
|
assert.type(this.querySelector(".dropdown_button"), HTMLElement).onclick = () => {
|
||||||
setActive();
|
setActive();
|
||||||
animate(assert.type(this.querySelector(".dropdown_container"), window.HTMLElement), {
|
animate(assert.type(this.querySelector(".dropdown_container"), HTMLElement), {
|
||||||
time: 100,
|
time: 100,
|
||||||
keyframes: slideYIn(2),
|
keyframes: slideYIn(2),
|
||||||
});
|
});
|
||||||
|
|
@ -88,4 +88,4 @@ export default class ComponentDropdown extends HTMLDivElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("component-dropdown", ComponentDropdown, { extends: "div" });
|
customElements.define("component-dropdown", ComponentDropdown);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { loadCSS } from "../helpers/loader.js";
|
import { loadCSS } from "../helpers/loader.js";
|
||||||
import assert from "../lib/assert.js";
|
import assert from "../lib/assert.js";
|
||||||
|
|
||||||
export default class ComponentFab extends window.HTMLButtonElement {
|
export default class ComponentFab extends HTMLButtonElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.innerHTML = `<div class="content"></div>`;
|
this.innerHTML = `<div class="content"></div>`;
|
||||||
|
|
@ -10,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");
|
||||||
assert.type(this.querySelector(".content"), window.HTMLElement).replaceChildren($icon);
|
assert.type(this.querySelector(".content"), HTMLElement).replaceChildren($icon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ export function $renderInput(options = {}) {
|
||||||
class="component_input"
|
class="component_input"
|
||||||
/>
|
/>
|
||||||
`);
|
`);
|
||||||
if (!($input instanceof window.HTMLInputElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
if (!($input instanceof HTMLInputElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
||||||
else if (value) $input.value = value;
|
else if (value) $input.value = value;
|
||||||
attrs.map((setAttribute) => setAttribute($input));
|
attrs.map((setAttribute) => setAttribute($input));
|
||||||
|
|
||||||
|
|
@ -135,7 +135,7 @@ export function $renderInput(options = {}) {
|
||||||
class="component_input"
|
class="component_input"
|
||||||
/>
|
/>
|
||||||
`);
|
`);
|
||||||
if (!($input instanceof window.HTMLInputElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
if (!($input instanceof HTMLInputElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
||||||
else if (value) $input.value = value;
|
else if (value) $input.value = value;
|
||||||
attrs.map((setAttribute) => setAttribute($input));
|
attrs.map((setAttribute) => setAttribute($input));
|
||||||
return $input;
|
return $input;
|
||||||
|
|
@ -151,14 +151,14 @@ export function $renderInput(options = {}) {
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
const $input = $div.querySelector("input");
|
const $input = $div.querySelector("input");
|
||||||
if (!($input instanceof window.HTMLInputElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
if (!($input instanceof HTMLInputElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
||||||
else if (value) $input.value = value;
|
else if (value) $input.value = value;
|
||||||
attrs.map((setAttribute) => setAttribute($input));
|
attrs.map((setAttribute) => setAttribute($input));
|
||||||
|
|
||||||
const $icon = $div.querySelector("component-icon");
|
const $icon = $div.querySelector("component-icon");
|
||||||
if ($icon instanceof window.HTMLElement) {
|
if ($icon instanceof HTMLElement) {
|
||||||
$icon.onclick = function(e) {
|
$icon.onclick = function(e) {
|
||||||
if (!(e.target instanceof window.HTMLElement)) return;
|
if (!(e.target instanceof HTMLElement)) return;
|
||||||
const $input = e.target?.parentElement?.previousElementSibling;
|
const $input = e.target?.parentElement?.previousElementSibling;
|
||||||
if (!$input) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
if (!$input) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
||||||
if ($input.getAttribute("type") === "password") $input.setAttribute("type", "text");
|
if ($input.getAttribute("type") === "password") $input.setAttribute("type", "text");
|
||||||
|
|
@ -174,7 +174,7 @@ export function $renderInput(options = {}) {
|
||||||
rows="8"
|
rows="8"
|
||||||
></textarea>
|
></textarea>
|
||||||
`);
|
`);
|
||||||
if (!($textarea instanceof window.HTMLTextAreaElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
if (!($textarea instanceof HTMLTextAreaElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
||||||
else if (value) $textarea.value = value;
|
else if (value) $textarea.value = value;
|
||||||
attrs.map((setAttribute) => setAttribute($textarea));
|
attrs.map((setAttribute) => setAttribute($textarea));
|
||||||
return $textarea;
|
return $textarea;
|
||||||
|
|
@ -187,7 +187,7 @@ export function $renderInput(options = {}) {
|
||||||
readonly
|
readonly
|
||||||
/>
|
/>
|
||||||
`);
|
`);
|
||||||
if (!($input instanceof window.HTMLInputElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
if (!($input instanceof HTMLInputElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
||||||
else if (value) $input.value = value;
|
else if (value) $input.value = value;
|
||||||
attrs.map((setAttribute) => setAttribute($input));
|
attrs.map((setAttribute) => setAttribute($input));
|
||||||
return $input;
|
return $input;
|
||||||
|
|
@ -196,7 +196,7 @@ export function $renderInput(options = {}) {
|
||||||
const $input = createElement(`
|
const $input = createElement(`
|
||||||
<input type="hidden" />
|
<input type="hidden" />
|
||||||
`);
|
`);
|
||||||
if (!($input instanceof window.HTMLInputElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
if (!($input instanceof HTMLInputElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
||||||
else if (value) $input.value = value;
|
else if (value) $input.value = value;
|
||||||
$input.setAttribute("name", path.join("."));
|
$input.setAttribute("name", path.join("."));
|
||||||
return $input;
|
return $input;
|
||||||
|
|
@ -219,7 +219,7 @@ export function $renderInput(options = {}) {
|
||||||
const $select = createElement(`
|
const $select = createElement(`
|
||||||
<select class="component_select"></select>
|
<select class="component_select"></select>
|
||||||
`);
|
`);
|
||||||
if (!($select instanceof window.HTMLSelectElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
if (!($select instanceof HTMLSelectElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
||||||
else if (value) $select.value = value || props.default;
|
else if (value) $select.value = value || props.default;
|
||||||
attrs.map((setAttribute) => setAttribute($select));
|
attrs.map((setAttribute) => setAttribute($select));
|
||||||
(options || []).forEach((name) => {
|
(options || []).forEach((name) => {
|
||||||
|
|
@ -242,7 +242,7 @@ export function $renderInput(options = {}) {
|
||||||
class="component_input"
|
class="component_input"
|
||||||
/>
|
/>
|
||||||
`);
|
`);
|
||||||
if (!($input instanceof window.HTMLInputElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
if (!($input instanceof HTMLInputElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
||||||
else if (value) $input.value = value;
|
else if (value) $input.value = value;
|
||||||
attrs.map((setAttribute) => setAttribute($input));
|
attrs.map((setAttribute) => setAttribute($input));
|
||||||
return $input;
|
return $input;
|
||||||
|
|
@ -254,7 +254,7 @@ export function $renderInput(options = {}) {
|
||||||
class="component_input"
|
class="component_input"
|
||||||
/>
|
/>
|
||||||
`);
|
`);
|
||||||
if (!($input instanceof window.HTMLInputElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
if (!($input instanceof HTMLInputElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: missing input");
|
||||||
else if (value) $input.value = value;
|
else if (value) $input.value = value;
|
||||||
attrs.map((setAttribute) => setAttribute($input));
|
attrs.map((setAttribute) => setAttribute($input));
|
||||||
return $input;
|
return $input;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
class Icon extends window.HTMLElement {
|
class Icon extends HTMLElement {
|
||||||
static get observedAttributes() {
|
static get observedAttributes() {
|
||||||
return ["name"];
|
return ["name"];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@ import { createElement, onDestroy } from "../lib/skeleton/index.js";
|
||||||
import rxjs from "../lib/rx.js";
|
import rxjs from "../lib/rx.js";
|
||||||
import { animate, opacityIn } from "../lib/animate.js";
|
import { animate, opacityIn } from "../lib/animate.js";
|
||||||
|
|
||||||
class Loader extends window.HTMLElement {
|
class Loader extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.timeout = window.setTimeout(() => {
|
this.timeout = setTimeout(() => {
|
||||||
this.innerHTML = this.render({
|
this.innerHTML = this.render({
|
||||||
inline: this.hasAttribute("inlined"),
|
inline: this.hasAttribute("inlined"),
|
||||||
});
|
});
|
||||||
|
|
@ -13,7 +13,7 @@ class Loader extends window.HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
window.clearTimeout(this.timeout);
|
clearTimeout(this.timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
render({ inline }) {
|
render({ inline }) {
|
||||||
|
|
@ -61,7 +61,7 @@ export function createLoader($parent, opts = {}) {
|
||||||
<component-icon name="loading"></component-icon>
|
<component-icon name="loading"></component-icon>
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
const id = window.setTimeout(() => {
|
const id = setTimeout(() => {
|
||||||
append($icon);
|
append($icon);
|
||||||
animate($icon, { time: 750, keyframes: opacityIn() });
|
animate($icon, { time: 750, keyframes: opacityIn() });
|
||||||
}, wait);
|
}, wait);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { qs, qsa } from "../lib/dom.js";
|
||||||
import { loadCSS } from "../helpers/loader.js";
|
import { loadCSS } from "../helpers/loader.js";
|
||||||
|
|
||||||
export function createModal(opts) {
|
export function createModal(opts) {
|
||||||
const $dom = assert.type(qs(document.body, "component-modal"), window.HTMLElement);
|
const $dom = assert.type(qs(document.body, "component-modal"), 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 });
|
||||||
|
|
@ -32,7 +32,7 @@ const $modal = createElement(`
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
class ModalComponent extends window.HTMLElement {
|
class ModalComponent extends HTMLElement {
|
||||||
async connectedCallback() {
|
async connectedCallback() {
|
||||||
await loadCSS(import.meta.url, "./modal.css");
|
await loadCSS(import.meta.url, "./modal.css");
|
||||||
}
|
}
|
||||||
|
|
@ -123,7 +123,7 @@ class ModalComponent extends window.HTMLElement {
|
||||||
let size = targetHeight;
|
let size = targetHeight;
|
||||||
if (size === null) {
|
if (size === null) {
|
||||||
const $box = document.querySelector("#modal-box > div");
|
const $box = document.querySelector("#modal-box > div");
|
||||||
if ($box instanceof window.HTMLElement) size = $box.offsetHeight;
|
if ($box instanceof HTMLElement) size = $box.offsetHeight;
|
||||||
}
|
}
|
||||||
size = Math.round((document.body.offsetHeight - size) / 2);
|
size = Math.round((document.body.offsetHeight - size) / 2);
|
||||||
if (size < 0) return 0;
|
if (size < 0) return 0;
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ const createNotification = async(msg, type) => createElement(`
|
||||||
</span>
|
</span>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
class NotificationComponent extends window.HTMLElement {
|
class NotificationComponent extends HTMLElement {
|
||||||
buffer = [];
|
buffer = [];
|
||||||
|
|
||||||
async connectedCallback() {
|
async connectedCallback() {
|
||||||
|
|
@ -28,8 +28,8 @@ class NotificationComponent extends window.HTMLElement {
|
||||||
this.buffer.push({ message, type });
|
this.buffer.push({ message, type });
|
||||||
if (this.buffer.length !== 1) {
|
if (this.buffer.length !== 1) {
|
||||||
const $close = this.querySelector(".close");
|
const $close = this.querySelector(".close");
|
||||||
if (!($close instanceof window.HTMLElement) || !$close.onclick) return;
|
if (!($close instanceof HTMLElement) || !$close.onclick) return;
|
||||||
$close.onclick(new window.MouseEvent("mousedown"));
|
$close.onclick(new MouseEvent("mousedown"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this.run();
|
await this.run();
|
||||||
|
|
@ -46,16 +46,16 @@ class NotificationComponent extends window.HTMLElement {
|
||||||
});
|
});
|
||||||
const ids = [];
|
const ids = [];
|
||||||
await Promise.race([
|
await Promise.race([
|
||||||
new Promise((done) => ids.push(window.setTimeout(() => {
|
new Promise((done) => ids.push(setTimeout(() => {
|
||||||
done(new window.MouseEvent("mousedown"));
|
done(new MouseEvent("mousedown"));
|
||||||
}, this.buffer.length === 1 ? 8000 : 800))),
|
}, this.buffer.length === 1 ? 8000 : 800))),
|
||||||
new Promise((done) => ids.push(window.setTimeout(() => {
|
new Promise((done) => ids.push(setTimeout(() => {
|
||||||
const $close = $notification.querySelector(".close");
|
const $close = $notification.querySelector(".close");
|
||||||
if (!($close instanceof window.HTMLElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: notification close button missing");
|
if (!($close instanceof HTMLElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: notification close button missing");
|
||||||
$close.onclick = done;
|
$close.onclick = done;
|
||||||
}, 1000))),
|
}, 1000))),
|
||||||
]);
|
]);
|
||||||
ids.forEach((id) => window.clearTimeout(id));
|
ids.forEach((id) => clearTimeout(id));
|
||||||
await animate($notification, {
|
await animate($notification, {
|
||||||
keyframes: slideYOut(10),
|
keyframes: slideYOut(10),
|
||||||
time: 200,
|
time: 200,
|
||||||
|
|
|
||||||
2
public/assets/helpers/log.d.ts
vendored
2
public/assets/helpers/log.d.ts
vendored
|
|
@ -1 +1 @@
|
||||||
export function report(msg: Event|string, error?: Error, link?: string, lineNo?: number, columnno?: number);
|
export function report(msg: Event|string, err?: any, link?: string, lineNo?: number, columnno?: number);
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export function report(msg, error, link, lineNo, columnNo) {
|
export function report(msg, err, link, lineNo, columnNo) {
|
||||||
if (window.navigator.onLine === false) return Promise.resolve();
|
if (window.navigator.onLine === false) return Promise.resolve();
|
||||||
let url = "./report?";
|
let url = "./report?";
|
||||||
url += "url=" + encodeURIComponent(location.href) + "&";
|
url += "url=" + encodeURIComponent(location.href) + "&";
|
||||||
|
|
@ -6,7 +6,7 @@ export function report(msg, error, link, lineNo, columnNo) {
|
||||||
url += "from=" + encodeURIComponent(link) + "&";
|
url += "from=" + encodeURIComponent(link) + "&";
|
||||||
url += "from.lineNo=" + lineNo + "&";
|
url += "from.lineNo=" + lineNo + "&";
|
||||||
url += "from.columnNo=" + columnNo;
|
url += "from.columnNo=" + columnNo;
|
||||||
if (error) url += "error=" + encodeURIComponent(error.message) + "&";
|
if (err instanceof Error) url += "error=" + encodeURIComponent(err.message) + "&";
|
||||||
|
|
||||||
return fetch(url, { method: "post" }).catch(() => {});
|
return fetch(url, { method: "post" }).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
public/assets/lib/animate.d.ts
vendored
2
public/assets/lib/animate.d.ts
vendored
|
|
@ -23,7 +23,7 @@ export function animate($node: HTMLElement | null, opts: {
|
||||||
fill?: string;
|
fill?: string;
|
||||||
onExit?: () => void;
|
onExit?: () => void;
|
||||||
onEnter?: () => void;
|
onEnter?: () => void;
|
||||||
}): Promise<void>;
|
}): Promise<() => void>;
|
||||||
|
|
||||||
export function slideXIn(dist: number): AnimationFrames[];
|
export function slideXIn(dist: number): AnimationFrames[];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,32 @@
|
||||||
export default class assert {
|
export default class assert {
|
||||||
|
/**
|
||||||
|
* @param {*} object
|
||||||
|
* @param {Function} type
|
||||||
|
* @param {string} [msg]
|
||||||
|
* @return {*}
|
||||||
|
* @throws {TypeError}
|
||||||
|
*/
|
||||||
static type(object, type, msg) {
|
static type(object, type, msg) {
|
||||||
if (!(object instanceof type)) throw new TypeError(msg || `assertion failed - unexpected type for ${object.toString()}`);
|
if (!(object instanceof type)) throw new TypeError(msg || `assertion failed - unexpected type for ${object.toString()}`);
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {*} object
|
||||||
|
* @param {string} [msg]
|
||||||
|
* @return {*}
|
||||||
|
* @throws {TypeError}
|
||||||
|
*/
|
||||||
static truthy(object, msg) {
|
static truthy(object, msg) {
|
||||||
if (!object) throw new TypeError(msg || `assertion failed - object is not truthy`);
|
if (!object) throw new TypeError(msg || `assertion failed - object is not truthy`);
|
||||||
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fail(object, msg) {
|
/**
|
||||||
throw new TypeError(msg || `assertion failed - ${object}`);
|
* @param {string} msg
|
||||||
|
* @throws {TypeError}
|
||||||
|
*/
|
||||||
|
static fail(msg) {
|
||||||
|
throw new TypeError(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ export function join(baseURL, segment) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function forwardURLParams(url, allowed = []) {
|
export function forwardURLParams(url, allowed = []) {
|
||||||
const _url = new URL(location.origin + "/" + url);
|
const _url = new URL(window.location.origin + "/" + url);
|
||||||
for (const [key, value] of new URLSearchParams(location.search)) {
|
for (const [key, value] of new URLSearchParams(location.search)) {
|
||||||
if (allowed.indexOf(key) < 0) continue;
|
if (allowed.indexOf(key) < 0) continue;
|
||||||
_url.searchParams.set(key, value);
|
_url.searchParams.set(key, value);
|
||||||
|
|
|
||||||
8
public/assets/lib/rx.d.ts
vendored
8
public/assets/lib/rx.d.ts
vendored
|
|
@ -1,7 +1,7 @@
|
||||||
import type { Observer } from "rx-core";
|
import type { Observer, Observable as coreObservable } from "rx-core";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Observable, fromEvent, startWith,
|
fromEvent, startWith, Observable,
|
||||||
catchError, tap, first, of,
|
catchError, tap, first, of,
|
||||||
map, mapTo, filter, mergeMap, EMPTY, empty,
|
map, mapTo, filter, mergeMap, EMPTY, empty,
|
||||||
switchMapTo, switchMap,
|
switchMapTo, switchMap,
|
||||||
|
|
@ -74,6 +74,6 @@ 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): ReturnType<typeof fromEvent>;
|
||||||
|
|
||||||
export function onLoad($node: HTMLElement): typeof fromEvent;
|
export function onLoad($node: HTMLElement): void;
|
||||||
|
|
@ -19,19 +19,19 @@ const getFn = (obj, arg0, ...args) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function applyMutation($node, ...keys) {
|
export function applyMutation($node, ...keys) {
|
||||||
assert.type($node, window.HTMLElement);
|
assert.type($node, HTMLElement);
|
||||||
const execute = getFn($node, ...keys);
|
const execute = getFn($node, ...keys);
|
||||||
return rxjs.tap((val) => Array.isArray(val) ? execute(...val) : execute(val));
|
return rxjs.tap((val) => Array.isArray(val) ? execute(...val) : execute(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyMutations($node, ...keys) {
|
export function applyMutations($node, ...keys) {
|
||||||
assert.type($node, window.HTMLElement);
|
assert.type($node, HTMLElement);
|
||||||
const execute = getFn($node, ...keys);
|
const execute = getFn($node, ...keys);
|
||||||
return rxjs.tap((vals) => vals.forEach((val) => execute(val)));
|
return rxjs.tap((vals) => vals.forEach((val) => execute(val)));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stateMutation($node, attr) {
|
export function stateMutation($node, attr) {
|
||||||
assert.type($node, window.HTMLElement);
|
assert.type($node, HTMLElement);
|
||||||
return rxjs.tap((val) => $node[attr] = val);
|
return rxjs.tap((val) => $node[attr] = val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,19 +41,19 @@ export function preventDefault() {
|
||||||
|
|
||||||
export function onClick($node) {
|
export function onClick($node) {
|
||||||
const sideE = ($node) => {
|
const sideE = ($node) => {
|
||||||
assert.type($node, window.HTMLElement);
|
assert.type($node, HTMLElement);
|
||||||
return rxjs.fromEvent($node, "click").pipe(
|
return rxjs.fromEvent($node, "click").pipe(
|
||||||
rxjs.map(() => $node)
|
rxjs.map(() => $node)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
if ($node instanceof window.NodeList) return rxjs.merge(
|
if ($node instanceof NodeList) return rxjs.merge(
|
||||||
...[...$node].map(($n) => sideE($n)),
|
...[...$node].map(($n) => sideE($n)),
|
||||||
);
|
);
|
||||||
return sideE($node);
|
return sideE($node);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onLoad($node) {
|
export function onLoad($node) {
|
||||||
assert.type($node, window.HTMLElement);
|
assert.type($node, HTMLElement);
|
||||||
return new rxjs.Observable((observer) => {
|
return new rxjs.Observable((observer) => {
|
||||||
$node.onload = () => {
|
$node.onload = () => {
|
||||||
observer.next($node);
|
observer.next($node);
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ async function load(route, opts) {
|
||||||
export function createElement(str) {
|
export function createElement(str) {
|
||||||
const $n = window.document.createElement("div");
|
const $n = window.document.createElement("div");
|
||||||
$n.innerHTML = str;
|
$n.innerHTML = str;
|
||||||
if (!($n.firstElementChild instanceof window.HTMLElement)) throw new Error("createElement - unexpected type");
|
if (!($n.firstElementChild instanceof HTMLElement)) throw new Error("createElement - unexpected type");
|
||||||
return $n.firstElementChild;
|
return $n.firstElementChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,10 +73,10 @@ export function createFragment(str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createRender($parent) {
|
export function createRender($parent) {
|
||||||
if (!($parent instanceof window.HTMLElement)) throw new Error(`assert failed: createRender on non HTMLElement`);
|
if (!($parent instanceof HTMLElement)) throw new Error(`assert failed: createRender on non HTMLElement`);
|
||||||
return ($view) => {
|
return ($view) => {
|
||||||
if ($view instanceof window.HTMLElement) $parent.replaceChildren($view);
|
if ($view instanceof HTMLElement) $parent.replaceChildren($view);
|
||||||
else if ($view instanceof window.DocumentFragment) $parent.replaceChildren($view);
|
else if ($view instanceof DocumentFragment) $parent.replaceChildren($view);
|
||||||
else throw new Error(`Unknown view type: ${typeof $view}`);
|
else throw new Error(`Unknown view type: ${typeof $view}`);
|
||||||
return $parent;
|
return $parent;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
const triggerPageChange = () => window.dispatchEvent(new window.Event("pagechange"));
|
const triggerPageChange = () => window.dispatchEvent(new window.Event("pagechange"));
|
||||||
const trimPrefix = (value, prefix) => value.startsWith(prefix) ? value.slice(prefix.length) : value;
|
const trimPrefix = (value = "", prefix) => value.startsWith(prefix) ? value.slice(prefix.length) : value;
|
||||||
|
|
||||||
const _base = window.document.head.querySelector("base").getAttribute("href").replace(new RegExp("/$"), "");
|
const _base = window.document.head.querySelector("base")?.getAttribute("href")?.replace(new RegExp("/$"), "");
|
||||||
export const base = () => _base;
|
export const base = () => _base || "";
|
||||||
export const fromHref = (href) => trimPrefix(href, base());
|
export const fromHref = (href) => trimPrefix(href, base());
|
||||||
export const toHref = (href) => base() + href;
|
export const toHref = (href) => base() + href;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
"A_FILE_NAMED_{{VALUE}}_WAS_CREATED": "Un fichier nommé \"{{VALUE}}\" a été créé",
|
"A_FILE_NAMED_{{VALUE}}_WAS_CREATED": "Un fichier nommé \"{{VALUE}}\" a été créé",
|
||||||
"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED": "Un dossier nommé \"{{VALUE}}\" a été créé",
|
"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED": "Un dossier nommé \"{{VALUE}}\" a été créé",
|
||||||
"BEAUTIFUL_URL": "id_du_lien",
|
"BEAUTIFUL_URL": "id_du_lien",
|
||||||
|
"BOOKMARK": "favoris",
|
||||||
"CAMERA": "appareil",
|
"CAMERA": "appareil",
|
||||||
"CANCEL": "annuler",
|
"CANCEL": "annuler",
|
||||||
"CANNOT_ESTABLISH_A_CONNECTION": "Impossible d'établir une connexion",
|
"CANNOT_ESTABLISH_A_CONNECTION": "Impossible d'établir une connexion",
|
||||||
|
|
@ -20,6 +21,7 @@
|
||||||
"CONNECT": "connexion",
|
"CONNECT": "connexion",
|
||||||
"CONNECTION_LOST": "Connection perdue",
|
"CONNECTION_LOST": "Connection perdue",
|
||||||
"COPIED_TO_CLIPBOARD": "Copié dans le presse-papier",
|
"COPIED_TO_CLIPBOARD": "Copié dans le presse-papier",
|
||||||
|
"CREATE_A_TAG": "créer un tag",
|
||||||
"CREATE_A_NEW_LINK": "créer un lien partagé",
|
"CREATE_A_NEW_LINK": "créer un lien partagé",
|
||||||
"CURRENT": "en cours",
|
"CURRENT": "en cours",
|
||||||
"CURRENT_UPLOAD": "en cours",
|
"CURRENT_UPLOAD": "en cours",
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ function mainReorderKey(argv) {
|
||||||
function mainAddTranslationKey(argv) {
|
function mainAddTranslationKey(argv) {
|
||||||
const key = argv[3];
|
const key = argv[3];
|
||||||
const filepath = argv[2];
|
const filepath = argv[2];
|
||||||
if (!filepath) throw new Error("missing args")
|
if (!filepath) throw new Error("missing args");
|
||||||
else if (!key) return;
|
else if (!key) return;
|
||||||
|
|
||||||
const json = JSON.parse(fs.readFileSync(filepath));
|
const json = JSON.parse(fs.readFileSync(filepath));
|
||||||
|
|
@ -28,6 +28,6 @@ function mainAddTranslationKey(argv) {
|
||||||
|
|
||||||
// usage: find *.json -type f -exec node script.js {} \;
|
// usage: find *.json -type f -exec node script.js {} \;
|
||||||
(function() {
|
(function() {
|
||||||
mainAddTranslationKey(process.argv)
|
mainAddTranslationKey(process.argv);
|
||||||
mainReorderKey(process.argv)
|
mainReorderKey(process.argv);
|
||||||
})()
|
})();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
export async function init() {
|
export async function init() {
|
||||||
if (!window.CONFIG.enable_chromecast) {
|
if (!window.CONFIG["enable_chromecast"]) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
} else if (!("chrome" in window)) {
|
} else if (!("chrome" in window)) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { ApplicationError } from "../../lib/error.js";
|
import { ApplicationError } from "../../lib/error.js";
|
||||||
|
|
||||||
class BoxItem extends window.HTMLDivElement {
|
class BoxItem extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.attributeChangedCallback();
|
this.attributeChangedCallback();
|
||||||
|
|
@ -42,4 +42,4 @@ class BoxItem extends window.HTMLDivElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("box-item", BoxItem, { extends: "div" });
|
customElements.define("box-item", BoxItem);
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ export default async function(render) {
|
||||||
const init$ = getMiddlewareAvailable().pipe(
|
const init$ = getMiddlewareAvailable().pipe(
|
||||||
rxjs.first(),
|
rxjs.first(),
|
||||||
rxjs.map((specs) => Object.keys(specs).map((label) => createElement(`
|
rxjs.map((specs) => Object.keys(specs).map((label) => createElement(`
|
||||||
<div is="box-item" data-label="${label}"></div>
|
<box-item data-label="${label}"></box-item>
|
||||||
`))),
|
`))),
|
||||||
rxjs.tap(() => {
|
rxjs.tap(() => {
|
||||||
qs($page, "h2").classList.remove("hidden");
|
qs($page, "h2").classList.remove("hidden");
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ export function getState() {
|
||||||
if (!authType) return config;
|
if (!authType) return config;
|
||||||
|
|
||||||
const $formIDP = document.querySelector(`[data-bind="idp"]`);
|
const $formIDP = document.querySelector(`[data-bind="idp"]`);
|
||||||
if (!($formIDP instanceof window.HTMLFormElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: idp isn't a form");
|
if (!($formIDP instanceof HTMLFormElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: idp isn't a form");
|
||||||
let formValues = [...new FormData($formIDP)];
|
let formValues = [...new FormData($formIDP)];
|
||||||
config.middleware.identity_provider = {
|
config.middleware.identity_provider = {
|
||||||
type: authType,
|
type: authType,
|
||||||
|
|
@ -121,7 +121,7 @@ export function getState() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const $formAM = document.querySelector(`[data-bind="attribute-mapping"]`);
|
const $formAM = document.querySelector(`[data-bind="attribute-mapping"]`);
|
||||||
if (!($formAM instanceof window.HTMLFormElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: attribute mapping isn't a form");
|
if (!($formAM instanceof HTMLFormElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: attribute mapping isn't a form");
|
||||||
formValues = [...new FormData($formAM)];
|
formValues = [...new FormData($formAM)];
|
||||||
config.middleware.attribute_mapping = {
|
config.middleware.attribute_mapping = {
|
||||||
related_backend: (formValues.shift() || [])[1],
|
related_backend: (formValues.shift() || [])[1],
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,6 @@ function componentStep2(render) {
|
||||||
rxjs.filter((config) => config["log"]["telemetry"] !== true),
|
rxjs.filter((config) => config["log"]["telemetry"] !== true),
|
||||||
rxjs.mergeMap(async(config) => {
|
rxjs.mergeMap(async(config) => {
|
||||||
const enabled = await componentTelemetryPopup(createModal({ withButtonsRight: "OK" }));
|
const enabled = await componentTelemetryPopup(createModal({ withButtonsRight: "OK" }));
|
||||||
console.log(enabled);
|
|
||||||
if (enabled === false) return null;
|
if (enabled === false) return null;
|
||||||
config["log"]["telemetry"] = enabled;
|
config["log"]["telemetry"] = enabled;
|
||||||
return config;
|
return config;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import rxjs, { effect, applyMutation, applyMutations, preventDefault, onClick }
|
||||||
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, opacityIn } from "../../lib/animate.js";
|
import { animate, slideYIn, transition, opacityIn } from "../../lib/animate.js";
|
||||||
|
import assert from "../../lib/assert.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";
|
||||||
|
|
@ -128,7 +129,7 @@ export default async function(render) {
|
||||||
const toggleLoader = (hide) => {
|
const toggleLoader = (hide) => {
|
||||||
if (hide) {
|
if (hide) {
|
||||||
$page.classList.add("hidden");
|
$page.classList.add("hidden");
|
||||||
$page.parentElement.appendChild($loader);
|
assert.truthy($page.parentElement).appendChild($loader);
|
||||||
} else {
|
} else {
|
||||||
$loader.remove();
|
$loader.remove();
|
||||||
$page.classList.remove("hidden");
|
$page.classList.remove("hidden");
|
||||||
|
|
@ -216,7 +217,7 @@ export default async function(render) {
|
||||||
// feature7: empty connection handling
|
// feature7: empty connection handling
|
||||||
effect(connections$.pipe(
|
effect(connections$.pipe(
|
||||||
rxjs.filter((conns) => conns.length === 0),
|
rxjs.filter((conns) => conns.length === 0),
|
||||||
rxjs.mergeMap((a) => Promise.reject(new Error("no backend selected"))),
|
rxjs.mergeMap(() => Promise.reject(new Error("there is nothing here"))), // TODO: check translation?
|
||||||
rxjs.catchError(ctrlError()),
|
rxjs.catchError(ctrlError()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export default function(render = createRender(qs(document.body, "[role=\"main\"]
|
||||||
return function(err) {
|
return function(err) {
|
||||||
const [msg, trace] = processError(err);
|
const [msg, trace] = processError(err);
|
||||||
|
|
||||||
const link = forwardURLParams(calculateBacklink(fromHref(location.pathname)), ["share"]);
|
const link = forwardURLParams(calculateBacklink(fromHref(window.location.pathname)), ["share"]);
|
||||||
const $page = createElement(`
|
const $page = createElement(`
|
||||||
<div>
|
<div>
|
||||||
<style>${css}</style>
|
<style>${css}</style>
|
||||||
|
|
@ -32,7 +32,7 @@ export default function(render = createRender(qs(document.body, "[role=\"main\"]
|
||||||
<h2>${t(msg)}</h2>
|
<h2>${t(msg)}</h2>
|
||||||
<p>
|
<p>
|
||||||
<button class="light" data-bind="details">${t("More details")}</button>
|
<button class="light" data-bind="details">${t("More details")}</button>
|
||||||
<button class="primary" data-bind="refresh">${t("Refresh")}</button>
|
<button class="primary" data-bind="refresh">${t("Reload")}</button>
|
||||||
<pre class="hidden"><code>${strToHTML(trace)}</code></pre>
|
<pre class="hidden"><code>${strToHTML(trace)}</code></pre>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ export default function(render) {
|
||||||
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 });
|
||||||
else if (step === "done") {
|
else if (step === "done") {
|
||||||
if (isDir(state.path)) navigate(toHref(`/files/?share=${shareID}`));
|
if (isDir(state["path"])) navigate(toHref(`/files/?share=${shareID}`));
|
||||||
else navigate(toHref(`/view/${basename(state.path)}?share=${shareID}&nav=false`));
|
else navigate(toHref(`/view/${basename(state["path"])}?share=${shareID}&nav=false`));
|
||||||
return rxjs.EMPTY;
|
return rxjs.EMPTY;
|
||||||
}
|
}
|
||||||
else assert.fail(`unknown step: "${step}"`);
|
else assert.fail(`unknown step: "${step}"`);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { createElement, createRender } from "../lib/skeleton/index.js";
|
||||||
import rxjs, { effect } from "../lib/rx.js";
|
import rxjs, { effect } from "../lib/rx.js";
|
||||||
import { ApplicationError } from "../lib/error.js";
|
import { ApplicationError } from "../lib/error.js";
|
||||||
import { basename } from "../lib/path.js";
|
import { basename } from "../lib/path.js";
|
||||||
|
import assert from "../lib/assert.js";
|
||||||
import { loadCSS } from "../helpers/loader.js";
|
import { loadCSS } from "../helpers/loader.js";
|
||||||
import WithShell, { init as initShell } from "../components/decorator_shell_filemanager.js";
|
import WithShell, { init as initShell } from "../components/decorator_shell_filemanager.js";
|
||||||
import { init as initMenubar } from "./viewerpage/component_menubar.js";
|
import { init as initMenubar } from "./viewerpage/component_menubar.js";
|
||||||
|
|
@ -46,7 +47,7 @@ export default WithShell(async function(render) {
|
||||||
render($page);
|
render($page);
|
||||||
|
|
||||||
// feature: render viewer application
|
// feature: render viewer application
|
||||||
effect(rxjs.of(window.CONFIG.mime || {}).pipe(
|
effect(rxjs.of(window.CONFIG["mime"] || {}).pipe(
|
||||||
rxjs.map((mimes) => opener(basename(getCurrentPath()), mimes)),
|
rxjs.map((mimes) => opener(basename(getCurrentPath()), mimes)),
|
||||||
rxjs.mergeMap(([opener, opts]) => rxjs.from(loadModule(opener)).pipe(rxjs.tap((module) => {
|
rxjs.mergeMap(([opener, opts]) => rxjs.from(loadModule(opener)).pipe(rxjs.tap((module) => {
|
||||||
module.default(createRender($page), { ...opts, acl$: options() });
|
module.default(createRender($page), { ...opts, acl$: options() });
|
||||||
|
|
@ -58,8 +59,9 @@ export default WithShell(async function(render) {
|
||||||
effect(rxjs.of(new URL(location.toString()).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";
|
const $parent = assert.truthy($page.parentElement);
|
||||||
$page.parentElement.style.borderRadius = "0";
|
$parent.style.border = "none";
|
||||||
|
$parent.style.borderRadius = "0";
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
|
@ -68,7 +70,7 @@ export async function init() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
loadCSS(import.meta.url, "./ctrl_viewerpage.css"),
|
loadCSS(import.meta.url, "./ctrl_viewerpage.css"),
|
||||||
initShell(), initMenubar(), initCache(),
|
initShell(), initMenubar(), initCache(),
|
||||||
rxjs.of(window.CONFIG.mime || {}).pipe(
|
rxjs.of(window.CONFIG["mime"] || {}).pipe(
|
||||||
rxjs.map((mimes) => opener(basename(getCurrentPath()), mimes)),
|
rxjs.map((mimes) => opener(basename(getCurrentPath()), mimes)),
|
||||||
rxjs.mergeMap(([opener]) => loadModule(opener)),
|
rxjs.mergeMap(([opener]) => loadModule(opener)),
|
||||||
rxjs.mergeMap((module) => typeof module.init === "function"? module.init() : rxjs.EMPTY),
|
rxjs.mergeMap((module) => typeof module.init === "function"? module.init() : rxjs.EMPTY),
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,38 @@
|
||||||
|
import assert from "../../lib/assert.js";
|
||||||
import { getSession } from "../../model/session.js";
|
import { getSession } from "../../model/session.js";
|
||||||
|
|
||||||
class ICache {
|
class ICache {
|
||||||
async get() { throw new Error("NOT_IMPLEMENTED"); }
|
/**
|
||||||
|
* @param {string} _path
|
||||||
|
* @return {Promise<any>}
|
||||||
|
*/
|
||||||
|
async get(_path) { throw new Error("NOT_IMPLEMENTED"); }
|
||||||
|
|
||||||
async store() { throw new Error("NOT_IMPLEMENTED"); }
|
/**
|
||||||
|
* @param {string} _path
|
||||||
|
* @param {any} _data
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
|
async store(_path, _data) { throw new Error("NOT_IMPLEMENTED"); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
async remove() { throw new Error("NOT_IMPLEMENTED"); }
|
async remove() { throw new Error("NOT_IMPLEMENTED"); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path
|
||||||
|
* @param {function(any): any} fn
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
async update(path, fn) {
|
async update(path, fn) {
|
||||||
const data = await this.get(path);
|
const data = await this.get(path);
|
||||||
return this.store(path, fn(data || {}));
|
return this.store(path, fn(data || {}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
async destroy() { throw new Error("NOT_IMPLEMENTED"); }
|
async destroy() { throw new Error("NOT_IMPLEMENTED"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -21,14 +42,23 @@ class InMemoryCache extends ICache {
|
||||||
this.data = {};
|
this.data = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async get(path) {
|
async get(path) {
|
||||||
return this.data[this._key(path)] || null;
|
return this.data[this._key(path)] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async store(path, obj) {
|
async store(path, obj) {
|
||||||
this.data[this._key(path)] = obj;
|
this.data[this._key(path)] = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async remove(path, exact = true) {
|
async remove(path, exact = true) {
|
||||||
if (!path) {
|
if (!path) {
|
||||||
this.data = {};
|
this.data = {};
|
||||||
|
|
@ -46,6 +76,9 @@ class InMemoryCache extends ICache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async destroy() {
|
async destroy() {
|
||||||
this.data = {};
|
this.data = {};
|
||||||
}
|
}
|
||||||
|
|
@ -58,7 +91,7 @@ class InMemoryCache extends ICache {
|
||||||
class IndexDBCache extends ICache {
|
class IndexDBCache extends ICache {
|
||||||
DB_VERSION = 5;
|
DB_VERSION = 5;
|
||||||
FILE_PATH = "file_path";
|
FILE_PATH = "file_path";
|
||||||
db = null;
|
/** @type {Promise<IDBDatabase> | null} */ db = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
@ -68,25 +101,31 @@ class IndexDBCache extends ICache {
|
||||||
|
|
||||||
this.db = new Promise((done, err) => {
|
this.db = new Promise((done, err) => {
|
||||||
request.onsuccess = (e) => {
|
request.onsuccess = (e) => {
|
||||||
done(e.target.result);
|
done(assert.truthy(e.target).result);
|
||||||
};
|
};
|
||||||
request.onerror = () => err(new Error("INDEXEDDB_NOT_SUPPORTED"));
|
request.onerror = () => err(new Error("INDEXEDDB_NOT_SUPPORTED"));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async get(path) {
|
async get(path) {
|
||||||
const db = await this.db;
|
const db = assert.truthy(await this.db);
|
||||||
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) => {
|
return await new Promise((done) => {
|
||||||
query.onsuccess = (e) => done(query.result || null);
|
query.onsuccess = () => done(query.result || null);
|
||||||
query.onerror = () => done(null);
|
query.onerror = () => done(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async store(path, value = {}) {
|
async store(path, value = {}) {
|
||||||
const db = await this.db;
|
const db = assert.truthy(await this.db);
|
||||||
const tx = db.transaction(this.FILE_PATH, "readwrite");
|
const tx = db.transaction(this.FILE_PATH, "readwrite");
|
||||||
const store = tx.objectStore(this.FILE_PATH);
|
const store = tx.objectStore(this.FILE_PATH);
|
||||||
|
|
||||||
|
|
@ -103,8 +142,11 @@ class IndexDBCache extends ICache {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async remove(path, exact = true) {
|
async remove(path, exact = true) {
|
||||||
const db = await this.db;
|
const db = assert.truthy(await this.db);
|
||||||
const tx = db.transaction(this.FILE_PATH, "readwrite");
|
const tx = db.transaction(this.FILE_PATH, "readwrite");
|
||||||
const store = tx.objectStore(this.FILE_PATH);
|
const store = tx.objectStore(this.FILE_PATH);
|
||||||
const key = this._key(path);
|
const key = this._key(path);
|
||||||
|
|
@ -179,7 +221,7 @@ export async function init() {
|
||||||
cache = new InMemoryCache();
|
cache = new InMemoryCache();
|
||||||
if (!("indexedDB" in window)) return;
|
if (!("indexedDB" in window)) return;
|
||||||
|
|
||||||
cache = new IndexDBCache();
|
cache = assert.truthy(new IndexDBCache());
|
||||||
return cache.db.catch((err) => {
|
return cache.db.catch((err) => {
|
||||||
if (err.message === "INDEXEDDB_NOT_SUPPORTED") {
|
if (err.message === "INDEXEDDB_NOT_SUPPORTED") {
|
||||||
// Firefox in private mode act like if it supports indexedDB but
|
// Firefox in private mode act like if it supports indexedDB but
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ export default async function(render) {
|
||||||
)),
|
)),
|
||||||
rxjs.mergeMap(({ show_hidden, files, ...rest }) => {
|
rxjs.mergeMap(({ show_hidden, files, ...rest }) => {
|
||||||
if (show_hidden === false) files = files.filter(({ name }) => name[0] !== ".");
|
if (show_hidden === false) files = files.filter(({ name }) => name[0] !== ".");
|
||||||
files = sort(files, rest.sort, rest.order);
|
files = sort(files, rest["sort"], rest["order"]);
|
||||||
return rxjs.of({ ...rest, files });
|
return rxjs.of({ ...rest, files });
|
||||||
}),
|
}),
|
||||||
rxjs.map((data) => ({ ...data, count: count++ })),
|
rxjs.map((data) => ({ ...data, count: count++ })),
|
||||||
|
|
@ -265,7 +265,7 @@ export default async function(render) {
|
||||||
rxjs.filter((e) => e.key === "a" &&
|
rxjs.filter((e) => e.key === "a" &&
|
||||||
(e.ctrlKey || e.metaKey) &&
|
(e.ctrlKey || e.metaKey) &&
|
||||||
(files$.value || []).length > 0 &&
|
(files$.value || []).length > 0 &&
|
||||||
assert.type(document.activeElement, window.HTMLElement).tagName !== "INPUT"),
|
assert.type(document.activeElement, HTMLElement).tagName !== "INPUT"),
|
||||||
preventDefault(),
|
preventDefault(),
|
||||||
rxjs.tap(() => {
|
rxjs.tap(() => {
|
||||||
clearSelection();
|
clearSelection();
|
||||||
|
|
@ -289,10 +289,10 @@ export default async function(render) {
|
||||||
));
|
));
|
||||||
effect(getSelection$().pipe(rxjs.tap(() => {
|
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(assert.truthy($thing.getAttribute("data-n"))));
|
||||||
$thing.classList.add(checked ? "selected" : "not-selected");
|
$thing.classList.add(checked ? "selected" : "not-selected");
|
||||||
$thing.classList.remove(checked ? "not-selected" : "selected");
|
$thing.classList.remove(checked ? "not-selected" : "selected");
|
||||||
qs($thing, `input[type="checkbox"]`).checked = checked;
|
qs(assert.type($thing, HTMLElement), `input[type="checkbox"]`).checked = checked;
|
||||||
};
|
};
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { createElement } from "../../lib/skeleton/index.js";
|
import { createElement, nop } from "../../lib/skeleton/index.js";
|
||||||
import rxjs, { effect, onClick, preventDefault } from "../../lib/rx.js";
|
import rxjs, { effect, onClick, preventDefault } from "../../lib/rx.js";
|
||||||
import { qs } from "../../lib/dom.js";
|
import { qs } from "../../lib/dom.js";
|
||||||
import { animate } from "../../lib/animate.js";
|
import { animate } from "../../lib/animate.js";
|
||||||
|
|
@ -71,7 +71,7 @@ export default async function(render) {
|
||||||
$icon.setAttribute("alt", alt);
|
$icon.setAttribute("alt", alt);
|
||||||
$input.value = "";
|
$input.value = "";
|
||||||
$input.nextSibling.setAttribute("name", alt);
|
$input.nextSibling.setAttribute("name", alt);
|
||||||
let done = Promise.resolve();
|
let done = Promise.resolve(nop);
|
||||||
if ($node.classList.contains("hidden")) done = animate($node, {
|
if ($node.classList.contains("hidden")) done = animate($node, {
|
||||||
keyframes: [{ height: `0px` }, { height: "50px" }],
|
keyframes: [{ height: `0px` }, { height: "50px" }],
|
||||||
time: 100,
|
time: 100,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { createElement, createRender, createFragment, onDestroy } from "../../li
|
||||||
import rxjs, { effect, onClick, preventDefault } from "../../lib/rx.js";
|
import rxjs, { effect, onClick, preventDefault } from "../../lib/rx.js";
|
||||||
import { animate, slideXIn, slideYIn } from "../../lib/animate.js";
|
import { animate, slideXIn, slideYIn } from "../../lib/animate.js";
|
||||||
import { loadCSS } from "../../helpers/loader.js";
|
import { loadCSS } from "../../helpers/loader.js";
|
||||||
|
import assert from "../../lib/assert.js";
|
||||||
import { qs, qsa } from "../../lib/dom.js";
|
import { qs, qsa } from "../../lib/dom.js";
|
||||||
import { basename } from "../../lib/path.js";
|
import { basename } from "../../lib/path.js";
|
||||||
import t from "../../locales/index.js";
|
import t from "../../locales/index.js";
|
||||||
|
|
@ -51,7 +52,7 @@ export default async function(render) {
|
||||||
render($page);
|
render($page);
|
||||||
onDestroy(() => clearSelection());
|
onDestroy(() => clearSelection());
|
||||||
|
|
||||||
const $scroll = $page.closest(".scroll-y");
|
const $scroll = assert.type($page.closest(".scroll-y"), HTMLElement);
|
||||||
componentLeft(createRender(qs($page, ".action.left")), { $scroll });
|
componentLeft(createRender(qs($page, ".action.left")), { $scroll });
|
||||||
componentRight(createRender(qs($page, ".action.right")));
|
componentRight(createRender(qs($page, ".action.right")));
|
||||||
|
|
||||||
|
|
@ -112,13 +113,13 @@ function componentLeft(render, { $scroll }) {
|
||||||
<button data-action="rename" title="${t("Rename")}"${toggleDependingOnPermission(currentPath(), "rename")}>
|
<button data-action="rename" title="${t("Rename")}"${toggleDependingOnPermission(currentPath(), "rename")}>
|
||||||
${t("Rename")}
|
${t("Rename")}
|
||||||
</button>
|
</button>
|
||||||
<button data-action="share" title="${t("Share")}" class="${(window.CONFIG.enable_share && !new URLSearchParams(location.search).has("share")) ? "" : "hidden"}">
|
<button data-action="share" title="${t("Share")}" class="${(window.CONFIG["enable_share"] && !new URLSearchParams(location.search).has("share")) ? "" : "hidden"}">
|
||||||
${t("Share")}
|
${t("Share")}
|
||||||
</button>
|
</button>
|
||||||
<button data-action="embed" class="hidden" title="${t("Embed")}">
|
<button data-action="embed" title="${t("Embed")}">
|
||||||
${t("Embed")}
|
${t("Embed")}
|
||||||
</button>
|
</button>
|
||||||
<button data-action="tag" class="hidden" title="${t("Tag")}">
|
<button data-action="tag" title="${t("Tag")}">
|
||||||
${t("Tag")}
|
${t("Tag")}
|
||||||
</button>
|
</button>
|
||||||
`))),
|
`))),
|
||||||
|
|
@ -396,6 +397,7 @@ export function init() {
|
||||||
loadCSS(import.meta.url, "../../css/designsystem_dropdown.css"),
|
loadCSS(import.meta.url, "../../css/designsystem_dropdown.css"),
|
||||||
loadCSS(import.meta.url, "./ctrl_submenu.css"),
|
loadCSS(import.meta.url, "./ctrl_submenu.css"),
|
||||||
loadCSS(import.meta.url, "./modal_share.css"),
|
loadCSS(import.meta.url, "./modal_share.css"),
|
||||||
|
loadCSS(import.meta.url, "./modal_tag.css"),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -407,10 +409,10 @@ function generateLinkAttributes(selections) {
|
||||||
const regDir = new RegExp("/$");
|
const regDir = new RegExp("/$");
|
||||||
const isDir = regDir.test(path);
|
const isDir = regDir.test(path);
|
||||||
if (isDir) {
|
if (isDir) {
|
||||||
filename = basename(path.replace(regDir, "")) + ".zip"
|
filename = basename(path.replace(regDir, "")) + ".zip";
|
||||||
} else {
|
} else {
|
||||||
filename = basename(path);
|
filename = basename(path);
|
||||||
href = "api/files/cat?"
|
href = "api/files/cat?";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
href += selections.map(({ path }) => "path=" + encodeURIComponent(path)).join("&");
|
href += selections.map(({ path }) => "path=" + encodeURIComponent(path)).join("&");
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ export default async function(render) {
|
||||||
<div is="component_filezone"></div>
|
<div is="component_filezone"></div>
|
||||||
<div is="component_upload_fab"></div>
|
<div is="component_upload_fab"></div>
|
||||||
`);
|
`);
|
||||||
componentFilezone(createRender(assert.type($page.children[0], window.HTMLElement)), { workers$ });
|
componentFilezone(createRender(assert.type($page.children[0], HTMLElement)), { workers$ });
|
||||||
componentUploadFAB(createRender(assert.type($page.children[1], window.HTMLElement)), { workers$ });
|
componentUploadFAB(createRender(assert.type($page.children[1], HTMLElement)), { workers$ });
|
||||||
render($page);
|
render($page);
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
|
|
@ -62,7 +62,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 = assert.type(document.body.querySelector(selector), window.HTMLElement);
|
const $target = assert.type(qs(document.body, selector), HTMLElement);
|
||||||
|
|
||||||
$target.ondragenter = (e) => {
|
$target.ondragenter = (e) => {
|
||||||
if (!isNativeFileUpload(e)) return;
|
if (!isNativeFileUpload(e)) return;
|
||||||
|
|
@ -78,7 +78,7 @@ function componentFilezone(render, { workers$ }) {
|
||||||
} else if (e.dataTransfer.files instanceof window.FileList) {
|
} else if (e.dataTransfer.files instanceof window.FileList) {
|
||||||
workers$.next(await processFiles(e.dataTransfer.files));
|
workers$.next(await processFiles(e.dataTransfer.files));
|
||||||
} else {
|
} else {
|
||||||
assert.fail("NOT_IMPLEMENTED - unknown entry type in ctrl_upload.js", e.dataTransfer);
|
assert.fail("NOT_IMPLEMENTED - unknown entry type in ctrl_upload.js");
|
||||||
}
|
}
|
||||||
clearTimeout(loadID);
|
clearTimeout(loadID);
|
||||||
render(createFragment(""));
|
render(createFragment(""));
|
||||||
|
|
@ -134,15 +134,13 @@ function componentUploadQueue(render, { workers$ }) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// feature1: close the queue
|
// feature1: close the queue
|
||||||
onClick(qs($page, `img[alt="close"]`)).pipe(
|
onClick(qs($page, `img[alt="close"]`)).pipe(rxjs.tap(async() => {
|
||||||
rxjs.tap(async(cancel) => {
|
|
||||||
const cleanup = await animate($page, { time: 200, keyframes: slideYOut(50) });
|
const cleanup = await animate($page, { time: 200, keyframes: slideYOut(50) });
|
||||||
$content.innerHTML = "";
|
$content.innerHTML = "";
|
||||||
$page.classList.add("hidden");
|
$page.classList.add("hidden");
|
||||||
updateTotal.reset();
|
updateTotal.reset();
|
||||||
cleanup();
|
cleanup();
|
||||||
}),
|
})).subscribe();
|
||||||
).subscribe();
|
|
||||||
|
|
||||||
// feature2: setup the task queue in the dom
|
// feature2: setup the task queue in the dom
|
||||||
workers$.subscribe(({ tasks }) => {
|
workers$.subscribe(({ tasks }) => {
|
||||||
|
|
@ -150,7 +148,7 @@ function componentUploadQueue(render, { workers$ }) {
|
||||||
updateTotal.addToTotal(tasks.length);
|
updateTotal.addToTotal(tasks.length);
|
||||||
const $fragment = document.createDocumentFragment();
|
const $fragment = document.createDocumentFragment();
|
||||||
for (let i = 0; i<tasks.length; i++) {
|
for (let i = 0; i<tasks.length; i++) {
|
||||||
const $task = $file.cloneNode(true);
|
const $task = assert.type($file.cloneNode(true), HTMLElement);
|
||||||
$fragment.appendChild($task);
|
$fragment.appendChild($task);
|
||||||
$task.setAttribute("data-path", tasks[i]["path"]);
|
$task.setAttribute("data-path", tasks[i]["path"]);
|
||||||
$task.firstElementChild.firstElementChild.textContent = tasks[i]["path"]; // qs($todo, ".file_path span.path")
|
$task.firstElementChild.firstElementChild.textContent = tasks[i]["path"]; // qs($todo, ".file_path span.path")
|
||||||
|
|
@ -176,10 +174,10 @@ function componentUploadQueue(render, { workers$ }) {
|
||||||
let last = 0;
|
let last = 0;
|
||||||
return (nworker, currentWorkerSpeed) => {
|
return (nworker, currentWorkerSpeed) => {
|
||||||
workersSpeed[nworker] = currentWorkerSpeed;
|
workersSpeed[nworker] = currentWorkerSpeed;
|
||||||
if (new Date() - last <= 500) return;
|
if (new Date().getTime() - last <= 500) return;
|
||||||
last = new Date();
|
last = new Date().getTime();
|
||||||
const speed = workersSpeed.reduce((acc, el) => acc + el, 0);
|
const speed = workersSpeed.reduce((acc, el) => acc + el, 0);
|
||||||
const $speed = assert.type($page.firstElementChild.nextElementSibling.firstElementChild, window.HTMLElement);
|
const $speed = assert.type($page.firstElementChild?.nextElementSibling?.firstElementChild, HTMLElement);
|
||||||
$speed.textContent = formatSpeed(speed);
|
$speed.textContent = formatSpeed(speed);
|
||||||
};
|
};
|
||||||
}(new Array(MAX_WORKERS).fill(0)));
|
}(new Array(MAX_WORKERS).fill(0)));
|
||||||
|
|
@ -208,7 +206,7 @@ function componentUploadQueue(render, { workers$ }) {
|
||||||
$close.removeEventListener("click", cancel);
|
$close.removeEventListener("click", cancel);
|
||||||
break;
|
break;
|
||||||
case "error":
|
case "error":
|
||||||
const $retry = assert.type($iconRetry.cloneNode(true), window.HTMLElement);
|
const $retry = assert.type($iconRetry.cloneNode(true), HTMLElement);
|
||||||
updateDOMGlobalTitle($page, t("Error"));
|
updateDOMGlobalTitle($page, t("Error"));
|
||||||
updateDOMGlobalSpeed(nworker, 0);
|
updateDOMGlobalSpeed(nworker, 0);
|
||||||
updateDOMTaskProgress($task, t("Error"));
|
updateDOMTaskProgress($task, t("Error"));
|
||||||
|
|
@ -276,7 +274,7 @@ function componentUploadQueue(render, { workers$ }) {
|
||||||
const nworker = reservations.indexOf(false);
|
const nworker = reservations.indexOf(false);
|
||||||
if (nworker === -1) break; // the pool of workers is already to its max
|
if (nworker === -1) break; // the pool of workers is already to its max
|
||||||
reservations[nworker] = true;
|
reservations[nworker] = true;
|
||||||
noFailureAllowed(processWorkerQueue.bind(this, nworker)).then(() => reservations[nworker] = false);
|
noFailureAllowed(processWorkerQueue.bind(null, nworker)).then(() => reservations[nworker] = false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -295,17 +293,24 @@ function workerImplFile({ error, progress, speed }) {
|
||||||
this.prevProgress = [];
|
this.prevProgress = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
cancel() {
|
cancel() {
|
||||||
this.xhr.abort();
|
assert.type(this.xhr, XMLHttpRequest).abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async run({ file, path, virtual }) {
|
async run({ file, path, virtual }) {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
this.xhr = xhr;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.xhr = new XMLHttpRequest();
|
xhr.open("POST", "api/files/cat?path=" + encodeURIComponent(path));
|
||||||
this.xhr.open("POST", "api/files/cat?path=" + encodeURIComponent(path));
|
xhr.withCredentials = true;
|
||||||
this.xhr.withCredentials = true;
|
xhr.setRequestHeader("X-Requested-With", "XmlHttpRequest");
|
||||||
this.xhr.setRequestHeader("X-Requested-With", "XmlHttpRequest");
|
xhr.upload.onprogress = (e) => {
|
||||||
this.xhr.upload.onprogress = (e) => {
|
|
||||||
if (!e.lengthComputable) return;
|
if (!e.lengthComputable) return;
|
||||||
const percent = Math.floor(100 * e.loaded / e.total);
|
const percent = Math.floor(100 * e.loaded / e.total);
|
||||||
progress(percent);
|
progress(percent);
|
||||||
|
|
@ -329,26 +334,26 @@ function workerImplFile({ error, progress, speed }) {
|
||||||
this.prevProgress.shift();
|
this.prevProgress.shift();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.xhr.upload.onabort = () => {
|
xhr.upload.onabort = () => {
|
||||||
reject(ABORT_ERROR);
|
reject(ABORT_ERROR);
|
||||||
error(ABORT_ERROR);
|
error(ABORT_ERROR);
|
||||||
virtual.afterError();
|
virtual.afterError();
|
||||||
};
|
};
|
||||||
this.xhr.onload = () => {
|
xhr.onload = () => {
|
||||||
progress(100);
|
progress(100);
|
||||||
if (this.xhr.status !== 200) {
|
if (xhr.status !== 200) {
|
||||||
virtual.afterError();
|
virtual.afterError();
|
||||||
reject(new Error(this.xhr.statusText));
|
reject(new Error(xhr.statusText));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
virtual.afterSuccess();
|
virtual.afterSuccess();
|
||||||
resolve(null);
|
resolve(null);
|
||||||
};
|
};
|
||||||
this.xhr.onerror = function(e) {
|
xhr.onerror = function(e) {
|
||||||
reject(new AjaxError("failed", e, "FAILED"));
|
reject(new AjaxError("failed", e, "FAILED"));
|
||||||
virtual.afterError();
|
virtual.afterError();
|
||||||
};
|
};
|
||||||
file().then((f) => this.xhr.send(f)).catch((err) => this.xhr.onerror(err));
|
file().then((f) => xhr.send(f)).catch((err) => xhr.onerror && xhr.onerror(err));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
|
|
@ -361,17 +366,24 @@ function workerImplDirectory({ error, progress }) {
|
||||||
this.xhr = null;
|
this.xhr = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
cancel() {
|
cancel() {
|
||||||
if (this.xhr instanceof XMLHttpRequest) this.xhr.abort();
|
assert.type(this.xhr, XMLHttpRequest).abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
run({ virtual, path }) {
|
run({ virtual, path }) {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
this.xhr = xhr;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.xhr = new XMLHttpRequest();
|
xhr.open("POST", "api/files/mkdir?path=" + encodeURIComponent(path));
|
||||||
this.xhr.open("POST", "api/files/mkdir?path=" + encodeURIComponent(path));
|
xhr.withCredentials = true;
|
||||||
this.xhr.withCredentials = true;
|
xhr.setRequestHeader("X-Requested-With", "XmlHttpRequest");
|
||||||
this.xhr.setRequestHeader("X-Requested-With", "XmlHttpRequest");
|
xhr.onerror = function(e) {
|
||||||
this.xhr.onerror = function(e) {
|
|
||||||
reject(new AjaxError("failed", e, "FAILED"));
|
reject(new AjaxError("failed", e, "FAILED"));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -384,24 +396,24 @@ function workerImplDirectory({ error, progress }) {
|
||||||
}
|
}
|
||||||
progress(percent);
|
progress(percent);
|
||||||
}, 100);
|
}, 100);
|
||||||
this.xhr.upload.onabort = () => {
|
xhr.upload.onabort = () => {
|
||||||
reject(ABORT_ERROR);
|
reject(ABORT_ERROR);
|
||||||
error(ABORT_ERROR);
|
error(ABORT_ERROR);
|
||||||
clearInterval(id);
|
clearInterval(id);
|
||||||
virtual.afterError();
|
virtual.afterError();
|
||||||
};
|
};
|
||||||
this.xhr.onload = () => {
|
xhr.onload = () => {
|
||||||
clearInterval(id);
|
clearInterval(id);
|
||||||
progress(100);
|
progress(100);
|
||||||
if (this.xhr.status !== 200) {
|
if (xhr.status !== 200) {
|
||||||
virtual.afterError();
|
virtual.afterError();
|
||||||
err(new Error(this.xhr.statusText));
|
reject(new Error(xhr.statusText));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
virtual.afterSuccess();
|
virtual.afterSuccess();
|
||||||
resolve(null);
|
resolve(null);
|
||||||
};
|
};
|
||||||
this.xhr.send(null);
|
xhr.send(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
|
|
@ -462,8 +474,9 @@ async function processFiles(filelist) {
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert.fail(type, `NOT_SUPPORTED type="${type}"`);
|
assert.fail(`NOT_SUPPORTED type="${type}"`);
|
||||||
}
|
}
|
||||||
|
task = assert.truthy(task);
|
||||||
task.virtual.before();
|
task.virtual.before();
|
||||||
tasks.push(task);
|
tasks.push(task);
|
||||||
}
|
}
|
||||||
|
|
@ -497,6 +510,7 @@ async function processItems(itemList) {
|
||||||
exec: workerImplFile,
|
exec: workerImplFile,
|
||||||
virtual: save(path, entrySize),
|
virtual: save(path, entrySize),
|
||||||
done: false,
|
done: false,
|
||||||
|
ready: () => false,
|
||||||
};
|
};
|
||||||
size += entrySize;
|
size += entrySize;
|
||||||
} else if (entry.isDirectory) {
|
} else if (entry.isDirectory) {
|
||||||
|
|
@ -506,12 +520,13 @@ async function processItems(itemList) {
|
||||||
exec: workerImplDirectory,
|
exec: workerImplDirectory,
|
||||||
virtual: mkdir(path),
|
virtual: mkdir(path),
|
||||||
done: false,
|
done: false,
|
||||||
|
ready: () => false,
|
||||||
};
|
};
|
||||||
queue = queue.concat(await new Promise((resolve) => {
|
queue = queue.concat(await new Promise((resolve) => {
|
||||||
entry.createReader().readEntries(resolve);
|
entry.createReader().readEntries(resolve);
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
assert.fail("NOT_IMPLEMENTED - unknown entry type in ctrl_upload.js", entry);
|
assert.fail("NOT_IMPLEMENTED - unknown entry type in ctrl_upload.js");
|
||||||
}
|
}
|
||||||
task.ready = () => {
|
task.ready = () => {
|
||||||
const isInDirectory = (filepath, folder) => folder.indexOf(filepath) === 0;
|
const isInDirectory = (filepath, folder) => folder.indexOf(filepath) === 0;
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,4 @@ function _moveHiddenFilesDownward(fileA, fileB) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isNativeFileUpload = (e) => {
|
export const isNativeFileUpload = (e) => JSON.stringify(e.dataTransfer.types.slice(-1)) === "[\"Files\"]";
|
||||||
return JSON.stringify(e.dataTransfer.types.slice(-1)) === "[\"Files\"]";
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ function renderDesktop(render, removeLabel) {
|
||||||
ret.next();
|
ret.next();
|
||||||
ret.complete();
|
ret.complete();
|
||||||
return ret.toPromise();
|
return ret.toPromise();
|
||||||
}).bind(this, MODAL_RIGHT_BUTTON);
|
}).bind(null, MODAL_RIGHT_BUTTON);
|
||||||
|
|
||||||
$input.focus();
|
$input.focus();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ function renderDesktop(render, filename) {
|
||||||
}
|
}
|
||||||
ret.next(value);
|
ret.next(value);
|
||||||
ret.complete();
|
ret.complete();
|
||||||
}).bind(this, MODAL_RIGHT_BUTTON);
|
}).bind(null, MODAL_RIGHT_BUTTON);
|
||||||
|
|
||||||
const ext = extname(filename);
|
const ext = extname(filename);
|
||||||
$input.value = filename;
|
$input.value = filename;
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,10 @@ export default function(render, { path }) {
|
||||||
render($modal);
|
render($modal);
|
||||||
const ret = new rxjs.Subject();
|
const ret = new rxjs.Subject();
|
||||||
const role$ = new rxjs.BehaviorSubject(null);
|
const role$ = new rxjs.BehaviorSubject(null);
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
form: {},
|
/** @type {object} */ form: {},
|
||||||
links: null,
|
/** @type {any[] | null} */ links: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
// feature: select
|
// feature: select
|
||||||
|
|
@ -84,7 +85,12 @@ export default function(render, { path }) {
|
||||||
body,
|
body,
|
||||||
url: `api/share/${id}`,
|
url: `api/share/${id}`,
|
||||||
}).toPromise();
|
}).toPromise();
|
||||||
state.links.push({ ...body, path: body.path.substring(currentPath().length - 1) });
|
;
|
||||||
|
// if (state.links === null) assert.fail("ttest");
|
||||||
|
assert.truthy(state.links).push({
|
||||||
|
...body,
|
||||||
|
path: body.path.substring(currentPath().length - 1),
|
||||||
|
});
|
||||||
role$.next(null);
|
role$.next(null);
|
||||||
},
|
},
|
||||||
remove: async({ id }) => {
|
remove: async({ id }) => {
|
||||||
|
|
@ -92,7 +98,7 @@ export default function(render, { path }) {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
url: `api/share/${id}`,
|
url: `api/share/${id}`,
|
||||||
}).toPromise();
|
}).toPromise();
|
||||||
state.links = state.links.filter((link) => link.id !== id);
|
state.links = (state.links || []).filter((link) => link && link.id !== id);
|
||||||
role$.next(null);
|
role$.next(null);
|
||||||
},
|
},
|
||||||
all: async() => {
|
all: async() => {
|
||||||
|
|
@ -240,7 +246,7 @@ async function ctrlCreateShare(render, { save, formState }) {
|
||||||
? t("Password")
|
? t("Password")
|
||||||
: label === "url_enable"
|
: label === "url_enable"
|
||||||
? t("Custom Link url")
|
? t("Custom Link url")
|
||||||
: assert.fail(label, "unknown label");
|
: assert.fail("unknown label");
|
||||||
return createElement(`
|
return createElement(`
|
||||||
<div class="component_supercheckbox">
|
<div class="component_supercheckbox">
|
||||||
<label>
|
<label>
|
||||||
|
|
@ -273,7 +279,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(assert.type($form.closest(".component_share"), window.HTMLElement), `input[name="create"]`).value = `${location.origin}${toHref("/s/" + id)}`;
|
qs(assert.type($form.closest(".component_share"), HTMLElement), `input[name="create"]`).value = `${location.origin}${toHref("/s/" + id)}`;
|
||||||
})));
|
})));
|
||||||
|
|
||||||
// feature: create a shared link
|
// feature: create a shared link
|
||||||
|
|
@ -281,7 +287,7 @@ 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(assert.type(qs(document.body, ".component_share form"), window.HTMLFormElement))]
|
const body = [...new FormData(assert.type(qs(document.body, ".component_share form"), HTMLFormElement))]
|
||||||
.reduce((acc, [key, value]) => {
|
.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;
|
||||||
|
|
@ -328,7 +334,7 @@ function shareObjToRole({ can_read, can_write, can_upload }) {
|
||||||
} else if (can_read === true && can_write === true && can_upload === true) {
|
} else if (can_read === true && can_write === true && can_upload === true) {
|
||||||
return "editor";
|
return "editor";
|
||||||
}
|
}
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function copyToClipboard(str) {
|
export function copyToClipboard(str) {
|
||||||
|
|
|
||||||
48
public/assets/pages/filespage/modal_tag.css
Normal file
48
public/assets/pages/filespage/modal_tag.css
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
.component_tag input {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
.component_tag input::placeholder {
|
||||||
|
font-weight: 100;
|
||||||
|
}
|
||||||
|
.component_tag .box {
|
||||||
|
display: flex;
|
||||||
|
background: var(--bg-color);
|
||||||
|
transition: background 0.1s ease;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.component_tag .box.active {
|
||||||
|
background: var(--primary);
|
||||||
|
color: var(--color);
|
||||||
|
}
|
||||||
|
.component_tag .box > div {
|
||||||
|
flex-grow: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.component_tag .box img {
|
||||||
|
width: 20px;
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
.component_tag .box img[alt="close"] {
|
||||||
|
width: 14px;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
.component_tag .box .count {
|
||||||
|
opacity: 0.5;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
.component_tag .box .count:before {
|
||||||
|
content: "[";
|
||||||
|
}
|
||||||
|
.component_tag .box .count:after {
|
||||||
|
content: "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
.component_tag .scroll-y {
|
||||||
|
overflow-y: auto !important;
|
||||||
|
max-height: 200px;
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,32 @@
|
||||||
import { createElement } from "../../lib/skeleton/index.js";
|
import { createElement } from "../../lib/skeleton/index.js";
|
||||||
|
import t from "../../locales/index.js";
|
||||||
|
|
||||||
export default function(render) {
|
export default function(render) {
|
||||||
const $modal = createElement(`
|
const $modal = createElement(`
|
||||||
<div>
|
<div class="component_tag">
|
||||||
TAG MODAL
|
<form>
|
||||||
|
<input class="component_input" type="text" placeholder="${t("Create a Tag")}" value="">
|
||||||
|
</form>
|
||||||
|
<div class="scroll-y">
|
||||||
|
<div class="box no-select">
|
||||||
|
<div>test <span class="count">2</span></div>
|
||||||
|
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgPHBhdGggc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MC41MzMzMzMwOSIgZD0ibSAxNi44ODUsMTUuNDU1IC00LjU5LC00LjU4IC00LjU5LDQuNTggLTEuNDEsLTEuNDEgNiwtNiA2LDYgeiIgLz4KICA8cGF0aCBmaWxsPSJub25lIiBkPSJNMC0uMjVoMjR2MjRIMHoiIC8+Cjwvc3ZnPgo=" alt="arrow_top">
|
||||||
|
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgPHBhdGggc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MC41MzMzMzMyMSIgZD0ibSA3LjcwNSw4LjA0NSA0LjU5LDQuNTggNC41OSwtNC41OCAxLjQxLDEuNDEgLTYsNiAtNiwtNiB6IiAvPgogIDxwYXRoIGZpbGw9Im5vbmUiIGQ9Ik0wLS4yNWgyNHYyNEgweiIgLz4KPC9zdmc+Cg==" alt="arrow_bottom">
|
||||||
|
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MS45NzYgNTEuOTc2Ij4KICA8cGF0aCBzdHlsZT0iZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eTowLjUzMzMzMjg1O3N0cm9rZS13aWR0aDoxLjQ1NjgxMTE5IiBkPSJtIDQxLjAwNTMxLDQwLjg0NDA2MiBjIC0xLjEzNzc2OCwxLjEzNzc2NSAtMi45ODIwODgsMS4xMzc3NjUgLTQuMTE5ODYxLDAgTCAyNi4wNjg2MjgsMzAuMDI3MjM0IDE0LjczNzU1MSw0MS4zNTgzMSBjIC0xLjEzNzc3MSwxLjEzNzc3MSAtMi45ODIwOTMsMS4xMzc3NzEgLTQuMTE5ODYxLDAgLTEuMTM3NzcyMiwtMS4xMzc3NjggLTEuMTM3NzcyMiwtMi45ODIwODggMCwtNC4xMTk4NjEgTCAyMS45NDg3NjYsMjUuOTA3MzcyIDExLjEzMTkzOCwxNS4wOTA1NTEgYyAtMS4xMzc3NjQ3LC0xLjEzNzc3MSAtMS4xMzc3NjQ3LC0yLjk4MzU1MyAwLC00LjExOTg2MSAxLjEzNzc3NCwtMS4xMzc3NzIxIDIuOTgyMDk4LC0xLjEzNzc3MjEgNC4xMTk4NjUsMCBMIDI2LjA2ODYyOCwyMS43ODc1MTIgMzYuMzY5NzM5LDExLjQ4NjM5OSBjIDEuMTM3NzY4LC0xLjEzNzc2OCAyLjk4MjA5MywtMS4xMzc3NjggNC4xMTk4NjIsMCAxLjEzNzc2NywxLjEzNzc2OSAxLjEzNzc2NywyLjk4MjA5NCAwLDQuMTE5ODYyIEwgMzAuMTg4NDg5LDI1LjkwNzM3MiA0MS4wMDUzMSwzNi43MjQxOTcgYyAxLjEzNzc3MSwxLjEzNzc2NyAxLjEzNzc3MSwyLjk4MjA5MSAwLDQuMTE5ODY1IHoiIC8+Cjwvc3ZnPgo=" alt="close">
|
||||||
|
</div>
|
||||||
|
<div class="box no-select">
|
||||||
|
<div>kjhjk <span class="count">1</span></div>
|
||||||
|
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgPHBhdGggc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MC41MzMzMzMwOSIgZD0ibSAxNi44ODUsMTUuNDU1IC00LjU5LC00LjU4IC00LjU5LDQuNTggLTEuNDEsLTEuNDEgNiwtNiA2LDYgeiIgLz4KICA8cGF0aCBmaWxsPSJub25lIiBkPSJNMC0uMjVoMjR2MjRIMHoiIC8+Cjwvc3ZnPgo=" alt="arrow_top">
|
||||||
|
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgPHBhdGggc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MC41MzMzMzMyMSIgZD0ibSA3LjcwNSw4LjA0NSA0LjU5LDQuNTggNC41OSwtNC41OCAxLjQxLDEuNDEgLTYsNiAtNiwtNiB6IiAvPgogIDxwYXRoIGZpbGw9Im5vbmUiIGQ9Ik0wLS4yNWgyNHYyNEgweiIgLz4KPC9zdmc+Cg==" alt="arrow_bottom">
|
||||||
|
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MS45NzYgNTEuOTc2Ij4KICA8cGF0aCBzdHlsZT0iZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eTowLjUzMzMzMjg1O3N0cm9rZS13aWR0aDoxLjQ1NjgxMTE5IiBkPSJtIDQxLjAwNTMxLDQwLjg0NDA2MiBjIC0xLjEzNzc2OCwxLjEzNzc2NSAtMi45ODIwODgsMS4xMzc3NjUgLTQuMTE5ODYxLDAgTCAyNi4wNjg2MjgsMzAuMDI3MjM0IDE0LjczNzU1MSw0MS4zNTgzMSBjIC0xLjEzNzc3MSwxLjEzNzc3MSAtMi45ODIwOTMsMS4xMzc3NzEgLTQuMTE5ODYxLDAgLTEuMTM3NzcyMiwtMS4xMzc3NjggLTEuMTM3NzcyMiwtMi45ODIwODggMCwtNC4xMTk4NjEgTCAyMS45NDg3NjYsMjUuOTA3MzcyIDExLjEzMTkzOCwxNS4wOTA1NTEgYyAtMS4xMzc3NjQ3LC0xLjEzNzc3MSAtMS4xMzc3NjQ3LC0yLjk4MzU1MyAwLC00LjExOTg2MSAxLjEzNzc3NCwtMS4xMzc3NzIxIDIuOTgyMDk4LC0xLjEzNzc3MjEgNC4xMTk4NjUsMCBMIDI2LjA2ODYyOCwyMS43ODc1MTIgMzYuMzY5NzM5LDExLjQ4NjM5OSBjIDEuMTM3NzY4LC0xLjEzNzc2OCAyLjk4MjA5MywtMS4xMzc3NjggNC4xMTk4NjIsMCAxLjEzNzc2NywxLjEzNzc2OSAxLjEzNzc2NywyLjk4MjA5NCAwLDQuMTE5ODYyIEwgMzAuMTg4NDg5LDI1LjkwNzM3MiA0MS4wMDUzMSwzNi43MjQxOTcgYyAxLjEzNzc3MSwxLjEzNzc2NyAxLjEzNzc3MSwyLjk4MjA5MSAwLDQuMTE5ODY1IHoiIC8+Cjwvc3ZnPgo=" alt="close">
|
||||||
|
</div>
|
||||||
|
<div class="box no-select active">
|
||||||
|
<div>jh <span class="count">2</span></div>
|
||||||
|
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgPHBhdGggc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MC41MzMzMzMwOSIgZD0ibSAxNi44ODUsMTUuNDU1IC00LjU5LC00LjU4IC00LjU5LDQuNTggLTEuNDEsLTEuNDEgNiwtNiA2LDYgeiIgLz4KICA8cGF0aCBmaWxsPSJub25lIiBkPSJNMC0uMjVoMjR2MjRIMHoiIC8+Cjwvc3ZnPgo=" alt="arrow_top">
|
||||||
|
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgPHBhdGggc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MC41MzMzMzMyMSIgZD0ibSA3LjcwNSw4LjA0NSA0LjU5LDQuNTggNC41OSwtNC41OCAxLjQxLDEuNDEgLTYsNiAtNiwtNiB6IiAvPgogIDxwYXRoIGZpbGw9Im5vbmUiIGQ9Ik0wLS4yNWgyNHYyNEgweiIgLz4KPC9zdmc+Cg==" alt="arrow_bottom">
|
||||||
|
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MS45NzYgNTEuOTc2Ij4KICA8cGF0aCBzdHlsZT0iZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eTowLjUzMzMzMjg1O3N0cm9rZS13aWR0aDoxLjQ1NjgxMTE5IiBkPSJtIDQxLjAwNTMxLDQwLjg0NDA2MiBjIC0xLjEzNzc2OCwxLjEzNzc2NSAtMi45ODIwODgsMS4xMzc3NjUgLTQuMTE5ODYxLDAgTCAyNi4wNjg2MjgsMzAuMDI3MjM0IDE0LjczNzU1MSw0MS4zNTgzMSBjIC0xLjEzNzc3MSwxLjEzNzc3MSAtMi45ODIwOTMsMS4xMzc3NzEgLTQuMTE5ODYxLDAgLTEuMTM3NzcyMiwtMS4xMzc3NjggLTEuMTM3NzcyMiwtMi45ODIwODggMCwtNC4xMTk4NjEgTCAyMS45NDg3NjYsMjUuOTA3MzcyIDExLjEzMTkzOCwxNS4wOTA1NTEgYyAtMS4xMzc3NjQ3LC0xLjEzNzc3MSAtMS4xMzc3NjQ3LC0yLjk4MzU1MyAwLC00LjExOTg2MSAxLjEzNzc3NCwtMS4xMzc3NzIxIDIuOTgyMDk4LC0xLjEzNzc3MjEgNC4xMTk4NjUsMCBMIDI2LjA2ODYyOCwyMS43ODc1MTIgMzYuMzY5NzM5LDExLjQ4NjM5OSBjIDEuMTM3NzY4LC0xLjEzNzc2OCAyLjk4MjA5MywtMS4xMzc3NjggNC4xMTk4NjIsMCAxLjEzNzc2NywxLjEzNzc2OSAxLjEzNzc2NywyLjk4MjA5NCAwLDQuMTE5ODYyIEwgMzAuMTg4NDg5LDI1LjkwNzM3MiA0MS4wMDUzMSwzNi43MjQxOTcgYyAxLjEzNzc3MSwxLjEzNzc2NyAxLjEzNzc3MSwyLjk4MjA5MSAwLDQuMTE5ODY1IHoiIC8+Cjwvc3ZnPgo=" alt="close">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
render($modal, ({ id }) => {
|
render($modal, ({ id }) => {
|
||||||
|
|
|
||||||
|
|
@ -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 && assert.type(document.activeElement, window.HTMLElement).tagName !== "INPUT"),
|
rxjs.filter((e) => e.keyCode === 82 && assert.type(document.activeElement, HTMLElement).tagName !== "INPUT"),
|
||||||
)).pipe(rxjs.switchMap(() => lsFromHttp(path))),
|
)).pipe(rxjs.switchMap(() => lsFromHttp(path))),
|
||||||
),
|
),
|
||||||
).pipe(
|
).pipe(
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ const mutationFiles$ = new rxjs.BehaviorSubject({
|
||||||
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"); }
|
||||||
async afterError() { throw new Error("NOT_IMPLEMENTED"); }
|
async afterError() { return rxjs.EMPTY; }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function withVirtualLayer(ajax$, mutate) {
|
export function withVirtualLayer(ajax$, mutate) {
|
||||||
|
|
@ -51,6 +51,9 @@ export function touch(path) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return new class TouchVL extends IVirtualLayer {
|
return new class TouchVL extends IVirtualLayer {
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
before() {
|
before() {
|
||||||
stateAdd(virtualFiles$, basepath, {
|
stateAdd(virtualFiles$, basepath, {
|
||||||
...file,
|
...file,
|
||||||
|
|
@ -58,6 +61,9 @@ export function touch(path) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async afterSuccess() {
|
async afterSuccess() {
|
||||||
removeLoading(virtualFiles$, basepath, filename);
|
removeLoading(virtualFiles$, basepath, filename);
|
||||||
onDestroy(() => statePop(virtualFiles$, basepath, filename));
|
onDestroy(() => statePop(virtualFiles$, basepath, filename));
|
||||||
|
|
@ -68,6 +74,9 @@ export function touch(path) {
|
||||||
hooks.mutation.emit({ op: "touch", path: basepath });
|
hooks.mutation.emit({ op: "touch", path: basepath });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async afterError() {
|
async afterError() {
|
||||||
statePop(virtualFiles$, basepath, filename);
|
statePop(virtualFiles$, basepath, filename);
|
||||||
return rxjs.of(fscache().remove(basepath)).pipe(
|
return rxjs.of(fscache().remove(basepath)).pipe(
|
||||||
|
|
@ -87,6 +96,9 @@ export function mkdir(path) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return new class MkdirVL extends IVirtualLayer {
|
return new class MkdirVL extends IVirtualLayer {
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
before() {
|
before() {
|
||||||
stateAdd(virtualFiles$, basepath, {
|
stateAdd(virtualFiles$, basepath, {
|
||||||
...file,
|
...file,
|
||||||
|
|
@ -95,6 +107,9 @@ export function mkdir(path) {
|
||||||
statePop(mutationFiles$, basepath, dirname); // case: rm followed by mkdir
|
statePop(mutationFiles$, basepath, dirname); // case: rm followed by mkdir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async afterSuccess() {
|
async afterSuccess() {
|
||||||
removeLoading(virtualFiles$, basepath, dirname);
|
removeLoading(virtualFiles$, basepath, dirname);
|
||||||
onDestroy(() => statePop(virtualFiles$, basepath, dirname));
|
onDestroy(() => statePop(virtualFiles$, basepath, dirname));
|
||||||
|
|
@ -105,6 +120,9 @@ export function mkdir(path) {
|
||||||
hooks.mutation.emit({ op: "mkdir", path: basepath });
|
hooks.mutation.emit({ op: "mkdir", path: basepath });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async afterError() {
|
async afterError() {
|
||||||
statePop(virtualFiles$, basepath, dirname);
|
statePop(virtualFiles$, basepath, dirname);
|
||||||
return rxjs.of(fscache().remove(basepath)).pipe(
|
return rxjs.of(fscache().remove(basepath)).pipe(
|
||||||
|
|
@ -124,6 +142,9 @@ export function save(path, size) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return new class SaveVL extends IVirtualLayer {
|
return new class SaveVL extends IVirtualLayer {
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
before() {
|
before() {
|
||||||
stateAdd(virtualFiles$, basepath, {
|
stateAdd(virtualFiles$, basepath, {
|
||||||
...file,
|
...file,
|
||||||
|
|
@ -132,6 +153,9 @@ export function save(path, size) {
|
||||||
statePop(mutationFiles$, basepath, filename); // eg: rm followed by save
|
statePop(mutationFiles$, basepath, filename); // eg: rm followed by save
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async afterSuccess() {
|
async afterSuccess() {
|
||||||
removeLoading(virtualFiles$, basepath, filename);
|
removeLoading(virtualFiles$, basepath, filename);
|
||||||
onDestroy(() => statePop(virtualFiles$, basepath, filename));
|
onDestroy(() => statePop(virtualFiles$, basepath, filename));
|
||||||
|
|
@ -142,6 +166,9 @@ export function save(path, size) {
|
||||||
hooks.mutation.emit({ op: "save", path: basepath });
|
hooks.mutation.emit({ op: "save", path: basepath });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async afterError() {
|
async afterError() {
|
||||||
statePop(virtualFiles$, basepath, filename);
|
statePop(virtualFiles$, basepath, filename);
|
||||||
return rxjs.EMPTY;
|
return rxjs.EMPTY;
|
||||||
|
|
@ -160,6 +187,9 @@ export function rm(...paths) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return new class RmVL extends IVirtualLayer {
|
return new class RmVL extends IVirtualLayer {
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
before() {
|
before() {
|
||||||
for (let i=0; i<arr.length; i+=2) {
|
for (let i=0; i<arr.length; i+=2) {
|
||||||
stateAdd(mutationFiles$, arr[i], {
|
stateAdd(mutationFiles$, arr[i], {
|
||||||
|
|
@ -176,6 +206,9 @@ export function rm(...paths) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async afterSuccess() {
|
async afterSuccess() {
|
||||||
for (let i=0; i<arr.length; i+=2) {
|
for (let i=0; i<arr.length; i+=2) {
|
||||||
stateAdd(mutationFiles$, arr[i], {
|
stateAdd(mutationFiles$, arr[i], {
|
||||||
|
|
@ -208,6 +241,9 @@ export function rm(...paths) {
|
||||||
if (arr.length > 0) hooks.mutation.emit({ op: "rm", path: arr[0] });
|
if (arr.length > 0) hooks.mutation.emit({ op: "rm", path: arr[0] });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async afterError() {
|
async afterError() {
|
||||||
for (let i=0; i<arr.length; i+=2) {
|
for (let i=0; i<arr.length; i+=2) {
|
||||||
stateAdd(mutationFiles$, arr[i], {
|
stateAdd(mutationFiles$, arr[i], {
|
||||||
|
|
@ -232,6 +268,9 @@ export function mv(fromPath, toPath) {
|
||||||
let type = null;
|
let type = null;
|
||||||
|
|
||||||
return new class MvVL extends IVirtualLayer {
|
return new class MvVL extends IVirtualLayer {
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
before() {
|
before() {
|
||||||
if (fromBasepath === toBasepath) this._beforeSamePath();
|
if (fromBasepath === toBasepath) this._beforeSamePath();
|
||||||
else this._beforeSamePath();
|
else this._beforeSamePath();
|
||||||
|
|
@ -270,6 +309,9 @@ export function mv(fromPath, toPath) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async afterSuccess() {
|
async afterSuccess() {
|
||||||
fscache().remove(fromPath, false);
|
fscache().remove(fromPath, false);
|
||||||
if (fromBasepath === toBasepath) await this._afterSuccessSamePath();
|
if (fromBasepath === toBasepath) await this._afterSuccessSamePath();
|
||||||
|
|
@ -325,6 +367,9 @@ export function mv(fromPath, toPath) {
|
||||||
hooks.mutation.emit({ op: "mv", path: toBasepath });
|
hooks.mutation.emit({ op: "mv", path: toBasepath });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
async afterError() {
|
async afterError() {
|
||||||
statePop(mutationFiles$, fromBasepath, fromName);
|
statePop(mutationFiles$, fromBasepath, fromName);
|
||||||
if (fromBasepath !== toBasepath) {
|
if (fromBasepath !== toBasepath) {
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ import { settingsGet, settingsSave } from "../../lib/store.js";
|
||||||
let state$ = null;
|
let state$ = null;
|
||||||
export function init() {
|
export function init() {
|
||||||
state$ = new rxjs.BehaviorSubject(settingsGet({
|
state$ = new rxjs.BehaviorSubject(settingsGet({
|
||||||
view: window.CONFIG.default_view || "grid",
|
view: window.CONFIG["default_view"] || "grid",
|
||||||
show_hidden: window.CONFIG.display_hidden || false,
|
show_hidden: window.CONFIG["display_hidden"] || false,
|
||||||
sort: window.CONFIG.default_sort || "type",
|
sort: window.CONFIG["default_sort"] || "type",
|
||||||
order: null,
|
order: null,
|
||||||
search: "",
|
search: "",
|
||||||
}, "filespage"));
|
}, "filespage"));
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ const selection$ = new rxjs.BehaviorSubject([
|
||||||
onDestroy(clearSelection);
|
onDestroy(clearSelection);
|
||||||
|
|
||||||
export function addSelection({ shift = false, n = 0, ...rest }) {
|
export function addSelection({ shift = false, n = 0, ...rest }) {
|
||||||
// console.log(n, shift)
|
|
||||||
const selections = selection$.value;
|
const selections = selection$.value;
|
||||||
const selection = { type: shift ? "range" : "anchor", n, ...rest };
|
const selection = { type: shift ? "range" : "anchor", n, ...rest };
|
||||||
|
|
||||||
|
|
|
||||||
3
public/assets/pages/filespage/thing.d.ts
vendored
Normal file
3
public/assets/pages/filespage/thing.d.ts
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export function init();
|
||||||
|
|
||||||
|
export function createThing(any): HTMLElement;
|
||||||
|
|
@ -70,7 +70,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 = assert.type($tmpl.cloneNode(true), window.HTMLElement);
|
const $thing = assert.type($tmpl.cloneNode(true), 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.
|
||||||
|
|
@ -107,9 +107,9 @@ export function createThing({
|
||||||
|
|
||||||
$img.src = "api/files/cat?path=" + encodeURIComponent(path) + "&thumbnail=true" + location.search.replace("?", "&");
|
$img.src = "api/files/cat?path=" + encodeURIComponent(path) + "&thumbnail=true" + location.search.replace("?", "&");
|
||||||
$img.loaded = false;
|
$img.loaded = false;
|
||||||
const t = new Date();
|
const t = new Date().getTime();
|
||||||
$img.onload = async() => {
|
$img.onload = async() => {
|
||||||
const duration = new Date() - t;
|
const duration = new Date().getTime() - t;
|
||||||
$img.loaded = true;
|
$img.loaded = true;
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
animate($img, {
|
animate($img, {
|
||||||
|
|
|
||||||
1
public/assets/pages/viewerpage/application_3d.d.ts
vendored
Normal file
1
public/assets/pages/viewerpage/application_3d.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export default function(any): void;
|
||||||
|
|
@ -3,3 +3,5 @@ interface Window {
|
||||||
create: (options: any) => any;
|
create: (options: any) => any;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function(any): void;
|
||||||
|
|
@ -3,3 +3,5 @@ interface Window {
|
||||||
Book: new (options: any) => any;
|
Book: new (options: any) => any;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function(any): void;
|
||||||
|
|
@ -60,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(assert.type(qs(document.body, "iframe"), window.HTMLElement).contentDocument.body, "keydown"))),
|
rendition$.pipe(rxjs.mergeMap(() => rxjs.fromEvent(assert.type(qs(document.body, "iframe"), HTMLElement).contentDocument.body, "keydown"))),
|
||||||
)),
|
)),
|
||||||
rxjs.map((e) => {
|
rxjs.map((e) => {
|
||||||
switch (e.code) {
|
switch (e.code) {
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,5 @@ interface Window {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function(any): void;
|
||||||
|
|
@ -97,7 +97,7 @@ export default async function(render, { acl$ }) {
|
||||||
"Ctrl-X Ctrl-C": () => window.history.back(),
|
"Ctrl-X Ctrl-C": () => window.history.back(),
|
||||||
});
|
});
|
||||||
if (mode === "orgmode") {
|
if (mode === "orgmode") {
|
||||||
const cleanup = CodeMirror.orgmode.init(editor);
|
const cleanup = window.CodeMirror.orgmode.init(editor);
|
||||||
onDestroy(cleanup);
|
onDestroy(cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,14 @@ import "../../../lib/vendor/codemirror/addon/mode/simple.js";
|
||||||
import {
|
import {
|
||||||
org_cycle, org_shifttab, org_metaleft, org_metaright, org_meta_return, org_metaup,
|
org_cycle, org_shifttab, org_metaleft, org_metaright, org_meta_return, org_metaup,
|
||||||
org_metadown, org_insert_todo_heading, org_shiftleft, org_shiftright, fold, unfold,
|
org_metadown, org_insert_todo_heading, org_shiftleft, org_shiftright, fold, unfold,
|
||||||
isFold, org_set_fold, org_shiftmetaleft, org_shiftmetaright,
|
isFold, org_shiftmetaleft, org_shiftmetaright,
|
||||||
} from "./emacs-org.js";
|
} from "./emacs-org.js";
|
||||||
import { getCurrentPath } from "../common.js";
|
|
||||||
import { join } from "../../../lib/path.js";
|
import { join } from "../../../lib/path.js";
|
||||||
import { currentShare } from "../../filespage/cache.js";
|
import { currentShare } from "../../filespage/cache.js";
|
||||||
|
|
||||||
window.CodeMirror.__mode = "orgmode";
|
window.CodeMirror.__mode = "orgmode";
|
||||||
|
|
||||||
CodeMirror.defineSimpleMode("orgmode", {
|
window.CodeMirror.defineSimpleMode("orgmode", {
|
||||||
start: [
|
start: [
|
||||||
{ regex: /(\*\s)(TODO|DOING|WAITING|NEXT|PENDING|)(CANCELLED|CANCELED|CANCEL|DONE|REJECTED|STOP|STOPPED|)(\s+\[\#[A-C]\]\s+|)(.*?)(?:(\s{10,}|))(\:[\S]+\:|)$/, sol: true, token: ["header level1 org-level-star", "header level1 org-todo", "header level1 org-done", "header level1 org-priority", "header level1", "header level1 void", "header level1 comment"] },
|
{ regex: /(\*\s)(TODO|DOING|WAITING|NEXT|PENDING|)(CANCELLED|CANCELED|CANCEL|DONE|REJECTED|STOP|STOPPED|)(\s+\[\#[A-C]\]\s+|)(.*?)(?:(\s{10,}|))(\:[\S]+\:|)$/, sol: true, token: ["header level1 org-level-star", "header level1 org-todo", "header level1 org-done", "header level1 org-priority", "header level1", "header level1 void", "header level1 comment"] },
|
||||||
{ regex: /(\*{1,}\s)(TODO|DOING|WAITING|NEXT|PENDING|)(CANCELLED|CANCELED|CANCEL|DEFERRED|DONE|REJECTED|STOP|STOPPED|)(\s+\[\#[A-C]\]\s+|)(.*?)(?:(\s{10,}|))(\:[\S]+\:|)$/, sol: true, token: ["header org-level-star", "header org-todo", "header org-done", "header org-priority", "header", "header void", "header comment"] },
|
{ regex: /(\*{1,}\s)(TODO|DOING|WAITING|NEXT|PENDING|)(CANCELLED|CANCELED|CANCEL|DEFERRED|DONE|REJECTED|STOP|STOPPED|)(\s+\[\#[A-C]\]\s+|)(.*?)(?:(\s{10,}|))(\:[\S]+\:|)$/, sol: true, token: ["header org-level-star", "header org-todo", "header org-done", "header org-priority", "header", "header void", "header comment"] },
|
||||||
|
|
@ -34,7 +33,7 @@ CodeMirror.defineSimpleMode("orgmode", {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
CodeMirror.registerHelper("fold", "orgmode", function(cm, start) {
|
window.CodeMirror.registerHelper("fold", "orgmode", function(cm, start) {
|
||||||
// init
|
// init
|
||||||
const levelToMatch = headerLevel(start.line);
|
const levelToMatch = headerLevel(start.line);
|
||||||
|
|
||||||
|
|
@ -54,21 +53,21 @@ CodeMirror.registerHelper("fold", "orgmode", function(cm, start) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
from: CodeMirror.Pos(start.line, cm.getLine(start.line).length),
|
from: window.CodeMirror.Pos(start.line, cm.getLine(start.line).length),
|
||||||
to: CodeMirror.Pos(end, cm.getLine(end).length),
|
to: window.CodeMirror.Pos(end, cm.getLine(end).length),
|
||||||
};
|
};
|
||||||
|
|
||||||
function headerLevel(lineNo) {
|
function headerLevel(lineNo) {
|
||||||
const line = cm.getLine(lineNo);
|
const line = cm.getLine(lineNo);
|
||||||
const match = /^\*+/.exec(line);
|
const match = /^\*+/.exec(line);
|
||||||
if (match && match.length === 1 && /header/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0)))) {
|
if (match && match.length === 1 && /header/.test(cm.getTokenTypeAt(window.CodeMirror.Pos(lineNo, 0)))) {
|
||||||
return match[0].length;
|
return match[0].length;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
CodeMirror.registerGlobalHelper("fold", "drawer", function(mode) {
|
window.CodeMirror.registerGlobalHelper("fold", "drawer", function(mode) {
|
||||||
return mode.name === "orgmode";
|
return mode.name === "orgmode";
|
||||||
}, function(cm, start) {
|
}, function(cm, start) {
|
||||||
const drawer = isBeginningOfADrawer(start.line);
|
const drawer = isBeginningOfADrawer(start.line);
|
||||||
|
|
@ -85,8 +84,8 @@ CodeMirror.registerGlobalHelper("fold", "drawer", function(mode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
from: CodeMirror.Pos(start.line, cm.getLine(start.line).length),
|
from: window.CodeMirror.Pos(start.line, cm.getLine(start.line).length),
|
||||||
to: CodeMirror.Pos(end, cm.getLine(end).length),
|
to: window.CodeMirror.Pos(end, cm.getLine(end).length),
|
||||||
};
|
};
|
||||||
|
|
||||||
function isBeginningOfADrawer(lineNo) {
|
function isBeginningOfADrawer(lineNo) {
|
||||||
|
|
@ -103,9 +102,9 @@ CodeMirror.registerGlobalHelper("fold", "drawer", function(mode) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
CodeMirror.registerHelper("orgmode", "init", (editor) => {
|
window.CodeMirror.registerHelper("orgmode", "init", (editor) => {
|
||||||
editor.setOption("extraKeys", {
|
editor.setOption("extraKeys", {
|
||||||
"Tab": (cm) => org_cycle(cm),
|
Tab: (cm) => org_cycle(cm),
|
||||||
"Shift-Tab": (cm) => org_shifttab(cm),
|
"Shift-Tab": (cm) => org_shifttab(cm),
|
||||||
"Alt-Left": (cm) => org_metaleft(cm),
|
"Alt-Left": (cm) => org_metaleft(cm),
|
||||||
"Alt-Right": (cm) => org_metaright(cm),
|
"Alt-Right": (cm) => org_metaright(cm),
|
||||||
|
|
@ -126,15 +125,15 @@ CodeMirror.registerHelper("orgmode", "init", (editor) => {
|
||||||
// fold everything except headers by default
|
// fold everything except headers by default
|
||||||
editor.operation(function() {
|
editor.operation(function() {
|
||||||
for (let i = 0; i < editor.lineCount(); i++) {
|
for (let i = 0; i < editor.lineCount(); i++) {
|
||||||
if (/header/.test(editor.getTokenTypeAt(CodeMirror.Pos(i, 0))) === false) {
|
if (/header/.test(editor.getTokenTypeAt(window.CodeMirror.Pos(i, 0))) === false) {
|
||||||
fold(editor, CodeMirror.Pos(i, 0));
|
fold(editor, window.CodeMirror.Pos(i, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return CodeMirror.orgmode.destroy.bind(this, editor);
|
return window.CodeMirror.orgmode.destroy.bind(this, editor);
|
||||||
});
|
});
|
||||||
|
|
||||||
CodeMirror.registerHelper("orgmode", "destroy", (editor) => {
|
window.CodeMirror.registerHelper("orgmode", "destroy", (editor) => {
|
||||||
editor.off("mousedown", toggleHandler);
|
editor.off("mousedown", toggleHandler);
|
||||||
editor.off("touchstart", toggleHandler);
|
editor.off("touchstart", toggleHandler);
|
||||||
editor.off("gutterClick", foldLine);
|
editor.off("gutterClick", foldLine);
|
||||||
|
|
@ -272,7 +271,7 @@ function toggleHandler(cm, e) {
|
||||||
if (/^https?\:\/\//.test(src)) {
|
if (/^https?\:\/\//.test(src)) {
|
||||||
$img.src = src;
|
$img.src = src;
|
||||||
} else {
|
} else {
|
||||||
let path = join(location, src).replace(/^\/view/, "");
|
const path = join(location, src).replace(/^\/view/, "");
|
||||||
$img.src = "/api/files/cat?path=" + path;
|
$img.src = "/api/files/cat?path=" + path;
|
||||||
const share = currentShare();
|
const share = currentShare();
|
||||||
if (share) $img.src += "&share=" + share;
|
if (share) $img.src += "&share=" + share;
|
||||||
|
|
|
||||||
8
public/assets/pages/viewerpage/application_image.d.ts
vendored
Normal file
8
public/assets/pages/viewerpage/application_image.d.ts
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
interface Window {
|
||||||
|
EXIF: {
|
||||||
|
getAllTags: (any) => object;
|
||||||
|
getData: (HTMLElement, any) => void;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function(any): void;
|
||||||
|
|
@ -30,17 +30,20 @@ export default function(render) {
|
||||||
render($page);
|
render($page);
|
||||||
transition(qs($page, ".component_image_container"));
|
transition(qs($page, ".component_image_container"));
|
||||||
|
|
||||||
const toggleInfo = () => qs($page, ".images_aside").classList.toggle("open");
|
|
||||||
const $imgContainer = qs($page, ".images_wrapper");
|
const $imgContainer = qs($page, ".images_wrapper");
|
||||||
const $photo = qs($page, "img.photo");
|
const $photo = qs($page, "img.photo");
|
||||||
const removeLoader = createLoader($imgContainer);
|
const removeLoader = createLoader($imgContainer);
|
||||||
const load$ = new rxjs.BehaviorSubject(null);
|
const load$ = new rxjs.BehaviorSubject(null);
|
||||||
|
const toggleInfo = () => {
|
||||||
|
qs($page, ".images_aside").classList.toggle("open");
|
||||||
|
componentMetadata(createRender(qs($page, ".images_aside")), { toggle: toggleInfo, load$ });
|
||||||
|
};
|
||||||
|
|
||||||
renderMenubar(
|
renderMenubar(
|
||||||
qs($page, "component-menubar"),
|
qs($page, "component-menubar"),
|
||||||
buttonDownload(getFilename(), getDownloadUrl()),
|
buttonDownload(getFilename(), getDownloadUrl()),
|
||||||
buttonFullscreen(qs($page, ".component_image_container")),
|
buttonFullscreen(qs($page, ".component_image_container")),
|
||||||
buttonInfo({ $img: $photo, toggle: toggleInfo }),
|
buttonInfo({ toggle: toggleInfo }),
|
||||||
);
|
);
|
||||||
|
|
||||||
effect(onLoad($photo).pipe(
|
effect(onLoad($photo).pipe(
|
||||||
|
|
@ -59,7 +62,7 @@ export default function(render) {
|
||||||
],
|
],
|
||||||
})),
|
})),
|
||||||
rxjs.catchError((err) => {
|
rxjs.catchError((err) => {
|
||||||
if (err.target instanceof window.HTMLElement && err.type === "error") {
|
if (err.target instanceof HTMLElement && err.type === "error") {
|
||||||
return rxjs.of($photo).pipe(
|
return rxjs.of($photo).pipe(
|
||||||
removeLoader,
|
removeLoader,
|
||||||
rxjs.tap(($img) => {
|
rxjs.tap(($img) => {
|
||||||
|
|
@ -79,11 +82,10 @@ export default function(render) {
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
|
|
||||||
componentMetadata(createRender(qs($page, ".images_aside")), { toggle: toggleInfo, load$ });
|
|
||||||
componentPager(createRender(qs($page, ".component_pager")));
|
componentPager(createRender(qs($page, ".component_pager")));
|
||||||
}
|
}
|
||||||
|
|
||||||
function buttonInfo({ $img, toggle }) {
|
function buttonInfo({ toggle }) {
|
||||||
const $el = createElement(`
|
const $el = createElement(`
|
||||||
<span>
|
<span>
|
||||||
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCgwLjg4MiwwLDAsMC44ODIsNS45LDUuOSkiPgogICAgPHBhdGggc3R5bGU9ImZpbGw6I2YyZjJmMjtmaWxsLW9wYWNpdHk6MSIgZD0ibSA2Mi4xNjIsMCBjIDYuNjk2LDAgMTAuMDQzLDQuNTY3IDEwLjA0Myw5Ljc4OSAwLDYuNTIyIC01LjgxNCwxMi41NTUgLTEzLjM5MSwxMi41NTUgLTYuMzQ0LDAgLTEwLjA0NSwtMy43NTIgLTkuODY5LC05Ljk0NyBDIDQ4Ljk0NSw3LjE3NiA1My4zNSwwIDYyLjE2MiwwIFogTSA0MS41NDMsMTAwIGMgLTUuMjg3LDAgLTkuMTY0LC0zLjI2MiAtNS40NjMsLTE3LjYxNSBsIDYuMDcsLTI1LjQ1NyBjIDEuMDU3LC00LjA3NyAxLjIzLC01LjcwNyAwLC01LjcwNyAtMS41ODgsMCAtOC40NTEsMi44MTYgLTEyLjUxLDUuNTkgTCAyNyw1Mi40MDYgQyAzOS44NjMsNDEuNDggNTQuNjYyLDM1LjA3MiA2MS4wMDQsMzUuMDcyIGMgNS4yODUsMCA2LjE2OCw2LjM2MSAzLjUyNSwxNi4xNDggTCA1Ny41OCw3Ny45OCBjIC0xLjIzNCw0LjcyOSAtMC43MDMsNi4zNTkgMC41MjcsNi4zNTkgMS41ODYsMCA2Ljc4NywtMS45NjMgMTEuODk2LC02LjA0MSBMIDczLDgyLjM3NyBDIDYwLjQ4OCw5NS4xIDQ2LjgzLDEwMCA0MS41NDMsMTAwIFoiIC8+CiAgPC9nPgo8L3N2Zz4K" alt="info">
|
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCgwLjg4MiwwLDAsMC44ODIsNS45LDUuOSkiPgogICAgPHBhdGggc3R5bGU9ImZpbGw6I2YyZjJmMjtmaWxsLW9wYWNpdHk6MSIgZD0ibSA2Mi4xNjIsMCBjIDYuNjk2LDAgMTAuMDQzLDQuNTY3IDEwLjA0Myw5Ljc4OSAwLDYuNTIyIC01LjgxNCwxMi41NTUgLTEzLjM5MSwxMi41NTUgLTYuMzQ0LDAgLTEwLjA0NSwtMy43NTIgLTkuODY5LC05Ljk0NyBDIDQ4Ljk0NSw3LjE3NiA1My4zNSwwIDYyLjE2MiwwIFogTSA0MS41NDMsMTAwIGMgLTUuMjg3LDAgLTkuMTY0LC0zLjI2MiAtNS40NjMsLTE3LjYxNSBsIDYuMDcsLTI1LjQ1NyBjIDEuMDU3LC00LjA3NyAxLjIzLC01LjcwNyAwLC01LjcwNyAtMS41ODgsMCAtOC40NTEsMi44MTYgLTEyLjUxLDUuNTkgTCAyNyw1Mi40MDYgQyAzOS44NjMsNDEuNDggNTQuNjYyLDM1LjA3MiA2MS4wMDQsMzUuMDcyIGMgNS4yODUsMCA2LjE2OCw2LjM2MSAzLjUyNSwxNi4xNDggTCA1Ny41OCw3Ny45OCBjIC0xLjIzNCw0LjcyOSAtMC43MDMsNi4zNTkgMC41MjcsNi4zNTkgMS41ODYsMCA2Ljc4NywtMS45NjMgMTEuODk2LC02LjA0MSBMIDczLDgyLjM3NyBDIDYwLjQ4OCw5NS4xIDQ2LjgzLDEwMCA0MS41NDMsMTAwIFoiIC8+CiAgPC9nPgo8L3N2Zz4K" alt="info">
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { createElement, createRender, onDestroy } from "../../lib/skeleton/index.js";
|
import { createElement, createRender } from "../../lib/skeleton/index.js";
|
||||||
import rxjs, { effect, onClick, onLoad } from "../../lib/rx.js";
|
import rxjs, { effect, onClick } from "../../lib/rx.js";
|
||||||
import { qs } from "../../lib/dom.js";
|
import { qs } from "../../lib/dom.js";
|
||||||
import assert from "../../lib/assert.js";
|
|
||||||
import t from "../../locales/index.js";
|
import t from "../../locales/index.js";
|
||||||
import { loadJS, loadCSS } from "../../helpers/loader.js";
|
import { loadJS, loadCSS } from "../../helpers/loader.js";
|
||||||
|
|
||||||
|
|
@ -30,7 +29,7 @@ function componentHeader(render, { toggle }) {
|
||||||
`);
|
`);
|
||||||
render($header);
|
render($header);
|
||||||
|
|
||||||
effect(onClick($header, `[alt="close"]`).pipe(rxjs.tap(toggle)));
|
effect(onClick(qs($header, `[alt="close"]`)).pipe(rxjs.tap(toggle)));
|
||||||
}
|
}
|
||||||
|
|
||||||
function componentBody(render, { load$ }) {
|
function componentBody(render, { load$ }) {
|
||||||
|
|
@ -72,7 +71,7 @@ async function componentMap(render, { metadata }) {
|
||||||
if (!d || d.length !== 4) return null;
|
if (!d || d.length !== 4) return null;
|
||||||
const [degrees, minutes, seconds, direction] = d;
|
const [degrees, minutes, seconds, direction] = d;
|
||||||
const dd = degrees + minutes/60 + seconds/(60*60);
|
const dd = degrees + minutes/60 + seconds/(60*60);
|
||||||
return direction == "S" || direction == "W" ? -dd : dd;
|
return direction === "S" || direction === "W" ? -dd : dd;
|
||||||
};
|
};
|
||||||
const lat = DMSToDD(metadata.location[0]);
|
const lat = DMSToDD(metadata.location[0]);
|
||||||
const lng = DMSToDD(metadata.location[1]);
|
const lng = DMSToDD(metadata.location[1]);
|
||||||
|
|
@ -100,9 +99,11 @@ async function componentMap(render, { metadata }) {
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||||
const TILE_SERVER = "https://tile.openstreetmap.org/${z}/${x}/${y}.png";
|
const TILE_SERVER = "https://tile.openstreetmap.org/${z}/${x}/${y}.png";
|
||||||
const TILE_SIZE = parseInt($page.clientWidth / 3 * 100) / 100;
|
const TILE_SIZE = Math.floor($page.clientWidth / 3 * 100) / 100;
|
||||||
|
if (TILE_SIZE === 0) return;
|
||||||
$page.style.height = "${TILE_SIZE*3}px;";
|
$page.style.height = "${TILE_SIZE*3}px;";
|
||||||
const mapper = function map_url(lat, lng, zoom) {
|
const defaultTo = (val, def) => val === undefined ? val : def;
|
||||||
|
const mapper = (function map_url(lat, lng, zoom) {
|
||||||
// https://wiki.openstreetmap.org/wiki/Slippy_map_tilenamse
|
// https://wiki.openstreetmap.org/wiki/Slippy_map_tilenamse
|
||||||
const n = Math.pow(2, zoom);
|
const n = Math.pow(2, zoom);
|
||||||
const tile_numbers = [
|
const tile_numbers = [
|
||||||
|
|
@ -113,19 +114,21 @@ async function componentMap(render, { metadata }) {
|
||||||
return {
|
return {
|
||||||
tile: function(tile_server, x = 0, y = 0) {
|
tile: function(tile_server, x = 0, y = 0) {
|
||||||
return tile_server
|
return tile_server
|
||||||
.replace("${x}", Math.floor(tile_numbers[0])+x)
|
.replace("${x}", Math.floor(tile_numbers[0] || 0)+x)
|
||||||
.replace("${y}", Math.floor(tile_numbers[1])+y)
|
.replace("${y}", Math.floor(tile_numbers[1] || 0)+y)
|
||||||
.replace("${z}", Math.floor(zoom));
|
.replace("${z}", Math.floor(zoom));
|
||||||
},
|
},
|
||||||
position: function() {
|
position: function() {
|
||||||
|
const t0 = defaultTo(tile_numbers[0], 0);
|
||||||
|
const t1 = defaultTo(tile_numbers[1], 0);
|
||||||
return [
|
return [
|
||||||
tile_numbers[0] - Math.floor(tile_numbers[0]),
|
t0 - Math.floor(t0),
|
||||||
tile_numbers[1] - Math.floor(tile_numbers[1]),
|
t1 - Math.floor(t1),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}(lat, lng, 11);
|
}(lat, lng, 11));
|
||||||
const center = (position, i) => parseInt(TILE_SIZE * (1 + position[i]) * 1000)/1000;
|
const center = (position, i) => Math.floor(TILE_SIZE * (1 + position[i]) * 1000)/1000;
|
||||||
const $tiles = createElement(`
|
const $tiles = createElement(`
|
||||||
<div class="bigpicture">
|
<div class="bigpicture">
|
||||||
<div class="line">
|
<div class="line">
|
||||||
|
|
@ -146,9 +149,10 @@ async function componentMap(render, { metadata }) {
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
qs($page, `[data-bind="maptile"]`).appendChild($tiles);
|
qs($page, `[data-bind="maptile"]`).appendChild($tiles);
|
||||||
|
const pos = mapper.position();
|
||||||
qs($page, ".marker").setAttribute("style", `
|
qs($page, ".marker").setAttribute("style", `
|
||||||
left: ${TILE_SIZE * (1 + mapper.position()[0]) - 15}px;
|
left: ${TILE_SIZE * (1 + defaultTo(pos[0], 0)) - 15}px;
|
||||||
top: ${TILE_SIZE * (1 + mapper.position()[1]) - 30}px;
|
top: ${TILE_SIZE * (1 + defaultTo(pos[1], 0)) - 30}px;
|
||||||
`);
|
`);
|
||||||
$tiles.setAttribute("style", `transform-origin: ${center(mapper.position(), 0)}px ${center(mapper.position(), 1)}px;`);
|
$tiles.setAttribute("style", `transform-origin: ${center(mapper.position(), 0)}px ${center(mapper.position(), 1)}px;`);
|
||||||
}
|
}
|
||||||
|
|
@ -166,13 +170,13 @@ function componentMore(render, { metadata }) {
|
||||||
const formatValue = (str) => {
|
const formatValue = (str) => {
|
||||||
if (!metadata.all || metadata.all[str] === undefined) return "-";
|
if (!metadata.all || metadata.all[str] === undefined) return "-";
|
||||||
if (typeof metadata.all[str] === "number") {
|
if (typeof metadata.all[str] === "number") {
|
||||||
return parseInt(metadata.all[str]*100)/100;
|
return Math.floor(metadata.all[str]*100)/100;
|
||||||
} else if (metadata.all[str].denominator !== undefined &&
|
} else if (metadata.all[str].denominator !== undefined &&
|
||||||
metadata.all[str].numerator !== undefined) {
|
metadata.all[str].numerator !== undefined) {
|
||||||
if (metadata.all[str].denominator === 1) {
|
if (metadata.all[str].denominator === 1) {
|
||||||
return metadata.all[str].numerator;
|
return metadata.all[str].numerator;
|
||||||
} else if (metadata.all[str].numerator > metadata.all[str].denominator) {
|
} else if (metadata.all[str].numerator > metadata.all[str].denominator) {
|
||||||
return parseInt(
|
return Math.floor(
|
||||||
metadata.all[str].numerator * 10 / metadata.all[str].denominator,
|
metadata.all[str].numerator * 10 / metadata.all[str].denominator,
|
||||||
) / 10;
|
) / 10;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -195,7 +199,7 @@ function componentMore(render, { metadata }) {
|
||||||
if (a.toLowerCase().trim() < b.toLowerCase().trim()) return -1;
|
if (a.toLowerCase().trim() < b.toLowerCase().trim()) return -1;
|
||||||
else if (a.toLowerCase().trim() > b.toLowerCase().trim()) return +1;
|
else if (a.toLowerCase().trim() > b.toLowerCase().trim()) return +1;
|
||||||
return 0;
|
return 0;
|
||||||
}).map((key) => {
|
}).forEach((key) => {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case "undefined":
|
case "undefined":
|
||||||
case "thumbnail":
|
case "thumbnail":
|
||||||
|
|
@ -218,18 +222,25 @@ export function init() {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const extractExif = ($img) => new Promise((resolve) => EXIF.getData($img, function(data) {
|
const extractExif = ($img) => new Promise((resolve) => window.EXIF.getData($img, function() {
|
||||||
const metadata = EXIF.getAllTags(this);
|
const metadata = window.EXIF.getAllTags($img);
|
||||||
const to_date = (str) => {
|
const to_date = (str = "") => {
|
||||||
if (!str) return null;
|
const digits = str.split(/[ :]/).map((digit) => parseInt(digit));
|
||||||
return new Date(...str.split(/[ :]/));
|
return new Date(
|
||||||
|
digits[0] || 0,
|
||||||
|
digits[1] || 0,
|
||||||
|
digits[2] || 0,
|
||||||
|
digits[3] || 0,
|
||||||
|
digits[4] || 0,
|
||||||
|
digits[5] || 0,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
resolve({
|
resolve({
|
||||||
date: to_date(
|
date: to_date(
|
||||||
metadata["DateTime"] || metadata["DateTimeDigitized"] ||
|
metadata["DateTime"] || metadata["DateTimeDigitized"] ||
|
||||||
metadata["DateTimeOriginal"] || metadata["GPSDateStamp"],
|
metadata["DateTimeOriginal"] || metadata["GPSDateStamp"],
|
||||||
),
|
),
|
||||||
location: metadata["GPSLatitude"] && metadata["GPSLongitude"] && [
|
location: (metadata["GPSLatitude"] && metadata["GPSLongitude"] && [
|
||||||
[
|
[
|
||||||
metadata["GPSLatitude"][0], metadata["GPSLatitude"][1],
|
metadata["GPSLatitude"][0], metadata["GPSLatitude"][1],
|
||||||
metadata["GPSLatitude"][2], metadata["GPSLatitudeRef"],
|
metadata["GPSLatitude"][2], metadata["GPSLatitudeRef"],
|
||||||
|
|
@ -238,34 +249,34 @@ const extractExif = ($img) => new Promise((resolve) => EXIF.getData($img, functi
|
||||||
metadata["GPSLongitude"][0], metadata["GPSLongitude"][1],
|
metadata["GPSLongitude"][0], metadata["GPSLongitude"][1],
|
||||||
metadata["GPSLongitude"][2], metadata["GPSLongitudeRef"],
|
metadata["GPSLongitude"][2], metadata["GPSLongitudeRef"],
|
||||||
],
|
],
|
||||||
] || null,
|
]) || null,
|
||||||
maker: metadata["Make"] || null,
|
maker: metadata["Make"] || null,
|
||||||
model: metadata["Model"] || null,
|
model: metadata["Model"] || null,
|
||||||
focal: metadata["FocalLength"] || null,
|
focal: metadata["FocalLength"] || null,
|
||||||
aperture: metadata["FNumber"] || null,
|
aperture: metadata["FNumber"] || null,
|
||||||
shutter: metadata["ExposureTime"] || null,
|
shutter: metadata["ExposureTime"] || null,
|
||||||
iso: metadata["ISOSpeedRatings"] || null,
|
iso: metadata["ISOSpeedRatings"] || null,
|
||||||
dimension: metadata["PixelXDimension"] && metadata["PixelYDimension"] && [
|
dimension: (metadata["PixelXDimension"] && metadata["PixelYDimension"] && [
|
||||||
metadata["PixelXDimension"],
|
metadata["PixelXDimension"],
|
||||||
metadata["PixelYDimension"],
|
metadata["PixelYDimension"],
|
||||||
] || null,
|
]) || null,
|
||||||
all: Object.keys(metadata).length === 0 ? null : metadata,
|
all: Object.keys(metadata).length === 0 ? null : metadata,
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const formatTime = (t) => t && t.toLocaleTimeString(
|
const formatTime = (t) => t?.toLocaleTimeString(
|
||||||
"en-us",
|
"en-us",
|
||||||
{ weekday: "short", hour: "2-digit", minute: "2-digit" },
|
{ weekday: "short", hour: "2-digit", minute: "2-digit" },
|
||||||
);
|
);
|
||||||
|
|
||||||
const formatDate = (t) => t && t.toLocaleDateString(
|
const formatDate = (t) => t?.toLocaleDateString(
|
||||||
navigator.language,
|
navigator.language,
|
||||||
{ day: "numeric", year: "numeric", month: "short", day: "numeric" },
|
{ year: "numeric", month: "short", day: "numeric" },
|
||||||
);
|
);
|
||||||
|
|
||||||
const formatCameraSettings = (metadata) => {
|
const formatCameraSettings = (metadata) => {
|
||||||
let str = format("model", metadata);
|
const str = format("model", metadata);
|
||||||
const f = format("focal", metadata)
|
const f = format("focal", metadata);
|
||||||
if (!f) return str;
|
if (!f) return str;
|
||||||
return `${str} (${f})`;
|
return `${str} (${f})`;
|
||||||
};
|
};
|
||||||
|
|
@ -286,11 +297,11 @@ const format = (key, metadata) => {
|
||||||
case "iso":
|
case "iso":
|
||||||
return `ISO${metadata.iso}`;
|
return `ISO${metadata.iso}`;
|
||||||
case "aperture":
|
case "aperture":
|
||||||
return "ƒ"+parseInt(metadata.aperture*10)/10;
|
return `ƒ${Math.floor(metadata.aperture*10)/10}`;
|
||||||
case "shutter":
|
case "shutter":
|
||||||
if (metadata.shutter > 60) return metadata.shutter+"m";
|
if (metadata.shutter > 60) return metadata.shutter+"m";
|
||||||
else if (metadata.shutter > 1) return metadata.shutter+"s";
|
else if (metadata.shutter > 1) return metadata.shutter+"s";
|
||||||
return "1/"+parseInt(metadata.shutter.denominator / metadata.shutter.numerator)+"s";
|
return `1/${Math.floor(metadata.shutter.denominator / metadata.shutter.numerator)}s`;
|
||||||
case "dimension":
|
case "dimension":
|
||||||
if (metadata.dimension.length !== 2 || !metadata.dimension[0] || !metadata.dimension[1]) return "-";
|
if (metadata.dimension.length !== 2 || !metadata.dimension[0] || !metadata.dimension[1]) return "-";
|
||||||
return metadata.dimension[0]+"x"+metadata.dimension[1];
|
return metadata.dimension[0]+"x"+metadata.dimension[1];
|
||||||
|
|
|
||||||
|
|
@ -9,3 +9,5 @@ interface Window {
|
||||||
env?: string;
|
env?: string;
|
||||||
chrome: object;
|
chrome: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function(any): void;
|
||||||
|
|
@ -7,7 +7,7 @@ import assert from "../../lib/assert.js";
|
||||||
|
|
||||||
import "../../components/dropdown.js";
|
import "../../components/dropdown.js";
|
||||||
|
|
||||||
export default class ComponentMenubar extends window.HTMLElement {
|
export default class ComponentMenubar extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.classList.add("component_menubar");
|
this.classList.add("component_menubar");
|
||||||
|
|
@ -20,13 +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") {
|
||||||
const $container = assert.type(this.firstElementChild, window.HTMLElement);
|
const $container = assert.type(this.firstElementChild, HTMLElement);
|
||||||
$container.classList.add("inherit-width");
|
$container.classList.add("inherit-width");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async connectedCallback() {
|
async connectedCallback() {
|
||||||
const $title = assert.type(this.querySelector(".titlebar"), window.HTMLElement);
|
const $title = assert.type(this.querySelector(".titlebar"), HTMLElement);
|
||||||
this.timeoutID = setTimeout(() => animate($title, {
|
this.timeoutID = setTimeout(() => animate($title, {
|
||||||
time: 250,
|
time: 250,
|
||||||
keyframes: slideYIn(2),
|
keyframes: slideYIn(2),
|
||||||
|
|
@ -39,7 +39,7 @@ export default class ComponentMenubar extends window.HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
render(buttons) {
|
render(buttons) {
|
||||||
const $item = assert.type(this.querySelector(".action-item"), window.HTMLElement);
|
const $item = assert.type(this.querySelector(".action-item"), 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]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,129 +0,0 @@
|
||||||
const CACHE_NAME = "v0.3";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Control everything going through the wire, applying different
|
|
||||||
* strategy for caching, fetching resources
|
|
||||||
*/
|
|
||||||
self.addEventListener("fetch", function(event) {
|
|
||||||
const errResponse = (err) => (new Response(JSON.stringify({
|
|
||||||
code: "CANNOT_LOAD",
|
|
||||||
message: err.message
|
|
||||||
}), { status: 502 }));
|
|
||||||
|
|
||||||
if (is_a_ressource(event.request)) {
|
|
||||||
return event.respondWith(cacheFirstStrategy(event).catch(errResponse));
|
|
||||||
} else if (is_an_api_call(event.request)) {
|
|
||||||
return event;
|
|
||||||
} else if (is_an_index(event.request)) {
|
|
||||||
return event.respondWith(cacheFirstStrategy(event).catch(errResponse));
|
|
||||||
} else {
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When a new service worker is coming in, we need to do a bit of
|
|
||||||
* cleanup to get rid of the rotten cache
|
|
||||||
*/
|
|
||||||
self.addEventListener("activate", function(event) {
|
|
||||||
vacuum(event);
|
|
||||||
});
|
|
||||||
|
|
||||||
self.addEventListener("error", function(err) {
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When a newly installed service worker is coming in, we want to use it
|
|
||||||
* straight away (make it active). By default it would be in a "waiting state"
|
|
||||||
*/
|
|
||||||
self.addEventListener("install", function() {
|
|
||||||
caches.open(CACHE_NAME).then(function(cache) {
|
|
||||||
return cache.addAll([
|
|
||||||
"/",
|
|
||||||
"/api/config"
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (self.skipWaiting) {
|
|
||||||
self.skipWaiting();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function is_a_ressource(request) {
|
|
||||||
const p = _pathname(request);
|
|
||||||
if (["assets", "manifest.json", "favicon.ico"].indexOf(p[0]) !== -1) {
|
|
||||||
return true;
|
|
||||||
} else if (p[0] === "api" && (p[1] === "config")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_an_api_call(request) {
|
|
||||||
return _pathname(request)[0] === "api";
|
|
||||||
}
|
|
||||||
function is_an_index(request) {
|
|
||||||
return ["files", "view", "login", "logout", ""]
|
|
||||||
.indexOf(_pathname(request)[0]) >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// //////////////////////////////////////
|
|
||||||
// HELPERS
|
|
||||||
// //////////////////////////////////////
|
|
||||||
|
|
||||||
function vacuum(event) {
|
|
||||||
return event.waitUntil(
|
|
||||||
caches.keys().then(function(cachesName) {
|
|
||||||
return Promise.all(cachesName.map(function(cacheName) {
|
|
||||||
if (cacheName !== CACHE_NAME) {
|
|
||||||
return caches.delete(cacheName);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}));
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _pathname(request) {
|
|
||||||
return request.url.replace(/^http[s]?:\/\/[^\/]*\//, "").split("/");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* strategy is cache first:
|
|
||||||
* 1. use whatever is in the cache
|
|
||||||
* 2. perform the network call to update the cache
|
|
||||||
*/
|
|
||||||
function cacheFirstStrategy(event) {
|
|
||||||
return caches.open(CACHE_NAME).then(function(cache) {
|
|
||||||
return cache.match(event.request).then(function(response) {
|
|
||||||
if (!response) {
|
|
||||||
return fetchAndCache(event);
|
|
||||||
}
|
|
||||||
fetchAndCache(event).catch(nil);
|
|
||||||
return response;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function fetchAndCache(event) {
|
|
||||||
// A request is a stream and can only be consumed once. Since we are consuming this
|
|
||||||
// once by cache and once by the browser for fetch, we need to clone the response as
|
|
||||||
// seen on:
|
|
||||||
// https://developers.google.com/web/fundamentals/getting-started/primers/service-workers
|
|
||||||
return fetch(event.request)
|
|
||||||
.then(function(response) {
|
|
||||||
if (!response || response.status !== 200) {
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A response is a stream and can only because we want the browser to consume the
|
|
||||||
// response as well as the cache consuming the response, we need to clone it
|
|
||||||
const responseClone = response.clone();
|
|
||||||
caches.open(CACHE_NAME).then(function(cache) {
|
|
||||||
cache.put(event.request, responseClone);
|
|
||||||
});
|
|
||||||
return response;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function nil() {}
|
|
||||||
}
|
|
||||||
1
public/global.d.ts
vendored
1
public/global.d.ts
vendored
|
|
@ -1,4 +1,5 @@
|
||||||
interface Window {
|
interface Window {
|
||||||
|
chrome: object;
|
||||||
overrides: {
|
overrides: {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
"xdg-open"?: (mime: string) => void;
|
"xdg-open"?: (mime: string) => void;
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,10 @@
|
||||||
<link rel="stylesheet" href="custom.css">
|
<link rel="stylesheet" href="custom.css">
|
||||||
{{ if eq .license "agpl" }}
|
{{ if eq .license "agpl" }}
|
||||||
<script>
|
<script>
|
||||||
class NyanCatLoader extends window.HTMLElement {
|
class NyanCatLoader extends HTMLElement {
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.innerHTML = this.render();
|
this.innerHTML = this.render();
|
||||||
this.timeout = window.setTimeout(function(){
|
this.timeout = setTimeout(function(){
|
||||||
const $rbw = document.querySelector("#rbw .w");
|
const $rbw = document.querySelector("#rbw .w");
|
||||||
$rbw.innerHTML = $rbw.innerHTML.repeat(10);
|
$rbw.innerHTML = $rbw.innerHTML.repeat(10);
|
||||||
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
window.clearTimeout(this.timeout);
|
clearTimeout(this.timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,21 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
|
"allowUnreachableCode": false,
|
||||||
|
"allowUnusedLabels": false,
|
||||||
|
"alwaysStrict": true,
|
||||||
"checkJs": true,
|
"checkJs": true,
|
||||||
|
"exactOptionalPropertyTypes": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
|
"noErrorTruncation": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noImplicitThis": true,
|
"noImplicitThis": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noUncheckedIndexedAccess": true,
|
"noUncheckedIndexedAccess": true,
|
||||||
|
|
@ -13,9 +23,7 @@
|
||||||
"strictPropertyInitialization": true,
|
"strictPropertyInitialization": true,
|
||||||
"strictBindCallApply": true,
|
"strictBindCallApply": true,
|
||||||
"strictFunctionTypes": true,
|
"strictFunctionTypes": true,
|
||||||
"strict": false,
|
"strict": true,
|
||||||
"allowUnreachableCode": false,
|
|
||||||
"allowUnusedLabels": false,
|
|
||||||
"lib": [
|
"lib": [
|
||||||
"dom",
|
"dom",
|
||||||
"dom.iterable",
|
"dom.iterable",
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,7 @@ export default defineConfig(({ comand, mode }) => {
|
||||||
test: {
|
test: {
|
||||||
global: true,
|
global: true,
|
||||||
environment: "jsdom",
|
environment: "jsdom",
|
||||||
setupFiles: ["./test/setup.js"],
|
setupFiles: ["./vite.setup.js"],
|
||||||
},
|
|
||||||
coverage: {
|
|
||||||
reporter: ["html"],
|
|
||||||
exclude: [
|
|
||||||
"./assets/lib/vendor/**"
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
17
public/vite.setup.js
Normal file
17
public/vite.setup.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import {
|
||||||
|
describe, it, test, expect, vi,
|
||||||
|
afterEach, afterAll, beforeEach, beforeAll,
|
||||||
|
} from "vitest";
|
||||||
|
|
||||||
|
global.nextTick = () => new Promise((done) => setTimeout(done, 0));
|
||||||
|
global.requestAnimationFrame = (callback) => setTimeout(callback, 0);
|
||||||
|
|
||||||
|
global.describe = describe;
|
||||||
|
global.it = it;
|
||||||
|
global.test = test;
|
||||||
|
global.expect = expect;
|
||||||
|
global.vi = vi;
|
||||||
|
global.beforeEach = beforeEach;
|
||||||
|
global.beforeAll = beforeAll;
|
||||||
|
global.afterEach = afterEach;
|
||||||
|
global.afterAll = afterAll;
|
||||||
Loading…
Reference in a new issue