chore (image): revamp image viewer

This commit is contained in:
MickaelK 2025-05-08 22:11:03 +10:00
parent 844fd5d7bb
commit ac95d9ea75
8 changed files with 205 additions and 193 deletions

View file

@ -5,6 +5,7 @@
"3gpp": "video/3gpp", "3gpp": "video/3gpp",
"7z": "application/x-7z-compressed", "7z": "application/x-7z-compressed",
"a": "application/x-archive", "a": "application/x-archive",
"aai": "image/x-dune-aai",
"aco": "application/x-aco", "aco": "application/x-aco",
"ai": "application/pdf", "ai": "application/pdf",
"aif": "audio/x-aiff", "aif": "audio/x-aiff",
@ -17,6 +18,7 @@
"asx": "video/x-ms-asf", "asx": "video/x-ms-asf",
"atom": "application/atom+xml", "atom": "application/atom+xml",
"avi": "video/x-msvideo", "avi": "video/x-msvideo",
"avif": "image/avif",
"avro": "application/vnd.apache.avro", "avro": "application/vnd.apache.avro",
"bin": "application/octet-stream", "bin": "application/octet-stream",
"bmp": "image/x-ms-bmp", "bmp": "image/x-ms-bmp",
@ -24,16 +26,20 @@
"cab": "application/vnd.ms-cab-compressed", "cab": "application/vnd.ms-cab-compressed",
"cap": "application/x-pcap", "cap": "application/x-pcap",
"cco": "application/x-cocoa", "cco": "application/x-cocoa",
"cdr": "application/vnd.corel-draw",
"cin": "image/x-cin",
"cr2": "image/x-canon-cr2", "cr2": "image/x-canon-cr2",
"crt": "application/x-x509-ca-cert", "crt": "application/x-x509-ca-cert",
"crw": "image/x-canon-crw", "crw": "image/x-canon-crw",
"css": "text/css", "css": "text/css",
"csv": "text/csv", "csv": "text/csv",
"cur": "image/x-win-bitmap",
"dae": "model/vnd.collada+xml", "dae": "model/vnd.collada+xml",
"db": "application/x-sqlite3", "db": "application/x-sqlite3",
"dbf": "application/dbf", "dbf": "application/dbf",
"dcm": "image/dicom", "dcm": "image/dicom",
"dcr": "image/x-kodak-dcr", "dcr": "image/x-kodak-dcr",
"dds": "image/x-dds",
"deb": "application/octet-stream", "deb": "application/octet-stream",
"der": "application/x-x509-ca-cert", "der": "application/x-x509-ca-cert",
"dll": "application/x-msdownload", "dll": "application/x-msdownload",
@ -45,16 +51,20 @@
"dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template", "dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
"dotm": "application/vnd.ms-word.template.macroEnabled.12", "dotm": "application/vnd.ms-word.template.macroEnabled.12",
"dpkg": "application/dpkg-www-installer", "dpkg": "application/dpkg-www-installer",
"dpx": "image/dpx",
"ds_store": "application/octet-stream", "ds_store": "application/octet-stream",
"dxf": "application/dxf", "dxf": "application/dxf",
"dwg": "application/acad", "dwg": "application/acad",
"dylib": "application/x-dylib", "dylib": "application/x-dylib",
"ear": "application/java-archive", "ear": "application/java-archive",
"emf": "image/emf",
"emz": "image/x-emz",
"eot": "application/vnd.ms-fontobject", "eot": "application/vnd.ms-fontobject",
"eps": "application/postscript", "eps": "application/postscript",
"epub": "application/epub+zip", "epub": "application/epub+zip",
"erf": "image/x-epson-erf", "erf": "image/x-epson-erf",
"exe": "application/octet-stream", "exe": "application/octet-stream",
"exr": "image/x-exr",
"fbx": "application/fbx", "fbx": "application/fbx",
"fea": "application/vnd.apache.feather", "fea": "application/vnd.apache.feather",
"feather": "application/vnd.apache.feather", "feather": "application/vnd.apache.feather",
@ -84,10 +94,12 @@
"jad": "text/vnd.sun.j2me.app-descriptor", "jad": "text/vnd.sun.j2me.app-descriptor",
"jar": "application/java-archive", "jar": "application/java-archive",
"jardiff": "application/x-java-archive-diff", "jardiff": "application/x-java-archive-diff",
"jfif": "image/jpeg",
"jng": "image/x-jng", "jng": "image/x-jng",
"jnlp": "application/x-java-jnlp-file", "jnlp": "application/x-java-jnlp-file",
"jpeg": "image/jpeg", "jpeg": "image/jpeg",
"jpg": "image/jpeg", "jpg": "image/jpeg",
"jp2": "image/jp2",
"js": "application/javascript", "js": "application/javascript",
"json": "application/json", "json": "application/json",
"kar": "audio/midi", "kar": "audio/midi",
@ -96,6 +108,8 @@
"kicad_sch": "application/vnd.kicad-sch", "kicad_sch": "application/vnd.kicad-sch",
"kml": "application/vnd.google-earth.kml+xml", "kml": "application/vnd.google-earth.kml+xml",
"kmz": "application/vnd.google-earth.kmz", "kmz": "application/vnd.google-earth.kmz",
"ktx": "image/ktx",
"ktx2": "image/ktx2",
"m3u8": "application/vnd.apple.mpegurl", "m3u8": "application/vnd.apple.mpegurl",
"m4a": "audio/x-m4a", "m4a": "audio/x-m4a",
"m4v": "video/x-m4v", "m4v": "video/x-m4v",
@ -135,19 +149,26 @@
"org": "text/org", "org": "text/org",
"otf": "font/otf", "otf": "font/otf",
"parquet": "application/vnd.apache.parquet", "parquet": "application/vnd.apache.parquet",
"pbm": "image/x-portable-bitmap",
"pdb": "application/x-pilot", "pdb": "application/x-pilot",
"pcap": "application/vnd.tcpdump.pcap", "pcap": "application/vnd.tcpdump.pcap",
"pcapng": "application/x-pcapng", "pcapng": "application/x-pcapng",
"pcd": "image/x-photo-cd",
"pcx": "image/vnd.zbrush.pcx",
"pdf": "application/pdf", "pdf": "application/pdf",
"pef": "image/x-pentax-pef", "pef": "image/x-pentax-pef",
"pem": "application/x-x509-ca-cert", "pem": "application/x-x509-ca-cert",
"pes": "image/x-pes",
"pgm": "image/x-portable-greymap",
"pkg": "application/x-newton-compatible-pkg", "pkg": "application/x-newton-compatible-pkg",
"pl": "application/x-perl", "pl": "application/x-perl",
"pm": "application/x-perl", "pm": "application/x-perl",
"png": "image/png", "png": "image/png",
"pnm": "image/x-portable-anymap",
"potm": "application/vnd.ms-powerpoint.template.macroEnabled.12", "potm": "application/vnd.ms-powerpoint.template.macroEnabled.12",
"potx": "application/vnd.openxmlformats-officedocument.presentationml.template", "potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
"ppam": "application/vnd.ms-powerpoint.addin.macroEnabled.12", "ppam": "application/vnd.ms-powerpoint.addin.macroEnabled.12",
"ppm": "image/x-portable-pixmap",
"pps": "application/vnd.ms-powerpoint", "pps": "application/vnd.ms-powerpoint",
"ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow", "ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
"ppsm": "application/vnd.ms-powerpoint.slideshow.macroEnabled.12", "ppsm": "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
@ -164,6 +185,8 @@
"ram": "audio/x-pn-realaudio", "ram": "audio/x-pn-realaudio",
"rar": "application/x-rar-compressed", "rar": "application/x-rar-compressed",
"raw": "image/x-raw", "raw": "image/x-raw",
"rgb": "image/x-rgb",
"rgba": "image/x-rgba",
"rpm": "application/x-redhat-package-manager", "rpm": "application/x-redhat-package-manager",
"rss": "application/rss+xml", "rss": "application/rss+xml",
"rtf": "application/rtf", "rtf": "application/rtf",
@ -171,15 +194,18 @@
"run": "application/x-makeself", "run": "application/x-makeself",
"rw2": "image/x-panasonic-rw2", "rw2": "image/x-panasonic-rw2",
"sea": "application/x-sea", "sea": "application/x-sea",
"sgi": "image/x-sgi",
"shtml": "text/html", "shtml": "text/html",
"shp": "application/vnd.shp", "shp": "application/vnd.shp",
"shx": "application/vnd.shx", "shx": "application/vnd.shx",
"sit": "application/x-stuffit", "sit": "application/x-stuffit",
"sketch": "application/x-sketch",
"so": "application/x-sharedlib", "so": "application/x-sharedlib",
"sql": "application/x-sqlite3", "sql": "application/x-sqlite3",
"sqlite": "application/x-sqlite3", "sqlite": "application/x-sqlite3",
"sqlite3": "application/x-sqlite3", "sqlite3": "application/x-sqlite3",
"sr2": "image/x-sony-sr2", "sr2": "image/x-sony-sr2",
"srf": "image/x-sony-srf",
"srw": "image/x-samsung-srw", "srw": "image/x-samsung-srw",
"stl": "model/stl", "stl": "model/stl",
"step": "model/step", "step": "model/step",
@ -189,6 +215,7 @@
"swf": "application/x-shockwave-flash", "swf": "application/x-shockwave-flash",
"tar": "application/x-tar", "tar": "application/x-tar",
"tcl": "application/x-tcl", "tcl": "application/x-tcl",
"tga": "image/x-tga",
"tgz": "application/x-gzip", "tgz": "application/x-gzip",
"tif": "image/tiff", "tif": "image/tiff",
"tiff": "image/tiff", "tiff": "image/tiff",
@ -214,16 +241,22 @@
"wmv": "video/x-ms-wmv", "wmv": "video/x-ms-wmv",
"woff": "font/woff", "woff": "font/woff",
"woff2": "font/woff2", "woff2": "font/woff2",
"wbmp": "image/vnd.wap.wbmp",
"wrl": "x-world/x-vrml", "wrl": "x-world/x-vrml",
"x3d": "model/x3d+xml", "x3d": "model/x3d+xml",
"x3dv": "model/x3d-vrml", "x3dv": "model/x3d-vrml",
"x3db": "model/x3d+fastinfoset", "x3db": "model/x3d+fastinfoset",
"x3f": "image/x-x3f", "x3f": "image/x-x3f",
"xbm": "image/x-xbitmap",
"xcf": "image/x-xcf",
"xd": "application/x-adobe-xd",
"xhtml": "application/xhtml+xml", "xhtml": "application/xhtml+xml",
"xls": "application/vnd.ms-excel", "xls": "application/vnd.ms-excel",
"xlsx": "application/excel", "xlsx": "application/excel",
"xml": "application/xml", "xml": "application/xml",
"xpi": "application/x-xpinstall", "xpi": "application/x-xpinstall",
"xpm": "image/x-xpixmap",
"xwd": "image/x-xwindowdump",
"xspf": "application/xspf+xml", "xspf": "application/xspf+xml",
"zip": "application/zip" "zip": "application/zip"
} }

