feature (viewerpage): rewrite of the viewers

This commit is contained in:
MickaelK
2023-12-21 02:06:44 +11:00
parent 55671b3dfa
commit 21bf973ba9
17 changed files with 460 additions and 70 deletions
+64
View File
@@ -0,0 +1,64 @@
.component_dropdown {
position: relative;
}
.component_dropdown .dropdown_container {
display: none;
position: absolute;
right: 0;
}
.component_dropdown .dropdown_button {
border: 1px solid rgba(0, 0, 0, 0);
border-radius: 4px;
padding: 5px;
min-width: 20px;
text-align: center;
}
.component_dropdown .dropdown_container {
padding-top: 5px;
z-index: 3;
}
.component_dropdown .dropdown_container:before {
content: ' ';
position: absolute;
right: 10px;
top: 1px;
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid white;
}
.component_dropdown .dropdown_container ul {
margin: 0;
list-style-type: none;
background: white;
border: 1px solid var(--border);
box-shadow: 1px 1px 2px var(--border);
color: rgba(0,10,20,0.85);
border-radius: 3px;
padding: 3px 0px;
font-size: 0.92em;
}
.component_dropdown .dropdown_container ul li {
display: flex;
}
.component_dropdown .dropdown_container ul li > div {
width: 160px;
padding: 8px 5px 8px 10px;
}
.component_dropdown.active .dropdown_container {
display: block;
}
.component_dropdown.active .dropdown_container li {
background: white;
transition: background 0.1s ease-out;
}
.component_dropdown.active .dropdown_container li:hover {
background: rgba(0, 0, 0, 0.05);
}
.component_dropdown.active .dropdown_button {
border-color: var(--bg-color);
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
}
+88
View File
@@ -0,0 +1,88 @@
import { createFragment } from "../lib/skeleton/index.js";
import { animate, slideYIn } from "../lib/animate.js";
import { loadCSS } from "../helpers/loader.js";
await loadCSS(import.meta.url, "./dropdown.css");
export default class ComponentDropdown extends HTMLDivElement {
constructor() {
super();
this.render();
}
static get observedAttributes() {
return ["options"];
}
render() {
this.classList.add("component_dropdown", "view", "sort")
this.appendChild(createFragment(`
<div class="dropdown_button">
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzODQgNTEyIj4KICA8cGF0aCBmaWxsPSIjZjJmMmYyIiBkPSJNIDM2MCw0NjAgSCAyNCBDIDEwLjcsNDYwIDAsNDUzLjMgMCw0NDAgdiAtMTIgYyAwLC0xMy4zIDEwLjcsLTIwIDI0LC0yMCBoIDMzNiBjIDEzLjMsMCAyNCw2LjcgMjQsMjAgdiAxMiBjIDAsMTMuMyAtMTAuNywyMCAtMjQsMjAgeiIgLz4KICA8cGF0aCBmaWxsPSIjZjJmMmYyIiBkPSJNIDIyNi41NTM5LDIzNC44ODQyOCBWIDUyLjk0MzI4MyBjIDAsLTYuNjI3IC01LjM3MywtMTIgLTEyLC0xMiBoIC00NCBjIC02LjYyNywwIC0xMiw1LjM3MyAtMTIsMTIgViAyMzQuODg0MjggaCAtNTIuMDU5IGMgLTIxLjM4MiwwIC0zMi4wOSwyNS44NTEgLTE2Ljk3MSw0MC45NzEgbCA4Ni4wNTksODYuMDU5IGMgOS4zNzMsOS4zNzMgMjQuNTY5LDkuMzczIDMzLjk0MSwwIGwgODYuMDU5LC04Ni4wNTkgYyAxNS4xMTksLTE1LjExOSA0LjQxMSwtNDAuOTcxIC0xNi45NzEsLTQwLjk3MSB6IiAvPgo8L3N2Zz4K" alt="download_white">
</div>`));
this.appendChild(createFragment(`
<div class="dropdown_container">
<ul>
<li>
<div>
<a download="README.org" href="/api/files/cat?path=%2FREADME.org">Save current file</a>
</div>
</li>
<li>
<div>
<a target="_blank" href="/api/export/private/text/html/README.org">Export as HTML</a>
</div>
</li>
<li>
<div>
<a target="_blank" href="/api/export/private/application/pdf/README.org">Export as PDF</a>
</div>
</li>
<li>
<div>
<a target="_blank" href="/api/export/private/text/markdown/README.org">Export as Markdown</a>
</div>
</li>
<li>
<div>
<a target="_blank" href="/api/export/private/text/plain/README.org">Export as TXT</a>
</div>
</li>
<li>
<div>
<a target="_blank" download="README.tex" href="/api/export/private/text/x-latex/README.org">Export as Latex</a>
</div>
</li>
<li>
<div>
<a target="_blank" download="README.ics" href="/api/export/private/text/calendar/README.org">Export as ical</a>
</div>
</li>
<li>
<div>
<a target="_blank" download="README.odt" href="/api/export/private/application/vnd.oasis.opendocument.text/README.org">Export as Open office</a>
</div>
</li>
<li>
<div>
<a target="_blank" download="README.pdf" href="/api/export/private/application/pdf/README.org?mode=beamer">Export as Beamer</a>
</div>
</li>
</ul>
</div>
</div>
`));
const setActive = () => this.classList.toggle("active");
this.querySelector(".dropdown_button").onclick = () => {
setActive();
animate(this.querySelector(".dropdown_container"), {
time: 100,
keyframes: slideYIn(2),
});
};
}
}
customElements.define("component-dropdown", ComponentDropdown, { extends: "div" });
+1
View File
@@ -3,6 +3,7 @@
bottom: 20px;
right: 20px;
z-index: 2;
background: transparent;
}
.component_fab .content {
height: 25px;
+8
View File
@@ -75,3 +75,11 @@ google-cast-launcher {
transition: all 0.25s ease-out;
transition-delay: 0.30s;
}
/* icons in additional menu */
.component_menubar .action-item [is="component-dropdown"] .dropdown_button {
border: none;
padding: 2px 0px;
margin: 0;
}
+14 -19
View File
@@ -1,7 +1,12 @@
import { createElement } from "../lib/skeleton/index.js";
import { basename } from "../lib/path.js";
import assert from "../lib/assert.js";
import { loadCSS } from "../helpers/loader.js";
await loadCSS(import.meta.url, "./menubar.css");
import "./dropdown.js";
export default class ComponentMenubar extends window.HTMLElement {
constructor() {
super();
@@ -13,33 +18,23 @@ export default class ComponentMenubar extends window.HTMLElement {
<div class="titlebar">
${basename(decodeURIComponent(location.pathname + location.hash)) || "&nbsp;"}
</div>
<div class="action-item no-select"></div>
<div class="action-item no-select">
<div is="component-dropdown"></div>
</div>
</span>
</div>
`;
this.render();
}
async render(html) {
await loadCSS(import.meta.url, "./menubar.css");
html = `<span class="specific">
<span id="chromecast-target"></span>
</span>
<span class="download-button">
<span>
<a href="/api/files/cat?path=%2FDocuments%2Fgetting_started.pdf" download="getting_started.pdf">
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzODQgNTEyIj4KICA8cGF0aCBmaWxsPSIjZjJmMmYyIiBkPSJNIDM2MCw0NjAgSCAyNCBDIDEwLjcsNDYwIDAsNDUzLjMgMCw0NDAgdiAtMTIgYyAwLC0xMy4zIDEwLjcsLTIwIDI0LC0yMCBoIDMzNiBjIDEzLjMsMCAyNCw2LjcgMjQsMjAgdiAxMiBjIDAsMTMuMyAtMTAuNywyMCAtMjQsMjAgeiIgLz4KICA8cGF0aCBmaWxsPSIjZjJmMmYyIiBkPSJNIDIyNi41NTM5LDIzNC44ODQyOCBWIDUyLjk0MzI4MyBjIDAsLTYuNjI3IC01LjM3MywtMTIgLTEyLC0xMiBoIC00NCBjIC02LjYyNywwIC0xMiw1LjM3MyAtMTIsMTIgViAyMzQuODg0MjggaCAtNTIuMDU5IGMgLTIxLjM4MiwwIC0zMi4wOSwyNS44NTEgLTE2Ljk3MSw0MC45NzEgbCA4Ni4wNTksODYuMDU5IGMgOS4zNzMsOS4zNzMgMjQuNTY5LDkuMzczIDMzLjk0MSwwIGwgODYuMDU5LC04Ni4wNTkgYyAxNS4xMTksLTE1LjExOSA0LjQxMSwtNDAuOTcxIC0xNi45NzEsLTQwLjk3MSB6IiAvPgo8L3N2Zz4K" alt="download_white">
</a>
</span>
</span>`;
this.querySelector(".action-item").appendChild(createElement(html));
render($fragment) {
this.querySelector(".action-item").appendChild($fragment);
}
}
export function render(html = "") {
const $el = document.body.querySelector("component-menubar");
if (!$el) throw new Error("component::menubar.js missing element");
$el.render(html);
export async function render($fragment, $root = document.body) {
const $el = $root.querySelector("component-menubar");
assert.truthy($el, "component::menubar.js missing element");
$el.render($fragment);
}
customElements.define("component-menubar", ComponentMenubar);
+6 -6
View File
@@ -1,15 +1,15 @@
export default class assert {
static type(object, type) {
static type(object, type, msg) {
if (object instanceof type) return;
throw new Error(`assertion failed - unexpected type for ${object.toString()}`);
throw new Error(msg ? msg : `assertion failed - unexpected type for ${object.toString()}`);
}
static truthy(object) {
static truthy(object, msg) {
if (object) return;
throw new Error(`assertion failed - object is not truthy`);
throw new Error(msg ? msg : `assertion failed - object is not truthy`);
}
static fail(object) {
throw new Error(`assertion failed - ${object}`);
static fail(object, msg) {
throw new Error(msg ? msg : `assertion failed - ${object}`);
}
}
+1 -3
View File
@@ -67,9 +67,7 @@ export function createFragment(str) {
const $n = window.document.createElement("div");
$n.innerHTML = str;
const $doc = document.createDocumentFragment();
for (let i=0; i<$n.children.length; i++) {
$doc.appendChild($n.children[i].cloneNode(true));
}
for (let i=0; i<$n.children.length; i++) $doc.appendChild($n.children[i].cloneNode(true));
$n.remove();
return $doc;
}
File diff suppressed because one or more lines are too long
@@ -1,3 +1,4 @@
/* General design */
[is="component_submenu"] {
margin-top: 20px;
position: sticky;
@@ -48,7 +49,7 @@
border-radius: 5px;
}
/* dark mode */
.dark-mode [is="component_submenu"] .component_submenu .action button img {
filter: brightness(70%) invert(1);
}
@@ -61,6 +62,11 @@
}
/* search */
/* img[alt="search"]:hover ~ input { */
/* background: red; */
/* display: block; */
/* } */
/* .component_submenu .menubar { */
/* font-size: 15px; */
@@ -5,6 +5,8 @@ import { loadCSS } from "../../helpers/loader.js";
import { qs } from "../../lib/dom.js";
import { getSelection$, clearSelection } from "./model_files.js";
import "../../components/dropdown.js";
export default async function(render) {
const $page = createElement(`<div class="component_submenu container"></div>`);
render($page);
@@ -38,7 +40,7 @@ export default async function(render) {
color: var(--color);
font-size: 0.8rem;
">
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj4KICA8cGF0aCBzdHlsZT0iZmlsbDojNjI2NDY5O2ZpbGwtb3BhY2l0eToxIiBkPSJNNTA1IDQ0Mi43TDQwNS4zIDM0M2MtNC41LTQuNS0xMC42LTctMTctN0gzNzJjMjcuNi0zNS4zIDQ0LTc5LjcgNDQtMTI4QzQxNiA5My4xIDMyMi45IDAgMjA4IDBTMCA5My4xIDAgMjA4czkzLjEgMjA4IDIwOCAyMDhjNDguMyAwIDkyLjctMTYuNCAxMjgtNDR2MTYuM2MwIDYuNCAyLjUgMTIuNSA3IDE3bDk5LjcgOTkuN2M5LjQgOS40IDI0LjYgOS40IDMzLjkgMGwyOC4zLTI4LjNjOS40LTkuNCA5LjQtMjQuNi4xLTM0ek0yMDggMzM2Yy03MC43IDAtMTI4LTU3LjItMTI4LTEyOCAwLTcwLjcgNTcuMi0xMjggMTI4LTEyOCA3MC43IDAgMTI4IDU3LjIgMTI4IDEyOCAwIDcwLjctNTcuMiAxMjgtMTI4IDEyOHoiIC8+Cjwvc3ZnPgo=" alt="search_dark" />
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj4KICA8cGF0aCBzdHlsZT0iZmlsbDojNjI2NDY5O2ZpbGwtb3BhY2l0eToxIiBkPSJNNTA1IDQ0Mi43TDQwNS4zIDM0M2MtNC41LTQuNS0xMC42LTctMTctN0gzNzJjMjcuNi0zNS4zIDQ0LTc5LjcgNDQtMTI4QzQxNiA5My4xIDMyMi45IDAgMjA4IDBTMCA5My4xIDAgMjA4czkzLjEgMjA4IDIwOCAyMDhjNDguMyAwIDkyLjctMTYuNCAxMjgtNDR2MTYuM2MwIDYuNCAyLjUgMTIuNSA3IDE3bDk5LjcgOTkuN2M5LjQgOS40IDI0LjYgOS40IDMzLjkgMGwyOC4zLTI4LjNjOS40LTkuNCA5LjQtMjQuNi4xLTM0ek0yMDggMzM2Yy03MC43IDAtMTI4LTU3LjItMTI4LTEyOCAwLTcwLjcgNTcuMi0xMjggMTI4LTEyOCA3MC43IDAgMTI4IDU3LjIgMTI4IDEyOCAwIDcwLjctNTcuMiAxMjgtMTI4IDEyOHoiIC8+Cjwvc3ZnPgo=" alt="search" />
</button>
<button>
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj4KICA8cGF0aCBzdHlsZT0iZmlsbDojNjI2NDY5O2ZpbGwtb3BhY2l0eToxIiBkPSJtIDEzMy4zMzMsNTYgdiA2NCBjIDAsMTMuMjU1IC0xMC43NDUsMjQgLTI0LDI0IEggMjQgQyAxMC43NDUsMTQ0IDAsMTMzLjI1NSAwLDEyMCBWIDU2IEMgMCw0Mi43NDUgMTAuNzQ1LDMyIDI0LDMyIGggODUuMzMzIGMgMTMuMjU1LDAgMjQsMTAuNzQ1IDI0LDI0IHogbSAzNzkuMzM0LDIzMiB2IC02NCBjIDAsLTEzLjI1NSAtMTAuNzQ1LC0yNCAtMjQsLTI0IEggMjEzLjMzMyBjIC0xMy4yNTUsMCAtMjQsMTAuNzQ1IC0yNCwyNCB2IDY0IGMgMCwxMy4yNTUgMTAuNzQ1LDI0IDI0LDI0IGggMjc1LjMzMyBjIDEzLjI1NiwwIDI0LjAwMSwtMTAuNzQ1IDI0LjAwMSwtMjQgeiBtIDAsLTE2OCBWIDU2IGMgMCwtMTMuMjU1IC0xMC43NDUsLTI0IC0yNCwtMjQgSCAyMTMuMzMzIGMgLTEzLjI1NSwwIC0yNCwxMC43NDUgLTI0LDI0IHYgNjQgYyAwLDEzLjI1NSAxMC43NDUsMjQgMjQsMjQgaCAyNzUuMzMzIGMgMTMuMjU2LDAgMjQuMDAxLC0xMC43NDUgMjQuMDAxLC0yNCB6IE0gMTA5LjMzMywyMDAgSCAyNCBDIDEwLjc0NSwyMDAgMCwyMTAuNzQ1IDAsMjI0IHYgNjQgYyAwLDEzLjI1NSAxMC43NDUsMjQgMjQsMjQgaCA4NS4zMzMgYyAxMy4yNTUsMCAyNCwtMTAuNzQ1IDI0LC0yNCB2IC02NCBjIDAsLTEzLjI1NSAtMTAuNzQ1LC0yNCAtMjQsLTI0IHogTSAwLDM5MiB2IDY0IGMgMCwxMy4yNTUgMTAuNzQ1LDI0IDI0LDI0IGggODUuMzMzIGMgMTMuMjU1LDAgMjQsLTEwLjc0NSAyNCwtMjQgdiAtNjQgYyAwLC0xMy4yNTUgLTEwLjc0NSwtMjQgLTI0LC0yNCBIIDI0IEMgMTAuNzQ1LDM2OCAwLDM3OC43NDUgMCwzOTIgWiBtIDE4OS4zMzMsMCB2IDY0IGMgMCwxMy4yNTUgMTAuNzQ1LDI0IDI0LDI0IGggMjc1LjMzMyBjIDEzLjI1NSwwIDI0LC0xMC43NDUgMjQsLTI0IHYgLTY0IGMgMCwtMTMuMjU1IC0xMC43NDUsLTI0IC0yNCwtMjQgSCAyMTMuMzMzIGMgLTEzLjI1NSwwIC0yNCwxMC43NDUgLTI0LDI0IHoiIC8+Cjwvc3ZnPgo=" alt="list" />
@@ -46,6 +48,7 @@ export default async function(render) {
<button>
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMjAgNTEyIj4KICA8cGF0aCBzdHlsZT0iZmlsbDojNjI2NDY5O2ZpbGwtb3BhY2l0eToxIiBkPSJNNDEgMjg4aDIzOGMyMS40IDAgMzIuMSAyNS45IDE3IDQxTDE3NyA0NDhjLTkuNCA5LjQtMjQuNiA5LjQtMzMuOSAwTDI0IDMyOWMtMTUuMS0xNS4xLTQuNC00MSAxNy00MXptMjU1LTEwNUwxNzcgNjRjLTkuNC05LjQtMjQuNi05LjQtMzMuOSAwTDI0IDE4M2MtMTUuMSAxNS4xLTQuNCA0MSAxNyA0MWgyMzhjMjEuNCAwIDMyLjEtMjUuOSAxNy00MXoiIC8+Cjwvc3ZnPgo=" alt="sort" />
</button>
<!--<div is="component-dropdown"></div>-->
</div>
`)),
applyMutation($page, "replaceChildren"),
@@ -28,7 +28,7 @@ body:not(.dark-mode) .component_audioplayer > .audioplayer_container {
box-shadow: rgba(0, 0, 0, 0.14) 0px 4px 5px 0px, rgba(0, 0, 0, 0.12) 0px 1px 10px 0px, rgba(0, 0, 0, 0.2) 0px 2px 4px -1px;
position: relative;
border-radius: 3px;
padding: 10px 0 30px 0;
padding: 10px 0 15px 0;
}
.component_audioplayer > .audioplayer_container .audioplayer_box .audioplayer_control {
padding-top: 20px;
@@ -1,37 +1,199 @@
import { createElement } from "../../lib/skeleton/index.js";
import rxjs, { effect, onClick } from "../../lib/rx.js";
import { qs } from "../../lib/dom.js";
import { onDestroy } from "../../lib/skeleton/lifecycle.js";
import rxjs, { effect } from "../../lib/rx.js";
import WaveSurfer from "../../lib/vendor/wavesurfer.js";
import { loadCSS } from "../../helpers/loader.js";
import { loadCSS, loadJS } from "../../helpers/loader.js";
import { settings_get, settings_put } from "../../lib/settings.js";
import { getDownloadUrl } from "./common.js";
import ctrlError from "../ctrl_error.js";
import { ICON } from "./common_icon.js";
import { transition, getDownloadUrl } from "./common.js";
import "../../components/menubar.js";
const STATUS_PLAYING = "PLAYING";
const STATUS_PAUSED = "PAUSED";
const STATUS_BUFFERING = "BUFFERING";
export default function(render) {
const $page = createElement(`
<div class="component_formviewer">
<div class="component_audioplayer">
<component-menubar></component-menubar>
<div id="waveform"></div>
<div class="audioplayer_container">
<div class="audioplayer_box">
<div data-bind="loader" class="hidde">
<div class="audioplayer_loader" style="width: 31%;"></div>
<span class="percent">31%</span>
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0nMTIwcHgnIGhlaWdodD0nMTIwcHgnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIiBjbGFzcz0idWlsLXJpbmctYWx0Ij4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0ibm9uZSIgY2xhc3M9ImJrIj48L3JlY3Q+CiAgPGNpcmNsZSBjeD0iNTAiIGN5PSI1MCIgcj0iNDAiIHN0cm9rZT0ibm9uZSIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj48L2NpcmNsZT4KICA8Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgc3Ryb2tlPSIjNmY2ZjZmIiBmaWxsPSJub25lIiBzdHJva2Utd2lkdGg9IjYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCI+CiAgICA8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJzdHJva2UtZGFzaG9mZnNldCIgZHVyPSIycyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGZyb209IjAiIHRvPSI1MDIiPjwvYW5pbWF0ZT4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9InN0cm9rZS1kYXNoYXJyYXkiIGR1cj0iMnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiB2YWx1ZXM9IjE1MC42IDEwMC40OzEgMjUwOzE1MC42IDEwMC40Ij48L2FuaW1hdGU+CiAgPC9jaXJjbGU+Cjwvc3ZnPgo=" alt="loading">
</div>
<div id="waveform"></div>
<div class="audioplayer_control" style="opacity: 1;">
<div class="buttons no-select">
<span>
<img class="component_icon" draggable="false" src="${ICON.PLAY}" alt="play">
<img class="component_icon hidden" draggable="false" src="${ICON.PAUSE}" alt="pause">
</span>
<span>
<img class="component_icon" draggable="false" src="${ICON.VOLUME_NORMAL}" alt="volume">
</span>
<input type="range" min="0" max="100" value="52">
</div>
<div class="timecode">
<span id="currentTime">00:00</span>
<span id="separator" class="no-select">/</span>
<span id="totalDuration">02:13</span>
</div>
</div>
</div>
</div>
</div>
`);
render($page);
transition(qs($page, ".audioplayer_box"));
const wavesurfer = WaveSurfer.create({
container: "#waveform",
interact: false,
const $control = {
play: qs($page, `.audioplayer_control [alt="play"]`),
pause: qs($page, `.audioplayer_control [alt="pause"]`),
// loading: qs($page, `.audioplayer_control component-icon[name="loading"]`),
};
const $volume = {
range: qs($page, `input[type="range"]`),
// icon_mute: qs($page, `img[alt="volume_mute"]`),
// icon_low: qs($page, `img[alt="volume_low"]`),
// icon_normal: qs($page, `img[alt="volume"]`),
};
const setVolume = (volume, wavesurfer) => {
wavesurfer.setVolume(volume / 100);
};
const currentTime$ = new rxjs.BehaviorSubject([
0, // time of lastSeek: we want to start from the start
0, // seek offset to align with wavesurfer currentTime in the audiocontext
]);
waveColor: "#323639",
progressColor: "#808080",
cursorColor: "#6f6f6f",
cursorWidth: 3,
height: 200,
barWidth: 1,
});
wavesurfer.load(getDownloadUrl());
onDestroy(() => wavesurfer.destroy());
// feature1: setup the dom
const setup$ = rxjs.of(qs($page, "#waveform")).pipe(
rxjs.mergeMap(($node) => Promise.resolve(WaveSurfer.create({
container: $node,
interact: false,
waveColor: "#323639",
progressColor: "#808080",
cursorColor: "#6f6f6f",
cursorWidth: 3,
height: 200,
barWidth: 1,
}))),
rxjs.tap((wavesurfer) => {
window.audio = wavesurfer; // TODO: remove
wavesurfer.load(getDownloadUrl());
wavesurfer.on("error", (err) => {
throw new Error(err)
});
}),
rxjs.tap((wavesurfer) => onDestroy(() => wavesurfer.destroy())),
rxjs.catchError(ctrlError()),
rxjs.shareReplay(1),
);
effect(setup$);
// feature2: loading animation
effect(setup$.pipe(
rxjs.mergeMap((wavesurfer) => new rxjs.Observable((observer) => {
wavesurfer.on("loading", (n) => observer.next(n));
wavesurfer.on("ready", () => observer.next(100));
})),
rxjs.mergeMap((n) => {
const $loader = qs($page, `[data-bind="loader"]`);
$loader.querySelector(".audioplayer_loader").style.width = `${n}%`;
$loader.querySelector(".percent").textContent = `${n}%`;
if (n !== 100) return rxjs.EMPTY;
return rxjs.of(null).pipe(
rxjs.delay(200),
rxjs.tap(() => $loader.classList.add("hidden")),
);
}),
));
// feature3: prepare the data source
const ready$ = setup$.pipe(
rxjs.mergeMap((wavesurfer) => new rxjs.Observable((observer) => {
wavesurfer.on("ready", () => observer.next(wavesurfer));
})),
rxjs.share(),
)
effect(ready$.pipe(rxjs.tap((wavesurfer) => {
wavesurfer.backend.createSource();
wavesurfer.backend.startPosition = 0;
wavesurfer.backend.lastPlay = 0;
wavesurfer.backend.source.start(0, 0);
})));
// feature: player control - volume
effect(setup$.pipe(
rxjs.switchMap(() => rxjs.fromEvent($volume.range, "input").pipe(rxjs.map((e) => e.target.value))),
rxjs.startWith(settings_get("volume") === null ? 80 : settings_get("volume")),
rxjs.mergeMap((volume) => setup$.pipe(rxjs.tap((wavesurfer) => setVolume(volume, wavesurfer)))),
));
// feature: player control - seek
effect(setup$.pipe(
rxjs.mergeMap((wavesurfer) => rxjs.fromEvent(qs($page, "#waveform"), "click").pipe(
rxjs.map((e) => ({ e, wavesurfer })),
)),
rxjs.map(({ e, wavesurfer }) => {
const rec = e.target.getBoundingClientRect();
return { wavesurfer, progress: (e.clientX - rec.x) / rec.width };
}),
rxjs.tap(({ progress, wavesurfer }) => {
wavesurfer.drawer.progress(progress);
const newTime = wavesurfer.getDuration() * progress;
currentTime$.next([newTime, wavesurfer.backend.ac.currentTime]);
wavesurfer.backend.source.stop(0);
wavesurfer.backend.disconnectSource();
wavesurfer.backend.createSource();
wavesurfer.backend.startPosition = newTime;
wavesurfer.backend.source.start(0, newTime);
}),
));
// feature4: time progression
effect(ready$.pipe(
rxjs.mergeMap(() => rxjs.merge(
onClick($control.play).pipe(rxjs.mapTo(STATUS_PLAYING)),
onClick($control.pause).pipe(rxjs.mapTo(STATUS_PAUSED)),
)),
rxjs.mergeMap((status) => setup$.pipe(rxjs.tap((wavesurfer) => {
switch (status) {
case STATUS_PLAYING:
wavesurfer.backend.ac.resume();
$control.play.classList.add("hidden");
$control.pause.classList.remove("hidden");
break;
case STATUS_PAUSED:
wavesurfer.backend.ac.suspend();
$control.play.classList.remove("hidden");
$control.pause.classList.add("hidden");
break;
default:
throw new Error("Unknown state");
}
}))),
rxjs.switchMap((wavesurfer) => rxjs.animationFrames().pipe(
rxjs.tap(() => {
const percent = (currentTime$.value[0] + (wavesurfer.backend.ac.currentTime - currentTime$.value[1])) / wavesurfer.getDuration();
if (percent > 1) return;
wavesurfer.drawer.progress(percent);
}),
)),
));
}
export function init() {
return loadCSS(import.meta.url, "./application_audio.css");
return Promise.all([
loadJS(import.meta.url, "../../lib/vendor/wavesurfer.js"),
loadCSS(import.meta.url, "./application_audio.css"),
]);
}
@@ -174,11 +174,6 @@ body:not(.dark-mode) .component_imageviewer .component_image_container, body:not
.component_imageviewer .component_image_container img[srcset].idle {
transition: 0.2s ease transform;
}
.component_imageviewer .component_image_container img.error {
filter: contrast(0.8);
width: 160px;
margin: 0 auto;
}
.component_imageviewer .loader, .component_imageviewer .loader > span {
height: 100%;
@@ -200,3 +195,10 @@ body:not(.dark-mode) .component_imageviewer .component_image_container, body:not
font-size: 1.3em;
margin-top: 10px;
}
.component_imageviewer .component_image_container img.error {
filter: contrast(0.8);
width: 160px;
margin: 0 auto;
}
@@ -61,13 +61,12 @@ export default function(render) {
rxjs.catchError(ctrlError()),
);
}
console.log(err)
return ctrlError()(err);
}),
));
// componentMetadata(createRender(qs($page, ".images_aside")));
// componentPager(createRender(qs($page, ".component_pager")));
componentMetadata(createRender(qs($page, ".images_aside")));
componentPager(createRender(qs($page, ".component_pager")));
}
export function init() {
@@ -1,5 +1,6 @@
import { createElement } from "../../lib/skeleton/index.js";
import rxjs, { effect } from "../../lib/rx.js";
import { animate, slideYIn } from "../../lib/animate.js";
import { loadCSS, loadJS } from "../../helpers/loader.js";
import { qs } from "../../lib/dom.js";
import { settings_get, settings_put } from "../../lib/settings.js";
@@ -10,8 +11,10 @@ import Hls from "../../lib/vendor/hlsjs/hls.js";
import ctrlError from "../ctrl_error.js";
import { transition, getDownloadUrl } from "./common.js";
import { ICON } from "./common_icon.js";
import { menubarDownload, buildMenubar } from "./common_menubar.js";
import { render as renderMenubar } from "../../components/menubar.js";
import "../../components/menubar.js";
import "../../components/icon.js";
const STATUS_PLAYING = "PLAYING";
@@ -45,13 +48,14 @@ export default function(render, { mime }) {
</div>
<div class="progress-placeholder"></div>
</div>
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzZmNmY2ZiIgc3Ryb2tlLXdpZHRoPSIyLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTSA4LjkyODE3MjQsMi41OTk4MDI5IEMgOC4yMjE2MTQ5LDIuMTUzMjg3MyA3LjA3MjExNDMsMi4zOTIwOTE4IDcuMDcxODI3NywzLjQwMDE5NzEgbCAtMC4wMDQ4OSwxNy4yMDUwNDU5IGMgLTIuODg5ZS00LDEuMDE1NzE1IDEuMjEyMTk3OSwxLjE2MDM3MiAxLjg2NjEzMDcsMC43ODk1MTMgQyAyMy45NzU3NCw4LjcyODk4NTYgMjMuOTMwMTUyLDE0LjEwNDQ2MyA4LjkyODE1ODQsMi41OTk4MDI5IFoiIC8+Cjwvc3ZnPgo=" alt="play">
<img class="component_icon hidden" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgZmlsbD0iIzZmNmY2ZiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cGF0aCBkPSJNIDMyMCw4MS45ODc4NjggViA0MjcuNzY5MzEgYyAwLDkuMzQzMzYgNy42NDQ2LDE2Ljk4Nzg4IDE2Ljk4NzksMTYuOTg3ODggaCAyNS45NzU3IGMgOS4zNDMzLDAgMTYuOTg3OCwtNy42NDQ1MiAxNi45ODc4LC0xNi45ODc4OCBWIDgxLjk4Nzg2OCBDIDM3OS45NTE0LDcyLjY0NDUzNiAzNzIuMzA2OSw2NSAzNjIuOTYzNiw2NSBIIDMzNi45ODc5IEMgMzI3LjY0NDYsNjUgMzIwLDcyLjY0NDUzNiAzMjAsODEuOTg3ODY4IFoiIC8+CiAgPHBhdGggZD0iTSAxNTAsODEuOTg3ODY4IFYgNDI3Ljc2OTMxIGMgMCw5LjM0MzM2IDcuNjQ0NTUsMTYuOTg3ODggMTYuOTg3NzksMTYuOTg3ODggaCAyNS45NzU1MiBjIDkuMzQzMjQsMCAxNi45ODc2OSwtNy42NDQ1MiAxNi45ODc2OSwtMTYuOTg3ODggViA4MS45ODc4NjggQyAyMDkuOTUxLDcyLjY0NDUzNiAyMDIuMzA2NTUsNjUgMTkyLjk2MzMxLDY1IEggMTY2Ljk4Nzc5IEMgMTU3LjY0NDU1LDY1IDE1MCw3Mi42NDQ1MzYgMTUwLDgxLjk4Nzg2OCBaIiAvPgo8L3N2Zz4K" alt="pause">
<img class="component_icon" draggable="false" src="${ICON.PLAY}" alt="play">
<img class="component_icon hidden" draggable="false" src="${ICON.PAUSE}" alt="pause">
<component-icon name="loading" class="hidden"></component-icon>
<img class="component_icon hidden" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzZmNmY2ZiIgc3Ryb2tlLXdpZHRoPSIyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxwYXRoIGQ9Im0gMjEuOTgzODgzLDE0Ljg4Mzg4MyAtNiwtNS45OTk5OTk1IG0gNiwwIC02LDUuOTk5OTk5NSIgLz4KICA8cGF0aCBkPSJNIDEuMTI1MzM1NiwxNS4yMTc4OTcgViA4Ljc4MTAzMjggYyAwLC0wLjYyNDIyMDUgMC40ODcxOTY3LC0xLjEzMDk5MTcgMS4wODc0OTIzLC0xLjEzMDk5MTcgSCA2LjExMjU3NDggQSAxLjA2NTc0MjIsMS4wNjU3NDIyIDAgMCAwIDYuODgxNDMxOCw3LjMxODM1NjIgTCAxMC4xNDM5MDksMy42MzM5MzI4IGMgMC42ODUxMiwtMC43MTMzOTUgMS44NTYzNDUsLTAuMjA3NzExMSAxLjg1NjM0NSwwLjgwMDM5NDIgdiAxNS4xMzEzNjggYyAwLDEuMDE1NzE1IC0xLjE4NTM2MiwxLjUxNzA0NSAtMS44NjYxMzEsMC43ODk1MTMgTCA2Ljg4MjUxOSwxNi42OTE0NSBBIDEuMDY1NzQyMiwxLjA2NTc0MjIgMCAwIDAgNi4xMDM4NzQ4LDE2LjM0OTk3NSBIIDIuMjEyODI3OSBjIC0wLjYwMDI5NTYsMCAtMS4wODc0OTIzLC0wLjUwNjc2OCAtMS4wODc0OTIzLC0xLjEzMjA3OCB6IiAvPgo8L3N2Zz4K" alt="volume_mute">
<img class="component_icon hidden" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzZmNmY2ZiIgc3Ryb2tlLXdpZHRoPSIyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxwYXRoIGQ9Im0gMTYuMzUwMjI1LDguMTkzNzg3IGMgMS40NDk2MjYsMS45MzM1NTkgMS40NDk2MjYsNS42Nzg4ODQgMCw3LjYxMjQ0NiIgLz4KICA8cGF0aCBkPSJNIDEuMTI1MzM1NiwxNS4yMTc4OTcgViA4Ljc4MTAzMjggYyAwLC0wLjYyNDIyMDUgMC40ODcxOTY3LC0xLjEzMDk5MTcgMS4wODc0OTIzLC0xLjEzMDk5MTcgSCA2LjExMjU3NDggQSAxLjA2NTc0MjIsMS4wNjU3NDIyIDAgMCAwIDYuODgxNDMxOCw3LjMxODM1NjIgTCAxMC4xNDM5MDksMy42MzM5MzI4IGMgMC42ODUxMiwtMC43MTMzOTUgMS44NTYzNDUsLTAuMjA3NzExMSAxLjg1NjM0NSwwLjgwMDM5NDIgdiAxNS4xMzEzNjggYyAwLDEuMDE1NzE1IC0xLjE4NTM2MiwxLjUxNzA0NSAtMS44NjYxMzEsMC43ODk1MTMgTCA2Ljg4MjUxOSwxNi42OTE0NSBBIDEuMDY1NzQyMiwxLjA2NTc0MjIgMCAwIDAgNi4xMDM4NzQ4LDE2LjM0OTk3NSBIIDIuMjEyODI3OSBjIC0wLjYwMDI5NTYsMCAtMS4wODc0OTIzLC0wLjUwNjc2OCAtMS4wODc0OTIzLC0xLjEzMjA3OCB6IiAvPgo8L3N2Zz4K" alt="volume_low">
<img class="component_icon hidden" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzZmNmY2ZiIgc3Ryb2tlLXdpZHRoPSIyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxwYXRoIGQ9Im0gMTYuMzUwMjI1LDguMTkzNzg3IGMgMS40NDk2MjYsMS45MzM1NTkgMS40NDk2MjYsNS42Nzg4ODQgMCw3LjYxMjQ0NiIgLz4KICA8cGF0aCBkPSJtIDE5LjYxMjcwMyw0LjM4NzU2NDQgYyA0LjMzNjkxOSw0LjE0MTE3MDQgNC4zNjMwMTQsMTEuMTEwOTA4NiAwLDE1LjIyNDg4NzYiIC8+CiAgPHBhdGggZD0iTSAxLjEyNTMzNTYsMTUuMjE3ODk3IFYgOC43ODEwMzI4IGMgMCwtMC42MjQyMjA1IDAuNDg3MTk2NywtMS4xMzA5OTE3IDEuMDg3NDkyMywtMS4xMzA5OTE3IEggNi4xMTI1NzQ4IEEgMS4wNjU3NDIyLDEuMDY1NzQyMiAwIDAgMCA2Ljg4MTQzMTgsNy4zMTgzNTYyIEwgMTAuMTQzOTA5LDMuNjMzOTMyOCBjIDAuNjg1MTIsLTAuNzEzMzk1IDEuODU2MzQ1LC0wLjIwNzcxMTEgMS44NTYzNDUsMC44MDAzOTQyIHYgMTUuMTMxMzY4IGMgMCwxLjAxNTcxNSAtMS4xODUzNjIsMS41MTcwNDUgLTEuODY2MTMxLDAuNzg5NTEzIEwgNi44ODI1MTksMTYuNjkxNDUgQSAxLjA2NTc0MjIsMS4wNjU3NDIyIDAgMCAwIDYuMTAzODc0OCwxNi4zNDk5NzUgSCAyLjIxMjgyNzkgYyAtMC42MDAyOTU2LDAgLTEuMDg3NDkyMywtMC41MDY3NjggLTEuMDg3NDkyMywtMS4xMzIwNzggeiIgLz4KPC9zdmc+Cg==" alt="volume">
<img class="component_icon hidden" draggable="false" src="${ICON.VOLUME_MUTE}" alt="volume_mute">
<img class="component_icon hidden" draggable="false" src="${ICON.VOLUME_LOW}" alt="volume_low">
<img class="component_icon hidden" draggable="false" src="${ICON.VOLUME_NORMAL}" alt="volume">
<input type="range" min="0" max="100" value="13">
<span class="timecode">
<div class="current"></div>
@@ -162,10 +166,25 @@ export default function(render, { mime }) {
hls.attachMedia($video);
}),
rxjs.mergeMap(() => rxjs.fromEvent($video, "loadeddata")),
rxjs.tap(() => {
qs($page, ".loader").classList.add("hidden");
qs($page, ".videoplayer_control").classList.remove("hidden");
rxjs.mergeMap(() => {
const $loader = qs($page, ".loader");
$loader.replaceChildren(createElement(`<img style="height:170px;cursor:pointer;filter:brightness(0.5) invert(1);" src="${ICON.PLAY}" />`));
animate($loader, { time: 150, keyframes: [
{ transform: "scale(0.7)" },
{ transform: "scale(1)" },
]});
setSeek(0);
return rxjs.fromEvent($loader, "click").pipe(rxjs.mapTo($loader));
}),
// rxjs.tap(() => renderMenubar(buildMenubar(
// menubarDownload(),
// ))),
rxjs.tap(($loader) => {
$loader.classList.add("hidden")
const $control = qs($page, ".videoplayer_control");
$control.classList.remove("hidden");
animate($control, { time: 300, keyframes: slideYIn(5) })
setStatus(STATUS_PLAYING);
}),
rxjs.share(),
);
@@ -224,7 +243,7 @@ export default function(render, { mime }) {
// feature5: player control - seek
effect(setup$.pipe(
rxjs.switchMap(() => rxjs.fromEvent(qs($page, ".progress"), "click").pipe(
rxjs.map((e) => {
rxjs.map((e) => { // TODO: use onClick instead?
let $progress = e.target;
if (e.target.classList.contains("progress") == false) {
$progress = e.target.parentElement;
@@ -321,7 +340,6 @@ export default function(render, { mime }) {
};
const $container = qs($page, `[data-bind="progress-buffer"]`);
if ($video.buffered.length !== $container.children.length ) {
console.log("RESET");
$container.innerHTML = "";
const $fragment = document.createDocumentFragment();
Array.apply(null, { length: $video.buffered.length })
@@ -0,0 +1,8 @@
export const ICON = {
PLAY: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgdmlld0JveD0iMCAwIDU4Ljc1MiA1OC43NTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+Cgk8cGF0aCBkPSJNIDQ1LjYwNjkxMywyNS4xNzQ3MTIgMjIuNjkxNDAxLDYuMTA4OTgyNSBjIC0xLjQ2OTYyNSwtMC45MDc4NTI1IC0zLjM3MjM1NCwtMC45MDUzNzY2IC00LjgzNjU4NCwwIC0xLjQ5MzUxNSwwLjkyMTA1NzcgLTIuNDIyMTQ1LDIuNjQxODUxIC0yLjQyMjE0NSw0LjQ4OTc0MzUgViA0OC43MzI2NiBjIDAsMS44NDg3MTggMC45Mjc4NiwzLjU2OTUxMSAyLjQxMjg5Nyw0LjQ4NTYxNyAwLjczNDQyNywwLjQ1ODA1MyAxLjU3MzY2MywwLjY5OTg3MiAyLjQyNjc2OSwwLjY5OTg3MiAwLjg1MDc5NSwwIDEuNjg5MjYsLTAuMjQwOTk0IDIuNDIwNjA0LC0wLjY5NTc0NSBsIDIyLjkxNTUxMiwtMTkuMDY3MzggYyAxLjQ5MTk3NCwtMC45MjM1MzMgMi40MTgyOTIsLTIuNjQzNTAxIDIuNDE4MjkyLC00LjQ4ODkxOCAtNy43ZS00LC0xLjg0Mjk0MSAtMC45MjYzMTgsLTMuNTYyOTA5IC0yLjQxOTgzMywtNC40OTEzOTQgeiBNIDQzLjAwMTIxMSwyOS44NjgzMSAyMC42NzA5MDYsNDguNjQyNzU0IGMgLTAuMDYzMTksMC4wMzg3OSAtMC4xMzg3MTYsMC4wNDI5MiAtMC4yMTUwMSwtMC4wMDQxIC0wLjA2NDc0LC0wLjA0MDQ0IC0wLjEwNTU3OSwtMC4xMTcxOTYgLTAuMTA1NTc5LC0wLjE5OTcyOCBWIDEwLjg5MTY2MSBjIDAsLTAuMDgyNTMgMC4wNDAwNywtMC4xNTg0NjIgMC4xMDc4OTEsLTAuMjAwNTUzIDAuMDMyMzcsLTAuMDIwNjMgMC4wNjkzNiwtMC4wMzEzNiAwLjEwNzEyLC0wLjAzMTM2IDAuMDM5MywwIDAuMDc2MjksMC4wMTA3MyAwLjEwOTQzMiwwLjAzMTM2IGwgMjIuMzIyNTk3LDE4Ljc2OTQ5MyBjIDAuMDY4NTksMC4wNDI5MiAwLjExMTc0NCwwLjEyMTMyMiAwLjExMTc0NCwwLjIwNTUwNSAtNy43ZS00LDAuMDg1MDEgLTAuMDQwODQsMC4xNjAxMTIgLTAuMTA3ODksMC4yMDIyMDQgeiIgc3R5bGU9ImZpbGw6IzZmNmY2ZiIgLz4KPC9zdmc+Cg==",
PAUSE: "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgZmlsbD0iIzZmNmY2ZiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cGF0aCBkPSJNIDMyMCw4MS45ODc4NjggViA0MjcuNzY5MzEgYyAwLDkuMzQzMzYgNy42NDQ2LDE2Ljk4Nzg4IDE2Ljk4NzksMTYuOTg3ODggaCAyNS45NzU3IGMgOS4zNDMzLDAgMTYuOTg3OCwtNy42NDQ1MiAxNi45ODc4LC0xNi45ODc4OCBWIDgxLjk4Nzg2OCBDIDM3OS45NTE0LDcyLjY0NDUzNiAzNzIuMzA2OSw2NSAzNjIuOTYzNiw2NSBIIDMzNi45ODc5IEMgMzI3LjY0NDYsNjUgMzIwLDcyLjY0NDUzNiAzMjAsODEuOTg3ODY4IFoiIC8+CiAgPHBhdGggZD0iTSAxNTAsODEuOTg3ODY4IFYgNDI3Ljc2OTMxIGMgMCw5LjM0MzM2IDcuNjQ0NTUsMTYuOTg3ODggMTYuOTg3NzksMTYuOTg3ODggaCAyNS45NzU1MiBjIDkuMzQzMjQsMCAxNi45ODc2OSwtNy42NDQ1MiAxNi45ODc2OSwtMTYuOTg3ODggViA4MS45ODc4NjggQyAyMDkuOTUxLDcyLjY0NDUzNiAyMDIuMzA2NTUsNjUgMTkyLjk2MzMxLDY1IEggMTY2Ljk4Nzc5IEMgMTU3LjY0NDU1LDY1IDE1MCw3Mi42NDQ1MzYgMTUwLDgxLjk4Nzg2OCBaIiAvPgo8L3N2Zz4K",
VOLUME_MUTE: "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzZmNmY2ZiIgc3Ryb2tlLXdpZHRoPSIyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxwYXRoIGQ9Im0gMjEuOTgzODgzLDE0Ljg4Mzg4MyAtNiwtNS45OTk5OTk1IG0gNiwwIC02LDUuOTk5OTk5NSIgLz4KICA8cGF0aCBkPSJNIDEuMTI1MzM1NiwxNS4yMTc4OTcgViA4Ljc4MTAzMjggYyAwLC0wLjYyNDIyMDUgMC40ODcxOTY3LC0xLjEzMDk5MTcgMS4wODc0OTIzLC0xLjEzMDk5MTcgSCA2LjExMjU3NDggQSAxLjA2NTc0MjIsMS4wNjU3NDIyIDAgMCAwIDYuODgxNDMxOCw3LjMxODM1NjIgTCAxMC4xNDM5MDksMy42MzM5MzI4IGMgMC42ODUxMiwtMC43MTMzOTUgMS44NTYzNDUsLTAuMjA3NzExMSAxLjg1NjM0NSwwLjgwMDM5NDIgdiAxNS4xMzEzNjggYyAwLDEuMDE1NzE1IC0xLjE4NTM2MiwxLjUxNzA0NSAtMS44NjYxMzEsMC43ODk1MTMgTCA2Ljg4MjUxOSwxNi42OTE0NSBBIDEuMDY1NzQyMiwxLjA2NTc0MjIgMCAwIDAgNi4xMDM4NzQ4LDE2LjM0OTk3NSBIIDIuMjEyODI3OSBjIC0wLjYwMDI5NTYsMCAtMS4wODc0OTIzLC0wLjUwNjc2OCAtMS4wODc0OTIzLC0xLjEzMjA3OCB6IiAvPgo8L3N2Zz4K",
VOLUME_LOW: "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzZmNmY2ZiIgc3Ryb2tlLXdpZHRoPSIyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxwYXRoIGQ9Im0gMTYuMzUwMjI1LDguMTkzNzg3IGMgMS40NDk2MjYsMS45MzM1NTkgMS40NDk2MjYsNS42Nzg4ODQgMCw3LjYxMjQ0NiIgLz4KICA8cGF0aCBkPSJNIDEuMTI1MzM1NiwxNS4yMTc4OTcgViA4Ljc4MTAzMjggYyAwLC0wLjYyNDIyMDUgMC40ODcxOTY3LC0xLjEzMDk5MTcgMS4wODc0OTIzLC0xLjEzMDk5MTcgSCA2LjExMjU3NDggQSAxLjA2NTc0MjIsMS4wNjU3NDIyIDAgMCAwIDYuODgxNDMxOCw3LjMxODM1NjIgTCAxMC4xNDM5MDksMy42MzM5MzI4IGMgMC42ODUxMiwtMC43MTMzOTUgMS44NTYzNDUsLTAuMjA3NzExMSAxLjg1NjM0NSwwLjgwMDM5NDIgdiAxNS4xMzEzNjggYyAwLDEuMDE1NzE1IC0xLjE4NTM2MiwxLjUxNzA0NSAtMS44NjYxMzEsMC43ODk1MTMgTCA2Ljg4MjUxOSwxNi42OTE0NSBBIDEuMDY1NzQyMiwxLjA2NTc0MjIgMCAwIDAgNi4xMDM4NzQ4LDE2LjM0OTk3NSBIIDIuMjEyODI3OSBjIC0wLjYwMDI5NTYsMCAtMS4wODc0OTIzLC0wLjUwNjc2OCAtMS4wODc0OTIzLC0xLjEzMjA3OCB6IiAvPgo8L3N2Zz4K",
VOLUME_NORMAL: "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzZmNmY2ZiIgc3Ryb2tlLXdpZHRoPSIyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxwYXRoIGQ9Im0gMTYuMzUwMjI1LDguMTkzNzg3IGMgMS40NDk2MjYsMS45MzM1NTkgMS40NDk2MjYsNS42Nzg4ODQgMCw3LjYxMjQ0NiIgLz4KICA8cGF0aCBkPSJtIDE5LjYxMjcwMyw0LjM4NzU2NDQgYyA0LjMzNjkxOSw0LjE0MTE3MDQgNC4zNjMwMTQsMTEuMTEwOTA4NiAwLDE1LjIyNDg4NzYiIC8+CiAgPHBhdGggZD0iTSAxLjEyNTMzNTYsMTUuMjE3ODk3IFYgOC43ODEwMzI4IGMgMCwtMC42MjQyMjA1IDAuNDg3MTk2NywtMS4xMzA5OTE3IDEuMDg3NDkyMywtMS4xMzA5OTE3IEggNi4xMTI1NzQ4IEEgMS4wNjU3NDIyLDEuMDY1NzQyMiAwIDAgMCA2Ljg4MTQzMTgsNy4zMTgzNTYyIEwgMTAuMTQzOTA5LDMuNjMzOTMyOCBjIDAuNjg1MTIsLTAuNzEzMzk1IDEuODU2MzQ1LC0wLjIwNzcxMTEgMS44NTYzNDUsMC44MDAzOTQyIHYgMTUuMTMxMzY4IGMgMCwxLjAxNTcxNSAtMS4xODUzNjIsMS41MTcwNDUgLTEuODY2MTMxLDAuNzg5NTEzIEwgNi44ODI1MTksMTYuNjkxNDUgQSAxLjA2NTc0MjIsMS4wNjU3NDIyIDAgMCAwIDYuMTAzODc0OCwxNi4zNDk5NzUgSCAyLjIxMjgyNzkgYyAtMC42MDAyOTU2LDAgLTEuMDg3NDkyMywtMC41MDY3NjggLTEuMDg3NDkyMywtMS4xMzIwNzggeiIgLz4KPC9zdmc+Cg==",
};
@@ -0,0 +1,33 @@
import { createFragment } from "../../lib/skeleton/index.js";
export function menubarDownload() {
const $fragment = createFragment(`
<span class="download-button">
<span>
<a href="/api/files/cat?path=%2FDocuments%2Fgetting_started.pdf" download="getting_started.pdf">
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzODQgNTEyIj4KICA8cGF0aCBmaWxsPSIjZjJmMmYyIiBkPSJNIDM2MCw0NjAgSCAyNCBDIDEwLjcsNDYwIDAsNDUzLjMgMCw0NDAgdiAtMTIgYyAwLC0xMy4zIDEwLjcsLTIwIDI0LC0yMCBoIDMzNiBjIDEzLjMsMCAyNCw2LjcgMjQsMjAgdiAxMiBjIDAsMTMuMyAtMTAuNywyMCAtMjQsMjAgeiIgLz4KICA8cGF0aCBmaWxsPSIjZjJmMmYyIiBkPSJNIDIyNi41NTM5LDIzNC44ODQyOCBWIDUyLjk0MzI4MyBjIDAsLTYuNjI3IC01LjM3MywtMTIgLTEyLC0xMiBoIC00NCBjIC02LjYyNywwIC0xMiw1LjM3MyAtMTIsMTIgViAyMzQuODg0MjggaCAtNTIuMDU5IGMgLTIxLjM4MiwwIC0zMi4wOSwyNS44NTEgLTE2Ljk3MSw0MC45NzEgbCA4Ni4wNTksODYuMDU5IGMgOS4zNzMsOS4zNzMgMjQuNTY5LDkuMzczIDMzLjk0MSwwIGwgODYuMDU5LC04Ni4wNTkgYyAxNS4xMTksLTE1LjExOSA0LjQxMSwtNDAuOTcxIC0xNi45NzEsLTQwLjk3MSB6IiAvPgo8L3N2Zz4K" alt="download_white">
</a>
</span>
</span>
`);
$fragment.querySelector(".download-button").onclick = () => {
console.log("CLICK");
}
return $fragment;
}
export function menubarChromecast() {
return createFragment(`
<span class="specific">
<span id="chromecast-target"></span>
</span>
`);
}
export function buildMenubar(...$fragments) {
const $buttons = document.createDocumentFragment();
for (let i=0; i<$fragments.length; i++) {
$buttons.appendChild($fragments[i]);
}
return $buttons;
}