diff --git a/README.md b/README.md
index bc864f80..4bd43713 100644
--- a/README.md
+++ b/README.md
@@ -249,13 +249,14 @@ the browser has the following hotkeys (assumes qwerty, ignores actual layout)
* `J/L, Left/Right` prev/next file
* `Home/End` first/last file
* `S` toggle selection
+ * `R` rotate clockwise (shift=ccw)
* `Esc` close viewer
* videos:
* `U/O` skip 10sec back/forward
* `P/K/Space` play/pause
* `F` fullscreen
* `C` continue playing next video
- * `R` loop
+ * `V` loop
* `M` mute
* when the navpane is open:
* `A/D` adjust tree width
diff --git a/copyparty/web/baguettebox.js b/copyparty/web/baguettebox.js
index 485c29b0..1bbbf5b5 100644
--- a/copyparty/web/baguettebox.js
+++ b/copyparty/web/baguettebox.js
@@ -22,7 +22,7 @@ window.baguetteBox = (function () {
afterHide: null,
onChange: null,
},
- overlay, slider, btnPrev, btnNext, btnHelp, btnSel, btnVmode, btnClose,
+ overlay, slider, btnPrev, btnNext, btnHelp, btnRotL, btnRotR, btnSel, btnVmode, btnClose,
currentGallery = [],
currentIndex = 0,
isOverlayVisible = false,
@@ -175,6 +175,8 @@ window.baguetteBox = (function () {
'' +
'
' +
'' +
+ '' +
+ '' +
'' +
'' +
'' +
@@ -188,6 +190,8 @@ window.baguetteBox = (function () {
btnPrev = ebi('bbox-prev');
btnNext = ebi('bbox-next');
btnHelp = ebi('bbox-help');
+ btnRotL = ebi('bbox-rotl');
+ btnRotR = ebi('bbox-rotr');
btnSel = ebi('bbox-tsel');
btnVmode = ebi('bbox-vmode');
btnClose = ebi('bbox-close');
@@ -205,12 +209,13 @@ window.baguetteBox = (function () {
['right, L', 'next file'],
['home', 'first file'],
['end', 'last file'],
+ ['R', 'rotate (shift=ccw)'],
['S', 'toggle file selection'],
['space, P, K', 'video: play / pause'],
['U', 'video: seek 10sec back'],
['P', 'video: seek 10sec ahead'],
['M', 'video: toggle mute'],
- ['R', 'video: toggle loop'],
+ ['V', 'video: toggle loop'],
['C', 'video: toggle auto-next'],
['F', 'video: toggle fullscreen'],
],
@@ -252,7 +257,7 @@ window.baguetteBox = (function () {
v.muted = vmute = !vmute;
mp_ctl();
}
- else if (k == "KeyR" && v) {
+ else if (k == "KeyV" && v) {
vloop = !vloop;
vnext = vnext && !vloop;
setVmode();
@@ -272,6 +277,8 @@ window.baguetteBox = (function () {
catch (ex) { }
else if (k == "KeyS")
tglsel();
+ else if (k == "KeyR")
+ rotn(e.shiftKey ? -1 : 1);
}
function setVmode() {
@@ -284,7 +291,7 @@ window.baguetteBox = (function () {
if (vloop) {
lbl = 'Loop';
msg += 'repeat it';
- tts = '$NHotkey: R';
+ tts = '$NHotkey: V';
}
else if (vnext) {
lbl = 'Cont';
@@ -333,7 +340,7 @@ window.baguetteBox = (function () {
}
function selbg() {
- var img = imagesElements[currentIndex].querySelector('img, video'),
+ var img = vidimg(),
thumb = currentGallery[currentIndex].imageElement,
name = vsplit(thumb.href)[1],
files = msel.getsel(),
@@ -387,6 +394,8 @@ window.baguetteBox = (function () {
bind(btnClose, 'click', hideOverlay);
bind(btnVmode, 'click', tglVmode);
bind(btnHelp, 'click', halp);
+ bind(btnRotL, 'click', rotl);
+ bind(btnRotR, 'click', rotr);
bind(btnSel, 'click', tglsel);
bind(slider, 'contextmenu', contextmenuHandler);
bind(overlay, 'touchstart', touchstartHandler, nonPassiveEvent);
@@ -402,6 +411,8 @@ window.baguetteBox = (function () {
unbind(btnClose, 'click', hideOverlay);
unbind(btnVmode, 'click', tglVmode);
unbind(btnHelp, 'click', halp);
+ unbind(btnRotL, 'click', rotl);
+ unbind(btnRotR, 'click', rotr);
unbind(btnSel, 'click', tglsel);
unbind(slider, 'contextmenu', contextmenuHandler);
unbind(overlay, 'touchstart', touchstartHandler, nonPassiveEvent);
@@ -658,10 +669,56 @@ window.baguetteBox = (function () {
return true;
}
+ function rotn(n) {
+ var el = vidimg(),
+ orot = parseInt(el.getAttribute('rot') || 0),
+ frot = orot + n * 90,
+ rot = frot,
+ iw = el.naturalWidth || el.videoWidth,
+ ih = el.naturalHeight || el.videoHeight,
+ magic = 4, // idk, works in enough browsers
+ co = ebi('bbox-overlay'),
+ dl = el.closest('div').querySelector('figcaption a'),
+ vw = co.clientWidth,
+ vh = co.clientHeight - dl.offsetHeight + magic,
+ pmag = Math.min(1, Math.min(vw / ih, vh / iw)),
+ wmag = Math.min(1, Math.min(vw / iw, vh / ih));
+
+ while (rot < 0) rot += 360;
+ while (rot >= 360) rot -= 360;
+ var q = rot == 90 || rot == 270 ? 1 : 0,
+ mag = q ? pmag : wmag;
+
+ el.style.cssText = 'max-width:none; max-height:none; position:absolute; display:block; margin:0';
+ if (!orot) {
+ el.style.width = iw * wmag + 'px';
+ el.style.height = ih * wmag + 'px';
+ el.style.left = (vw - iw * wmag) / 2 + 'px';
+ el.style.top = (vh - ih * wmag) / 2 - magic + 'px';
+ q = el.offsetHeight;
+ }
+ el.style.width = iw * mag + 'px';
+ el.style.height = ih * mag + 'px';
+ el.style.left = (vw - iw * mag) / 2 + 'px';
+ el.style.top = (vh - ih * mag) / 2 - magic + 'px';
+ el.style.transform = 'rotate(' + frot + 'deg)';
+ el.setAttribute('rot', frot);
+ }
+ function rotl() {
+ rotn(-1);
+ }
+ function rotr() {
+ rotn(1);
+ }
+
function vid() {
return imagesElements[currentIndex].querySelector('video');
}
+ function vidimg() {
+ return imagesElements[currentIndex].querySelector('img, video');
+ }
+
function playvid(play) {
if (vid())
vid()[play ? 'play' : 'pause']();
diff --git a/copyparty/web/browser.css b/copyparty/web/browser.css
index e29b28a5..4b2e0250 100644
--- a/copyparty/web/browser.css
+++ b/copyparty/web/browser.css
@@ -1329,6 +1329,7 @@ html.light #tree::-webkit-scrollbar {
margin-bottom: 1.4em;
vertical-align: middle;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
+ transition: transform .2s, left .2s, top .2s, width .2s, height .2s;
}
.full-image video {
background: #333;
@@ -1341,6 +1342,7 @@ html.light #tree::-webkit-scrollbar {
text-align: center;
white-space: normal;
color: #ccc;
+ z-index: 1;
}
#bbox-overlay figcaption a {
background: rgba(0, 0, 0, 0.6);