View file

@ -9,10 +9,10 @@ import { transition } from "./common.js";
import "../../components/icon.js"; import "../../components/icon.js";
import "./component_menubar.js"; import "./component_menubar.js";
export default async function(render, { acl$, getFilename, getDownloadUrl }) { export default async function(render, { acl$, getFilename, getDownloadUrl, hasMenubar = true }) {
const $page = createElement(` const $page = createElement(`
<div class="component_filedownloader"> <div class="component_filedownloader">
<component-menubar filename="${getFilename()}"></component-menubar> <component-menubar filename="${getFilename()}" class="${!hasMenubar && "hidden"}"></component-menubar>
<div class="download_button no-select"> <div class="download_button no-select">
<a download="${getFilename()}" href="${getDownloadUrl()}">${t("DOWNLOAD")}</a> <a download="${getFilename()}" href="${getDownloadUrl()}">${t("DOWNLOAD")}</a>
<component-icon name="loading" class="hidden"></component-icon> <component-icon name="loading" class="hidden"></component-icon>

View file

@ -2,14 +2,8 @@
body:not(.dark-mode) .component_imageviewer .component_image_container .fullscreen .component_pager .wrapper > span { body:not(.dark-mode) .component_imageviewer .component_image_container .fullscreen .component_pager .wrapper > span {
background: #525659; background: #525659;
} }
.dark-mode .component_imageviewer .component_image_container {
.component_imageviewer, .component_imageviewer .component_image_container > .images_wrapper { background: #2d2f31;
flex: 1;
display: flex;
overflow: hidden;
width: 100%;
height: 100%;
flex-direction: column;
} }
.component_imageviewer .component_image_container { .component_imageviewer .component_image_container {
@ -19,10 +13,40 @@ body:not(.dark-mode) .component_imageviewer .component_image_container .fullscre
width: 100%; width: 100%;
text-align: center; text-align: center;
overflow: hidden; overflow: hidden;
padding: 10px 10px 10px 10px;
height: 100%; height: 100%;
box-sizing: border-box; box-sizing: border-box;
} }
.component_imageviewer, .component_imageviewer .images_wrapper {
flex: 1;
display: flex;
overflow: hidden;
width: 100%;
height: 100%;
flex-direction: column;
position: relative;
justify-content: center;
}
.component_imageviewer img.photo {
margin: 10px;
width: fit-content;
max-width: 98%;;
min-height: 100px;
max-height: 100%;
background: var(--dark);
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;
border-radius: 2px;
align-self: center;
}
.component_imageviewer img.photo.idle {
transition: 0.2s ease transform;
}
@media screen and (max-width: 500px) {
.component_imageviewer img.photo {
margin: 7px;
}
}
/* fullscreen mode */
.component_imageviewer .component_image_container.fullscreen { .component_imageviewer .component_image_container.fullscreen {
background: var(--dark); background: var(--dark);
} }
@ -32,161 +56,113 @@ body:not(.dark-mode) .component_imageviewer .component_image_container .fullscre
.component_imageviewer .component_image_container.fullscreen img.photo { .component_imageviewer .component_image_container.fullscreen img.photo {
background: var(--color); background: var(--color);
} }
@media screen and (max-height: 410px) {
.component_imageviewer .component_image_container {
padding: 5px 0px 40px 10px;
}
.component_imageviewer .component_image_container .component_pager .wrapper {
padding: 5px 0;
}
.component_imageviewer .component_image_container .component_pager .wrapper > span {
padding: 2px 5px;
}
.component_imageviewer .component_image_container .images_aside {
margin: -5px -5px -40px 10px !important;
}
}
.component_imageviewer .component_image_container .images_wrapper {
width: 100%;
position: relative;
justify-content: center;
}
.component_imageviewer .component_image_container .images_wrapper > span {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.component_imageviewer .component_image_container .images_aside {
flex: 0;
text-align: left;
width: 0;
z-index: 1;
min-width: 0px;
transition: 0.3s ease min-width;
border-left: 1px solid var(--color);
background: #949290;
margin: -15px -10px -65px 10px;
color: var(--dark);
}
.component_imageviewer .component_image_container .images_aside.open {
min-width: 300px;
transition: 0.5s ease min-width;
}
@media screen and (max-width: 850px) {
.component_imageviewer .component_image_container .images_aside.open {
min-width: 250px;
font-size: 0.94em;
}
.component_imageviewer .component_image_container .images_aside.open .header, .component_imageviewer .component_image_container .images_aside.open .content {
padding: 15px 15px 0px 15px;
}
}
@media screen and (max-width: 650px) {
.component_imageviewer .component_image_container .images_aside.open {
min-width: 200px;
}
}
@media screen and (max-width: 580px) {
.component_imageviewer .component_image_container .images_aside.open {
width: 0px;
min-width: 0;
}
}
.component_imageviewer .component_image_container .images_aside.open .content {
transform: translateX(0px);
opacity: 1;
}
.component_imageviewer .component_image_container .images_aside .content {
transition: 0.2s ease opacity, 0.3s ease transform;
opacity: 0;
transform: translateX(10px);
transition-delay: 0.2s;
}
.component_imageviewer .component_image_container .images_aside .header {
display: flex;
line-height: 25px;
white-space: nowrap;
padding: 20px 25px;
font-size: 1.25em;
}
.component_imageviewer .component_image_container .images_aside .header .component_icon {
height: 18px;
float: right;
cursor: pointer;
}
.component_imageviewer .component_image_container .images_aside .content {
padding: 10px 20px 0px 20px;
}
.component_imageviewer .component_image_container .images_aside .content .content_box {
clear: both;
opacity: 0.85;
margin-bottom: 20px;
}
.component_imageviewer .component_image_container .images_aside .content .content_box > div {
margin: 3px 0;
width: calc(100% - 40px);
}
.component_imageviewer .component_image_container .images_aside .content .content_box .component_icon {
height: 30px;
width: 30px;
float: left;
padding: 5px 10px 5px 0;
}
.component_imageviewer .component_image_container .images_aside .content .component_mapshot {
margin-bottom: 10px;
}
.component_imageviewer .component_image_container .images_aside .content .more, .component_imageviewer .component_image_container .images_aside .content .meta_key {
text-align: right;
font-size: 0.9em;
margin: 10px 0;
}
.component_imageviewer .component_image_container .images_aside .content .more_container {
margin: 30px 0 50px 0;
padding-bottom: 20px;
}
.component_imageviewer .component_image_container .images_aside .content .more_container .meta_key {
display: flex;
justify-content: space-between;
margin: 5px 0;
border-top: 1px dashed var(--color);
padding-top: 5px;
}
.component_imageviewer .component_image_container .images_aside .content .more_container .meta_key .title {
margin-right: 5px;
}
.component_imageviewer .component_image_container .images_aside .content .more_container .meta_key .value {
color: var(--bg-color);
}
.component_imageviewer .component_image_container img.photo:not(.error) {
margin: auto;
max-height: 100%;
max-width: 100%;
min-height: 100px;
background: var(--dark);
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;
border-radius: 2px;
}
.component_imageviewer .component_image_container img.photo.idle {
transition: 0.2s ease transform;
}
/* image loading spinner */
.component_imageviewer .component_loader { .component_imageviewer .component_loader {
margin-top: 0; margin-top: 0;
} }
.component_imageviewer .component_loader img { .component_imageviewer .component_loader img {
width: 40px; width: 40px;
} }
.component_imageviewer .error {
/* error loading image */
.component_imageviewer .component_filedownloader { margin: 0 auto; }
/* information menu */
.component_imageviewer .images_aside {
flex: 0;
text-align: left;
width: 0;
z-index: 1;
min-width: 0px;
transition: 0.15s ease min-width;
border-left: 1px solid var(--border);
background: #949290;
color: var(--dark); color: var(--dark);
font-size: 1.3em;
} }
.component_imageviewer .component_image_container img.error { .dark-mode .component_imageviewer .images_aside {
filter: contrast(0.8); background: #f2f2f2;
width: 160px;
margin: 0 auto;
} }
.dark-mode .component_imageviewer .component_image_container { .component_imageviewer .images_aside.open {
background: #232426; min-width: 300px;
transition: 0.5s ease min-width;
transition: 0.3s ease min-width;
}
@media screen and (max-width: 850px) {
.component_imageviewer .images_aside.open {
min-width: 250px;
font-size: 0.94em;
}
.component_imageviewer .images_aside.open [data-bind="header"],
.component_imageviewer .images_aside.open [data-bind="body"] {
padding: 15px 15px 0px 15px;
}
}
@media screen and (max-width: 650px) {
.component_imageviewer .images_aside.open {
min-width: 200px;
}
}
@media screen and (max-width: 580px) {
.component_imageviewer .images_aside.open {
width: 0px;
min-width: 0;
}
}
.component_imageviewer .images_aside.open [data-bind="body"] {
transform: translateX(0px);
opacity: 1;
}
.component_imageviewer .images_aside [data-bind="body"] {
transition: 0.2s ease opacity, 0.3s ease transform;
opacity: 0;
transform: translateX(10px);
transition-delay: 0.2s;
}
.component_imageviewer .images_aside .header {
display: flex;
line-height: 25px;
white-space: nowrap;
padding: 20px 25px;
font-size: 1.25em;
}
.component_imageviewer .images_aside .header .component_icon {
height: 18px;
float: right;
cursor: pointer;
}
.component_imageviewer .images_aside [data-bind="body"] {
padding: 10px 20px 0px 20px;
}
.component_imageviewer .images_aside [data-bind="body"] .content_box {
clear: both;
opacity: 0.85;
margin-bottom: 20px;
}
.component_imageviewer .images_aside [data-bind="body"] .content_box > div {
width: calc(100% - 40px);
}
.component_imageviewer .images_aside [data-bind="body"] .content_box .component_icon {
height: 30px;
width: 30px;
float: left;
padding: 5px 10px 5px 0;
}
.component_imageviewer .images_aside [data-bind="body"] .component_mapshot {
margin-bottom: 10px;
}
.component_imageviewer .images_aside .meta_key {
display: flex;
justify-content: space-between;
margin: 5px 0;
border-top: 1px solid #ffffff15;
padding-top: 5px;
text-align: right;
font-size: 0.85em;
}
.component_imageviewer .images_aside .meta_key .title {
margin-right: 5px;
}
.component_imageviewer .images_aside .meta_key .value {
color: var(--bg-color);
} }

View file

@ -1,10 +1,12 @@
import { createElement, createRender, onDestroy } from "../../lib/skeleton/index.js"; import { createElement, createRender, onDestroy } from "../../lib/skeleton/index.js";
import { toHref } from "../../lib/skeleton/router.js"; import { toHref } from "../../lib/skeleton/router.js";
import rxjs, { effect, onLoad, onClick } from "../../lib/rx.js"; import rxjs, { effect, onLoad, onClick } from "../../lib/rx.js";
import ajax from "../../lib/ajax.js";
import { animate } from "../../lib/animate.js"; import { animate } from "../../lib/animate.js";
import { extname } from "../../lib/path.js"; import { extname } from "../../lib/path.js";
import { qs } from "../../lib/dom.js"; import { qs } from "../../lib/dom.js";
import { get as getConfig } from "../../model/config.js"; import { get as getConfig } from "../../model/config.js";
import { load as loadPlugin } from "../../model/plugin.js";
import { Chromecast } from "../../model/chromecast.js"; import { Chromecast } from "../../model/chromecast.js";
import { loadCSS } from "../../helpers/loader.js"; import { loadCSS } from "../../helpers/loader.js";
import { createLoader } from "../../components/loader.js"; import { createLoader } from "../../components/loader.js";
@ -15,17 +17,22 @@ import ctrlError from "../ctrl_error.js";
import { transition } from "./common.js"; import { transition } from "./common.js";
import componentMetadata, { init as initMetadata } from "./application_image_metadata.js"; import componentMetadata, { init as initMetadata } from "./application_image_metadata.js";
import ctrlDownloader, { init as initDownloader } from "./application_downloader.js";
import componentPager, { init as initPager } from "./component_pager.js"; import componentPager, { init as initPager } from "./component_pager.js";
import { renderMenubar, buttonDownload, buttonFullscreen } from "./component_menubar.js"; import { renderMenubar, buttonDownload, buttonFullscreen } from "./component_menubar.js";
export default function(render, { getFilename, getDownloadUrl, hasMenubar = true }) { class IImage {
getSRC() { throw new Error("NOT_IMPLEMENTED"); }
}
export default function(render, { getFilename, getDownloadUrl, mime, hasMenubar = true, acl$ }) {
const $page = createElement(` const $page = createElement(`
<div class="component_imageviewer"> <div class="component_imageviewer">
<component-menubar filename="${getFilename()}" class="${!hasMenubar && "hidden"}"></component-menubar> <component-menubar filename="${getFilename()}" class="${!hasMenubar && "hidden"}"></component-menubar>
<div class="component_image_container"> <div class="component_image_container">
<div class="images_wrapper"> <div class="images_wrapper">
<img class="photo idle hidden" src="${getDownloadUrl()}&size=${window.innerWidth}"> <img class="photo idle hidden" draggable="false" />
</div> </div>
<div class="images_aside scroll-y"></div> <div class="images_aside scroll-y"></div>
<div class="component_pager hidden"></div> <div class="component_pager hidden"></div>
@ -37,6 +44,7 @@ export default function(render, { getFilename, getDownloadUrl, hasMenubar = true
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 $menubar = qs($page, "component-menubar");
const removeLoader = createLoader($imgContainer); const removeLoader = createLoader($imgContainer);
const load$ = new rxjs.BehaviorSubject(null); const load$ = new rxjs.BehaviorSubject(null);
const toggleInfo = () => { const toggleInfo = () => {
@ -45,17 +53,25 @@ export default function(render, { getFilename, getDownloadUrl, hasMenubar = true
}; };
renderMenubar( renderMenubar(
qs($page, "component-menubar"), $menubar,
buttonDownload(getFilename(), getDownloadUrl()), buttonDownload(getFilename(), getDownloadUrl()),
buttonFullscreen(qs($page, ".component_image_container")), buttonFullscreen(qs($page, ".component_image_container")),
buttonInfo({ toggle: toggleInfo }), buttonInfo({ toggle: toggleInfo }),
buttonChromecast(getFilename(), getDownloadUrl()), buttonChromecast(getFilename(), getDownloadUrl()),
); );
effect(onLoad($photo).pipe( effect(rxjs.from(loadPlugin(mime)).pipe(
rxjs.tap(() => { rxjs.mergeMap(async(loader) => {
load$.next($photo); let src = `${getDownloadUrl()}&size=${window.innerWidth}`;
if (loader) {
const { response } = await ajax({ url: getDownloadUrl(), responseType: "arraybuffer" }).toPromise();
const img = new (await loader(IImage, { mime, $menubar, getFilename, getDownloadUrl }))();
src = await img.getSRC(response);
}
$photo.setAttribute("src", src);
await onLoad($photo).toPromise();
}), }),
rxjs.tap(() => load$.next($photo)),
removeLoader, removeLoader,
rxjs.tap(() => animate($photo, { rxjs.tap(() => animate($photo, {
onEnter: () => $photo.classList.remove("hidden"), onEnter: () => $photo.classList.remove("hidden"),
@ -69,25 +85,18 @@ export default function(render, { getFilename, getDownloadUrl, hasMenubar = true
})), })),
rxjs.catchError((err) => { rxjs.catchError((err) => {
if (err.target instanceof HTMLElement && err.type === "error") { if (err.target instanceof HTMLElement && err.type === "error") {
return rxjs.of($photo).pipe( return rxjs.of(initDownloader()).pipe(
removeLoader, removeLoader,
rxjs.tap(($img) => { rxjs.mergeMap(() => {
$img.setAttribute("src", "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgaGVpZ2h0PSIxNiIKICAgd2lkdGg9IjE2IgogICB2ZXJzaW9uPSIxLjEiCiAgIGlkPSJzdmcyNzU2IgogICBzb2RpcG9kaTpkb2NuYW1lPSJkb3dubG9hZC5zdmciCiAgIGlua3NjYXBlOnZlcnNpb249IjEuMi4yIChiMGE4NDg2NTQxLCAyMDIyLTEyLTAxKSIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzMjc2MCIgLz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9Im5hbWVkdmlldzI3NTgiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjMDAwMDAwIgogICAgIGJvcmRlcm9wYWNpdHk9IjAuMjUiCiAgICAgaW5rc2NhcGU6c2hvd3BhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlY2hlY2tlcmJvYXJkPSIwIgogICAgIGlua3NjYXBlOmRlc2tjb2xvcj0iI2QxZDFkMSIKICAgICBzaG93Z3JpZD0iZmFsc2UiCiAgICAgaW5rc2NhcGU6em9vbT0iNDEuNzE5MyIKICAgICBpbmtzY2FwZTpjeD0iMTEuMzI1NjkzIgogICAgIGlua3NjYXBlOmN5PSI4LjU1NzE5MDUiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxOTA0IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjExNTciCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjciCiAgICAgaW5rc2NhcGU6d2luZG93LXk9IjM0IgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ic3ZnMjc1NiIgLz4KICA8cGF0aAogICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO3RleHQtaW5kZW50OjA7dGV4dC10cmFuc2Zvcm06bm9uZTtmaWxsOiMzYjQwNDU7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlLXdpZHRoOjAuOTg0ODEwNDEiCiAgICAgZD0ibSAyLDEzLjA4MjQxMiAwLjAxOTQ2MiwxLjQ5MjM0NyBjIDVlLTYsMC4yMjIxNDUgMC4yMDU1OTAyLDAuNDI0MjYyIDAuNDMxMTUwMiwwLjQyNDI3MiBMIDEzLjU4OTYxMiwxNSBDIDEzLjgxNTE3MywxNC45OTk5OTUgMTMuOTk5OTksMTQuNzk3ODc0IDE0LDE0LjU3NTcyOSB2IC0xLjQ5MzMxNyBjIC00LjE3MTg2OTIsMC42NjIwMjMgLTcuNjUxNjkyOCwwLjM5ODY5NiAtMTIsMCB6IgogICAgIGlkPSJwYXRoMjc1MCIgLz4KICA8cGF0aAogICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO3RleHQtaW5kZW50OjA7dGV4dC10cmFuc2Zvcm06bm9uZTtkaXNwbGF5OmlubGluZTtmaWxsOiNmOWY5ZmE7c3Ryb2tlLXdpZHRoOjAuOTg0MDgxMjc7ZmlsbC1vcGFjaXR5OjEiCiAgICAgZD0iTSAyLjM1MDEsMS4wMDEzMzEyIEMgMi4xNTI1OSwxLjAzODMyNDcgMS45OTY1OSwxLjIyNzI3MjMgMi4wMDAwOSwxLjQyNDkzNTYgViAxNC4xMzM0NTcgYyA1ZS02LDAuMjIxODE2IDAuMjA1MjMsMC40MjM2MzQgMC40MzA3OSwwLjQyMzY0NCBsIDExLjEzOSwtMS4wMWUtNCBjIDAuMjI1NTYsLTZlLTYgMC40MzAxMSwtMC4yMDA3NTggMC40MzAxMiwtMC40MjI1NzQgbCA2LjdlLTQsLTkuODIyNjQyNiBjIC0yLjQ4NDA0NiwtMS4zNTUwMDYgLTIuNDM1MjM0LC0yLjAzMTIyNTQgLTMuNTAwMSwtMy4zMDk3MDcgLTAuMDQzLC0wLjAxNTg4MiAwLjA0NiwwLjAwMTc0IDAsMCBMIDIuNDMwNjcsMS4wMDExMDggQyAyLjQwMzgzLDAuOTk4NTkgMi4zNzY3NCwwLjk5ODU5IDIuMzQ5OSwxLjAwMTEwOCBaIgogICAgIGlkPSJwYXRoMjc1MiIgLz4KICA8cGF0aAogICAgIHN0eWxlPSJkaXNwbGF5OmlubGluZTtmaWxsOiMzYjQwNDU7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOiM5ZTc1NzU7c3Ryb2tlLXdpZHRoOjA7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICBkPSJtIDEwLjUwMDU3LDEuMDAyMDc2NCBjIDAsMy4yNzY4MDI4IC0wLjAwNTIsMy4xNzM5MTYxIDAuMzYyOTIxLDMuMjY5ODIwMiAwLjI4MDEwOSwwLjA3Mjk4NCAzLjEzNzE4LDAuMDM5ODg3IDMuMTM3MTgsMC4wMzk4ODcgLTEuMTIwMDY3LC0xLjA1NTY2OTIgLTIuMzMzNCwtMi4yMDY0NzEzIC0zLjUwMDEsLTMuMzA5NzA3NCB6IgogICAgIGlkPSJwYXRoMjc1NCIgLz4KPC9zdmc+Cg=="); load$.error(err);
$img.classList.remove("hidden"); ctrlDownloader(createRender(qs($page, ".images_wrapper")), { acl$, getFilename, getDownloadUrl, hasMenubar: false });
$img.classList.add("error"); return rxjs.EMPTY;
$img.parentElement.appendChild(createElement(`
<div class="error no-select">
${t("Not Supported")}
</div>
`));
}), }),
rxjs.catchError(ctrlError()),
); );
} }
return ctrlError()(err); return ctrlError()(err);
}), }),
)); ));
componentPager(createRender(qs($page, ".component_pager"))); componentPager(createRender(qs($page, ".component_pager")));
} }

View file

@ -28,13 +28,12 @@ function componentHeader(render, { toggle }) {
</div> </div>
`); `);
render($header); render($header);
effect(onClick(qs($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$ }) {
const $page = createElement(` const $page = createElement(`
<div class="content"> <div>
<div class="content_box"> <div class="content_box">
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDggNTEyIj4KICA8cGF0aCBzdHlsZT0iZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eTowLjIiIGQ9Ik0gNDAwLDY0IEggMzUyIFYgMTIgQyAzNTIsNS40IDM0Ni42LDAgMzQwLDAgaCAtNDAgYyAtNi42LDAgLTEyLDUuNCAtMTIsMTIgViA2NCBIIDE2MCBWIDEyIEMgMTYwLDUuNCAxNTQuNiwwIDE0OCwwIEggMTA4IEMgMTAxLjQsMCA5Niw1LjQgOTYsMTIgViA2NCBIIDQ4IEMgMjEuNSw2NCAwLDg1LjUgMCwxMTIgdiAzNTIgYyAwLDI2LjUgMjEuNSw0OCA0OCw0OCBoIDM1MiBjIDI2LjUsMCA0OCwtMjEuNSA0OCwtNDggViAxMTIgQyA0NDgsODUuNSA0MjYuNSw2NCA0MDAsNjQgWiBtIC0yLDQwNCBIIDUwIGMgLTMuMywwIC02LjAyMjE0NywtMi43MDAwNyAtNiwtNiBWIDE1NCBoIDM2MCB2IDMwOCBjIDAsMy4zIC0yLjcsNiAtNiw2IHoiIC8+Cjwvc3ZnPgo=" alt="schedule"> <img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDggNTEyIj4KICA8cGF0aCBzdHlsZT0iZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eTowLjIiIGQ9Ik0gNDAwLDY0IEggMzUyIFYgMTIgQyAzNTIsNS40IDM0Ni42LDAgMzQwLDAgaCAtNDAgYyAtNi42LDAgLTEyLDUuNCAtMTIsMTIgViA2NCBIIDE2MCBWIDEyIEMgMTYwLDUuNCAxNTQuNiwwIDE0OCwwIEggMTA4IEMgMTAxLjQsMCA5Niw1LjQgOTYsMTIgViA2NCBIIDQ4IEMgMjEuNSw2NCAwLDg1LjUgMCwxMTIgdiAzNTIgYyAwLDI2LjUgMjEuNSw0OCA0OCw0OCBoIDM1MiBjIDI2LjUsMCA0OCwtMjEuNSA0OCwtNDggViAxMTIgQyA0NDgsODUuNSA0MjYuNSw2NCA0MDAsNjQgWiBtIC0yLDQwNCBIIDUwIGMgLTMuMywwIC02LjAyMjE0NywtMi43MDAwNyAtNiwtNiBWIDE1NCBoIDM2MCB2IDMwOCBjIDAsMy4zIC0yLjcsNiAtNiw2IHoiIC8+Cjwvc3ZnPgo=" alt="schedule">
<div class="headline ellipsis" data-bind="date">-</div> <div class="headline ellipsis" data-bind="date">-</div>
@ -63,6 +62,9 @@ function componentBody(render, { load$ }) {
if (metadata.location) await componentMap(createRender(qs($page, `[data-bind="map"]`)), { metadata }); if (metadata.location) await componentMap(createRender(qs($page, `[data-bind="map"]`)), { metadata });
componentMore(createRender(qs($page, `[data-bind="all"]`)), { metadata }); componentMore(createRender(qs($page, `[data-bind="all"]`)), { metadata });
}), rxjs.catchError((err) => {
qs($page, `[data-bind="all"]`).remove();
return rxjs.EMPTY;
}))); })));
} }
@ -155,13 +157,6 @@ async function componentMap(render, { metadata }) {
} }
function componentMore(render, { metadata }) { function componentMore(render, { metadata }) {
const $page = createElement(`
<div class="more">
<div class="more_container"></div>
</div>
`);
render($page);
const $all = document.createDocumentFragment(); const $all = document.createDocumentFragment();
const formatKey = (str) => str.replace(/([A-Z][a-z])/g, " $1"); const formatKey = (str) => str.replace(/([A-Z][a-z])/g, " $1");
const formatValue = (str) => { const formatValue = (str) => {
@ -209,7 +204,7 @@ function componentMore(render, { metadata }) {
`)); `));
} }
}); });
qs($page, ".more_container").appendChild($all); render($all);
} }
export function init() { export function init() {
@ -222,6 +217,7 @@ export function init() {
const extractExif = ($img) => new Promise((resolve) => window.EXIF.getData($img, function() { const extractExif = ($img) => new Promise((resolve) => window.EXIF.getData($img, function() {
const metadata = window.EXIF.getAllTags($img); 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)); const digits = str.split(/[ :]/).map((digit) => parseInt(digit));
return new Date( return new Date(
digits[0] || 0, digits[0] || 0,

View file

@ -7,10 +7,10 @@ import { createLoader } from "../../components/loader.js";
import ctrlError from "../ctrl_error.js"; import ctrlError from "../ctrl_error.js";
import componentDownloader, { init as initDownloader } from "./application_downloader.js"; import componentDownloader, { init as initDownloader } from "./application_downloader.js";
export default function(render, { mime, getFilename, getDownloadUrl, acl$ }) { export default function(render, { mime, getFilename, getDownloadUrl, acl$, hasMenubar = true }) {
const $page = createElement(` const $page = createElement(`
<div class="component_skeletonviewer"> <div class="component_skeletonviewer">
<component-menubar filename="${getFilename()}"></component-menubar> <component-menubar filename="${getFilename()}" class="${!hasMenubar && "hidden"}"></component-menubar>
<div class="component_skeleton_container flex"></div> <div class="component_skeleton_container flex"></div>
</div> </div>
`); `);

View file

@ -12,10 +12,7 @@ export function opener(file = "", mimes) {
} }
const p = getPlugin(mime); const p = getPlugin(mime);
if (p) return [ if (p) return [p[0], { mime, ...p[1] }];
p[0],
{ mime, loader: p[1] },
];
if (type === "text") { if (type === "text") {
return ["editor", { mime }]; return ["editor", { mime }];

View file

@ -4,6 +4,7 @@ import (
"io" "io"
"net/http" "net/http"
"os" "os"
"path/filepath"
"strings" "strings"
. "github.com/mickael-kerjean/filestash/server/common" . "github.com/mickael-kerjean/filestash/server/common"
@ -32,7 +33,7 @@ func PluginExportHandler(ctx *App, res http.ResponseWriter, req *http.Request) {
} }
plgExports[module["mime"]] = []string{ plgExports[module["mime"]] = []string{
module["application"], module["application"],
WithBase(JoinPath("/plugin/", name+index)), WithBase(JoinPath("/plugin/", filepath.Join(name, index))),
} }
} }
} }