mirror of
https://github.com/9001/copyparty.git
synced 2026-06-20 05:02:28 -06:00
1365 lines
41 KiB
JavaScript
1365 lines
41 KiB
JavaScript
"use strict";
|
||
|
||
/*!
|
||
* baguetteBox.js
|
||
* @author feimosi
|
||
* @version 1.11.1-mod
|
||
* @url https://github.com/feimosi/baguetteBox.js
|
||
*/
|
||
|
||
var J_BBX = 1;
|
||
|
||
window.baguetteBox = (function () {
|
||
'use strict';
|
||
|
||
var options = {},
|
||
defaults = {
|
||
captions: true,
|
||
buttons: 'auto',
|
||
noScrollbars: false,
|
||
bodyClass: 'bbox-open',
|
||
titleTag: false,
|
||
async: false,
|
||
preload: 2,
|
||
refocus: true,
|
||
afterShow: null,
|
||
afterHide: null,
|
||
duringHide: null,
|
||
onChange: null,
|
||
readDirRtl: false,
|
||
},
|
||
overlay, slider, btnPrev, btnNext, btnHelp, btnAnim, btnRotL, btnRotR, btnSel, btnFull, btnZoom, btnVmode, btnReadDir, btnClose,
|
||
currentGallery = [],
|
||
currentIndex = 0,
|
||
isOverlayVisible = false,
|
||
touch = {}, // start-pos
|
||
touchFlag = false, // busy
|
||
scrollCSS = ['', ''],
|
||
scrollTimer = 0,
|
||
re_i = APPLE ?
|
||
/^[^?]+\.(a?png|avif|bmp|gif|hei[cf]s?|jfif|jpe?g|jxl|svg|tiff?|webp)(\?|$)/i :
|
||
/^[^?]+\.(a?png|avif|bmp|gif|jfif|jpe?g|jxl|svg|tiff?|webp)(\?|$)/i,
|
||
re_v = /^[^?]+\.(webm|mkv|mp4|m4v|mov)(\?|$)/i,
|
||
re_cbz = /^[^?]+\.(cbz)(\?|$)/i,
|
||
anims = ['slideIn', 'fadeIn', 'none'],
|
||
data = {}, // all galleries
|
||
imagesElements = [],
|
||
documentLastFocus = null,
|
||
isFullscreen = false,
|
||
vmute = false,
|
||
vloop = sread('vmode') == 'L',
|
||
vnext = sread('vmode') == 'C',
|
||
loopA = null,
|
||
loopB = null,
|
||
url_ts = null,
|
||
un_pp = 0,
|
||
resume_mp = false;
|
||
|
||
var onFSC = function (e) {
|
||
isFullscreen = !!document.fullscreenElement;
|
||
clmod(document.documentElement, 'bb_fsc', isFullscreen);
|
||
};
|
||
|
||
var overlayClickHandler = function (e) {
|
||
if (e.target.id.indexOf('baguette-img') !== -1)
|
||
hideOverlay();
|
||
};
|
||
|
||
var vtouch = function (e) {
|
||
var v = vid(),
|
||
bv = v.getBoundingClientRect(),
|
||
tp = e.changedTouches[0];
|
||
|
||
if (bv.bottom - tp.clientY < 90)
|
||
touchFlag = true;
|
||
};
|
||
|
||
var touchstartHandler = function (e) {
|
||
touch.count = e.touches.length;
|
||
if (touch.count > 1)
|
||
touch.multitouch = true;
|
||
|
||
touch.startX = e.changedTouches[0].pageX;
|
||
touch.startY = e.changedTouches[0].pageY;
|
||
};
|
||
var touchmoveHandler = function (e) {
|
||
if (touchFlag || touch.multitouch)
|
||
return;
|
||
|
||
e.preventDefault ? e.preventDefault() : e.returnValue = false;
|
||
var touchEvent = e.touches[0] || e.changedTouches[0];
|
||
if (touchEvent.pageX - touch.startX > 40) {
|
||
touchFlag = true;
|
||
showLeftImage();
|
||
} else if (touchEvent.pageX - touch.startX < -40) {
|
||
touchFlag = true;
|
||
showRightImage();
|
||
} else if (touch.startY - touchEvent.pageY > 100) {
|
||
hideOverlay();
|
||
}
|
||
};
|
||
var touchendHandler = function (e) {
|
||
touch.count--;
|
||
if (e && e.touches)
|
||
touch.count = e.touches.length;
|
||
|
||
if (touch.count <= 0)
|
||
touch.multitouch = false;
|
||
|
||
touchFlag = false;
|
||
};
|
||
var contextmenuHandler = function () {
|
||
touchendHandler();
|
||
};
|
||
|
||
var overlayWheelHandler = function (e) {
|
||
if (!options.noScrollbars || anymod(e))
|
||
return;
|
||
|
||
ev(e);
|
||
|
||
var x = e.deltaX,
|
||
y = e.deltaY,
|
||
d = Math.abs(x) > Math.abs(y) ? x : y;
|
||
|
||
if (e.deltaMode)
|
||
d *= 10;
|
||
|
||
if (Date.now() - scrollTimer < (Math.abs(d) > 20 ? 100 : 300))
|
||
return;
|
||
|
||
scrollTimer = Date.now();
|
||
|
||
if (d > 0)
|
||
showNextImageIgnoreReadDir();
|
||
else
|
||
showPreviousImageIgnoreReadDir();
|
||
};
|
||
|
||
var trapFocusInsideOverlay = function (e) {
|
||
if (overlay.style.display === 'block' && (overlay.contains && !overlay.contains(e.target))) {
|
||
e.stopPropagation();
|
||
btnClose.focus();
|
||
}
|
||
};
|
||
|
||
function run(selector, userOptions) {
|
||
buildOverlay();
|
||
removeFromCache(selector);
|
||
return bindImageClickListeners(selector, userOptions);
|
||
}
|
||
|
||
function bindImageClickListeners(selector, userOptions) {
|
||
var galleryNodeList = QSA(selector);
|
||
var selectorData = {
|
||
galleries: [],
|
||
nodeList: galleryNodeList
|
||
};
|
||
data[selector] = selectorData;
|
||
|
||
[].forEach.call(galleryNodeList, function (galleryElement) {
|
||
var tagsNodeList = [];
|
||
if (galleryElement.tagName === 'A')
|
||
tagsNodeList = [galleryElement];
|
||
else
|
||
tagsNodeList = galleryElement.getElementsByTagName('a');
|
||
if (have_zls)
|
||
bindCbzClickListeners(tagsNodeList, userOptions);
|
||
|
||
tagsNodeList = [].filter.call(tagsNodeList, function (element) {
|
||
if (element.className.indexOf(userOptions && userOptions.ignoreClass) === -1)
|
||
return re_i.test(element.href) || re_v.test(element.href);
|
||
});
|
||
if (!tagsNodeList.length)
|
||
return;
|
||
|
||
var gallery = [];
|
||
[].forEach.call(tagsNodeList, function (imageElement, imageIndex) {
|
||
var imageElementClickHandler = function (e) {
|
||
if (ctrl(e) || e && e.shiftKey)
|
||
return true;
|
||
|
||
e.preventDefault ? e.preventDefault() : e.returnValue = false;
|
||
prepareOverlay(gallery, userOptions);
|
||
showOverlay(imageIndex);
|
||
};
|
||
var imageItem = {
|
||
eventHandler: imageElementClickHandler,
|
||
imageElement: imageElement,
|
||
};
|
||
bind(imageElement, 'click', imageElementClickHandler);
|
||
gallery.push(imageItem);
|
||
});
|
||
selectorData.galleries.push(gallery);
|
||
});
|
||
|
||
return [selectorData.galleries, options];
|
||
}
|
||
|
||
function bindCbzClickListeners(tagsNodeList, userOptions) {
|
||
var cbzNodes = [].filter.call(tagsNodeList, function (element) {
|
||
return re_cbz.test(element.href);
|
||
});
|
||
if (!tagsNodeList.length) {
|
||
return;
|
||
}
|
||
|
||
[].forEach.call(cbzNodes, function (cbzElement, index) {
|
||
var gallery = [];
|
||
var eventHandler = function (e) {
|
||
if (ctrl(e) || e && e.shiftKey)
|
||
return true;
|
||
|
||
e.preventDefault ? e.preventDefault() : e.returnValue = false;
|
||
fillCbzGallery(gallery, cbzElement, eventHandler).then(function () {
|
||
prepareOverlay(gallery, userOptions);
|
||
showOverlay(0);
|
||
}
|
||
).catch(function (reason) {
|
||
console.error("cbz-ded", reason);
|
||
var t;
|
||
try {
|
||
t = uricom_dec(cbzElement.href.split('/').pop());
|
||
} catch (ex) { }
|
||
|
||
var msg = "Could not browse " + (t ? t : 'archive');
|
||
try {
|
||
msg += "\n\n" + reason.message;
|
||
} catch (ex) { }
|
||
toast.err(20, msg, 'cbz-ded');
|
||
});
|
||
}
|
||
|
||
bind(cbzElement, "click", eventHandler);
|
||
})
|
||
}
|
||
|
||
function fillCbzGallery(gallery, cbzElement, eventHandler) {
|
||
if (gallery.length !== 0) {
|
||
return Promise.resolve();
|
||
}
|
||
var href = cbzElement.href;
|
||
var zlsHref = href + (href.indexOf("?") === -1 ? "?" : "&") + "zls";
|
||
return fetch(zlsHref)
|
||
.then(function (response) {
|
||
if (response.ok) {
|
||
return response.json();
|
||
} else {
|
||
throw new Error("Archive is invalid");
|
||
}
|
||
})
|
||
.then(function (fileList) {
|
||
var imagesList = fileList.map(function (file) {
|
||
return file["fn"];
|
||
}).filter(function (file) {
|
||
return re_i.test(file);
|
||
}).sort();
|
||
|
||
if (imagesList.length === 0) {
|
||
throw new Error("Archive does not contain any images");
|
||
}
|
||
|
||
imagesList.forEach(function (imageName, index) {
|
||
var imageHref = href
|
||
+ (href.indexOf("?") === -1 ? "?" : "&")
|
||
+ "zget="
|
||
+ encodeURIComponent(imageName);
|
||
|
||
var galleryItem = {
|
||
href: imageHref,
|
||
imageElement: cbzElement,
|
||
eventHandler: eventHandler,
|
||
};
|
||
gallery.push(galleryItem);
|
||
});
|
||
});
|
||
}
|
||
|
||
function clearCachedData() {
|
||
for (var selector in data)
|
||
if (data.hasOwnProperty(selector))
|
||
removeFromCache(selector);
|
||
}
|
||
|
||
function removeFromCache(selector) {
|
||
if (!data.hasOwnProperty(selector))
|
||
return;
|
||
|
||
var galleries = data[selector].galleries;
|
||
[].forEach.call(galleries, function (gallery) {
|
||
[].forEach.call(gallery, function (imageItem) {
|
||
unbind(imageItem.imageElement, 'click', imageItem.eventHandler);
|
||
});
|
||
|
||
if (currentGallery === gallery)
|
||
currentGallery = [];
|
||
});
|
||
|
||
delete data[selector];
|
||
}
|
||
|
||
function buildOverlay() {
|
||
overlay = ebi('bbox-overlay');
|
||
if (!overlay) {
|
||
var ctr = mknod('div');
|
||
ctr.innerHTML = (
|
||
'<div id="bbox-overlay" role="dialog">' +
|
||
'<div id="bbox-slider"></div>' +
|
||
'<div id="bbox-prev"><a class="btn" aria-label="Previous">◀</a></div>' +
|
||
'<div id="bbox-next"><a class="btn" aria-label="Next">▶</a></div>' +
|
||
'<div id="bbox-btns">' +
|
||
'<a id="bbox-close" class="btn" aria-label="Close">✕</a>' +
|
||
'<a id="bbox-vmode" class="btn" tt="a"></a>' +
|
||
'<a id="bbzoom" class="tgl btn" tt="zoom/stretch smaller images to fill screen">↕</a>' +
|
||
'<a id="bbox-full" class="btn" tt="full-screen">⛶</a>' +
|
||
'<a id="bbox-tsel" class="tgl btn">☑️sel</a>' +
|
||
'<a id="bbox-rotr" class="btn">↷</a>' +
|
||
'<a id="bbox-rotl" class="btn">↶</a>' +
|
||
'<a id="bbox-readdir" class="btn" tt="a">ltr</a>' +
|
||
'<a id="bbox-anim" class="btn" tt="a">-</a>' +
|
||
'<a id="bbox-help" class="btn">?</a>' +
|
||
'</div></div>'
|
||
);
|
||
overlay = ctr.firstChild;
|
||
QS('body').appendChild(overlay);
|
||
tt.att(overlay);
|
||
}
|
||
slider = ebi('bbox-slider');
|
||
btnPrev = ebi('bbox-prev');
|
||
btnNext = ebi('bbox-next');
|
||
btnHelp = ebi('bbox-help');
|
||
btnAnim = ebi('bbox-anim');
|
||
btnReadDir = ebi('bbox-readdir');
|
||
btnRotL = ebi('bbox-rotl');
|
||
btnRotR = ebi('bbox-rotr');
|
||
btnSel = ebi('bbox-tsel');
|
||
btnFull = ebi('bbox-full');
|
||
btnZoom = ebi('bbzoom');
|
||
btnVmode = ebi('bbox-vmode');
|
||
btnClose = ebi('bbox-close');
|
||
|
||
bcfg_bind(options, 'bbzoom', 'bbzoom', false, setzoom);
|
||
setzoom();
|
||
}
|
||
|
||
function halp() {
|
||
if (ebi('bbox-halp'))
|
||
return;
|
||
|
||
var list = [
|
||
['<b># hotkey</b>', '<b># operation</b>'],
|
||
['escape', 'close'],
|
||
['left, J', 'previous file'],
|
||
['right, L', 'next file'],
|
||
['home', 'first file'],
|
||
['end', 'last file'],
|
||
['R', 'rotate (shift=ccw)'],
|
||
['F', 'toggle fullscreen'],
|
||
['Z', 'toggle zoom/stretch'],
|
||
['S', 'toggle file selection'],
|
||
['space, P, K', 'video: play / pause'],
|
||
['U', 'video: seek 10sec back'],
|
||
['O', 'video: seek 10sec ahead'],
|
||
['0..9', 'video: seek 0%..90%'],
|
||
['M', 'video: toggle mute'],
|
||
['V', 'video: toggle loop'],
|
||
['C', 'video: toggle auto-next'],
|
||
['<code>[</code>, <code>]</code>', 'video: loop start / end'],
|
||
],
|
||
d = mknod('table', 'bbox-halp'),
|
||
html = ['<tbody>'];
|
||
|
||
for (var a = 0; a < list.length; a++)
|
||
html.push('<tr><td>' + list[a][0] + '</td><td>' + list[a][1] + '</td></tr>');
|
||
|
||
html.push('<tr><td colspan="2">tap middle of img to hide btns</td></tr>');
|
||
html.push('<tr><td colspan="2">tap left/right sides for prev/next</td></tr>');
|
||
d.innerHTML = html.join('\n') + '</tbody>';
|
||
d.onclick = function () {
|
||
overlay.removeChild(d);
|
||
};
|
||
overlay.appendChild(d);
|
||
}
|
||
|
||
function keyDownHandler(e) {
|
||
if (modal.busy)
|
||
return;
|
||
|
||
if (anymod(e, true))
|
||
return;
|
||
|
||
var k = (e.key || e.code) + '', v = vid();
|
||
|
||
if (k.startsWith('Key'))
|
||
k = k.slice(3);
|
||
else if (k.startsWith('Digit'))
|
||
k = k.slice(5);
|
||
|
||
var kl = k.toLowerCase();
|
||
|
||
if (k == '?')
|
||
return halp();
|
||
|
||
if (k == "[" || k == "BracketLeft")
|
||
setloop(1);
|
||
else if (k == "]" || k == "BracketRight")
|
||
setloop(2);
|
||
else if (e.shiftKey && kl != "r")
|
||
return;
|
||
else if (k == "ArrowLeft" || k == "Left" || kl == "j")
|
||
showLeftImage();
|
||
else if (k == "ArrowRight" || k == "Right" || kl == "l")
|
||
showRightImage();
|
||
else if (k == "Escape" || k == "Esc")
|
||
hideOverlay();
|
||
else if (k == "Home")
|
||
showFirstImage(e);
|
||
else if (k == "End")
|
||
showLastImage(e);
|
||
else if (k == "Space" || k == "Spacebar" || kl == " " || kl == "p" || kl == "k")
|
||
playpause();
|
||
else if (kl == "u" || kl == "o")
|
||
relseek(kl == "u" ? -10 : 10);
|
||
else if (v && /^[0-9]$/.test(k) && isNum(v.duration))
|
||
v.currentTime = v.duration * parseInt(k) * 0.1;
|
||
else if (kl == "m" && v) {
|
||
v.muted = vmute = !vmute;
|
||
mp_ctl();
|
||
}
|
||
else if (kl == "v" && v) {
|
||
vloop = !vloop;
|
||
vnext = vnext && !vloop;
|
||
setVmode();
|
||
}
|
||
else if (kl == "c" && v) {
|
||
vnext = !vnext;
|
||
vloop = vloop && !vnext;
|
||
setVmode();
|
||
}
|
||
else if (kl == "f")
|
||
tglfull();
|
||
else if (kl == "z")
|
||
btnZoom.click();
|
||
else if (kl == "s")
|
||
tglsel();
|
||
else if (kl == "r")
|
||
rotn(e.shiftKey ? -1 : 1);
|
||
else if (kl == "y")
|
||
dlpic();
|
||
else
|
||
return;
|
||
return ev(e);
|
||
}
|
||
|
||
function anim() {
|
||
var i = (anims.indexOf(options.animation) + 1) % anims.length,
|
||
o = options;
|
||
swrite('ganim', anims[i]);
|
||
options = {};
|
||
setOptions(o);
|
||
if (tt.en)
|
||
tt.show.call(this);
|
||
}
|
||
|
||
function toggleReadDir() {
|
||
var o = options,
|
||
next = options.readDirRtl ? "ltr" : "rtl";
|
||
swrite('greaddir', next);
|
||
slider.className = "no-transition";
|
||
options = {};
|
||
setOptions(o);
|
||
updateOffset(true);
|
||
window.getComputedStyle(slider).opacity; // force a restyle
|
||
slider.className = "";
|
||
|
||
if (tt.en)
|
||
tt.show.call(this);
|
||
}
|
||
|
||
function setVmode() {
|
||
var v = vid();
|
||
ebi('bbox-vmode').style.display = v ? '' : 'none';
|
||
if (!v)
|
||
return;
|
||
|
||
var msg = 'When video ends, ', tts = '', lbl;
|
||
if (vloop) {
|
||
lbl = 'Loop';
|
||
msg += 'repeat it';
|
||
tts = '$NHotkey: V';
|
||
}
|
||
else if (vnext) {
|
||
lbl = 'Cont';
|
||
msg += 'continue to next';
|
||
tts = '$NHotkey: C';
|
||
}
|
||
else {
|
||
lbl = 'Stop';
|
||
msg += 'just stop'
|
||
}
|
||
btnVmode.setAttribute('aria-label', msg);
|
||
btnVmode.setAttribute('tt', msg + tts);
|
||
btnVmode.textContent = lbl;
|
||
swrite('vmode', lbl[0]);
|
||
|
||
v.loop = vloop
|
||
if (vloop && v.paused)
|
||
v.play();
|
||
}
|
||
|
||
function tglVmode() {
|
||
if (vloop) {
|
||
vnext = true;
|
||
vloop = false;
|
||
}
|
||
else if (vnext)
|
||
vnext = false;
|
||
else
|
||
vloop = true;
|
||
|
||
setVmode();
|
||
if (tt.en)
|
||
tt.show.call(this);
|
||
}
|
||
|
||
function findfile() {
|
||
var thumb = currentGallery[currentIndex].imageElement,
|
||
name = vsplit(thumb.href)[1].split('?')[0],
|
||
files = msel.getall();
|
||
|
||
for (var a = 0; a < files.length; a++)
|
||
if (vsplit(files[a].vp)[1] == name)
|
||
return [name, a, files, ebi(files[a].id)];
|
||
}
|
||
|
||
function tglfull() {
|
||
try {
|
||
if (isFullscreen)
|
||
document.exitFullscreen();
|
||
else
|
||
ebi('bbox-overlay').requestFullscreen();
|
||
}
|
||
catch (ex) {
|
||
if (IPHONE)
|
||
alert('sorry, apple decided to make this impossible on iphones (should work on ipad tho)');
|
||
else
|
||
alert(ex);
|
||
}
|
||
}
|
||
|
||
function setzoom() {
|
||
var sel = clgot(btnZoom, 'on')
|
||
clmod(ebi('bbox-overlay'), 'fill', sel);
|
||
btnState(btnZoom, sel);
|
||
}
|
||
|
||
function tglsel() {
|
||
var o = findfile()[3];
|
||
clmod(o.closest('tr'), 'sel', 't');
|
||
msel.selui();
|
||
selbg();
|
||
}
|
||
|
||
function dlpic() {
|
||
var url = addq(findfile()[3].href, 'cache');
|
||
dl_file(url);
|
||
}
|
||
|
||
function selbg() {
|
||
var img = vidimg(),
|
||
thumb = currentGallery[currentIndex].imageElement,
|
||
name = vsplit(thumb.href)[1].split('?')[0],
|
||
files = msel.getsel(),
|
||
sel = false;
|
||
|
||
for (var a = 0; a < files.length; a++)
|
||
if (vsplit(files[a].vp)[1] == name)
|
||
sel = true;
|
||
|
||
clmod(ebi('bbox-overlay'), 'sel', sel);
|
||
|
||
img.style.borderRadius = sel ? '1em' : '';
|
||
btnState(btnSel, sel);
|
||
}
|
||
|
||
function btnState(btn, sel) {
|
||
clmod(btn, 'on', sel);
|
||
}
|
||
|
||
function keyUpHandler(e) {
|
||
if (anymod(e))
|
||
return;
|
||
|
||
var k = (e.key || e.code) + '';
|
||
|
||
if (k == "Space" || k == "Spacebar" || k == " ") {
|
||
un_pp = Date.now();
|
||
return ev(e);
|
||
}
|
||
}
|
||
|
||
var passiveSupp = false;
|
||
try {
|
||
var opts = {
|
||
get passive() {
|
||
passiveSupp = true;
|
||
return false;
|
||
}
|
||
};
|
||
window.addEventListener('test', null, opts);
|
||
window.removeEventListener('test', null, opts);
|
||
}
|
||
catch (ex) {
|
||
passiveSupp = false;
|
||
}
|
||
var passiveEvent = passiveSupp ? { passive: false } : null;
|
||
var nonPassiveEvent = passiveSupp ? { passive: true } : null;
|
||
|
||
function bindEvents() {
|
||
bind(document, 'keydown', keyDownHandler);
|
||
bind(document, 'keyup', keyUpHandler);
|
||
bind(document, 'fullscreenchange', onFSC);
|
||
bind(overlay, 'click', overlayClickHandler);
|
||
bind(overlay, 'wheel', overlayWheelHandler);
|
||
bind(btnPrev, 'click', showLeftImage);
|
||
bind(btnNext, 'click', showRightImage);
|
||
bind(btnClose, 'click', hideOverlay);
|
||
bind(btnVmode, 'click', tglVmode);
|
||
bind(btnHelp, 'click', halp);
|
||
bind(btnAnim, 'click', anim);
|
||
bind(btnReadDir, 'click', toggleReadDir);
|
||
bind(btnRotL, 'click', rotl);
|
||
bind(btnRotR, 'click', rotr);
|
||
bind(btnSel, 'click', tglsel);
|
||
bind(btnFull, 'click', tglfull);
|
||
bind(slider, 'contextmenu', contextmenuHandler);
|
||
bind(overlay, 'touchstart', touchstartHandler, nonPassiveEvent);
|
||
bind(overlay, 'touchmove', touchmoveHandler, passiveEvent);
|
||
bind(overlay, 'touchend', touchendHandler);
|
||
bind(document, 'focus', trapFocusInsideOverlay, true);
|
||
}
|
||
|
||
function unbindEvents() {
|
||
unbind(document, 'keydown', keyDownHandler);
|
||
unbind(document, 'keyup', keyUpHandler);
|
||
unbind(document, 'fullscreenchange', onFSC);
|
||
unbind(overlay, 'click', overlayClickHandler);
|
||
unbind(overlay, 'wheel', overlayWheelHandler);
|
||
unbind(btnPrev, 'click', showLeftImage);
|
||
unbind(btnNext, 'click', showRightImage);
|
||
unbind(btnClose, 'click', hideOverlay);
|
||
unbind(btnVmode, 'click', tglVmode);
|
||
unbind(btnHelp, 'click', halp);
|
||
unbind(btnAnim, 'click', anim);
|
||
unbind(btnReadDir, 'click', toggleReadDir);
|
||
unbind(btnRotL, 'click', rotl);
|
||
unbind(btnRotR, 'click', rotr);
|
||
unbind(btnSel, 'click', tglsel);
|
||
unbind(btnFull, 'click', tglfull);
|
||
unbind(slider, 'contextmenu', contextmenuHandler);
|
||
unbind(overlay, 'touchstart', touchstartHandler, nonPassiveEvent);
|
||
unbind(overlay, 'touchmove', touchmoveHandler, passiveEvent);
|
||
unbind(overlay, 'touchend', touchendHandler);
|
||
unbind(document, 'focus', trapFocusInsideOverlay, true);
|
||
timer.rm(rotn);
|
||
}
|
||
|
||
function prepareOverlay(gallery, userOptions) {
|
||
if (currentGallery === gallery)
|
||
return;
|
||
|
||
currentGallery = gallery;
|
||
setOptions(userOptions);
|
||
slider.innerHTML = '';
|
||
imagesElements.length = 0;
|
||
|
||
var imagesFiguresIds = [];
|
||
var imagesCaptionsIds = [];
|
||
for (var i = 0, fullImage; i < gallery.length; i++) {
|
||
fullImage = mknod('div', 'baguette-img-' + i);
|
||
fullImage.className = 'full-image';
|
||
imagesElements.push(fullImage);
|
||
|
||
imagesFiguresIds.push('bbox-figure-' + i);
|
||
imagesCaptionsIds.push('bbox-figcaption-' + i);
|
||
slider.appendChild(imagesElements[i]);
|
||
}
|
||
overlay.setAttribute('aria-labelledby', imagesFiguresIds.join(' '));
|
||
overlay.setAttribute('aria-describedby', imagesCaptionsIds.join(' '));
|
||
}
|
||
|
||
function setOptions(newOptions) {
|
||
if (!newOptions)
|
||
newOptions = {};
|
||
|
||
for (var item in defaults) {
|
||
options[item] = defaults[item];
|
||
if (typeof newOptions[item] !== 'undefined')
|
||
options[item] = newOptions[item];
|
||
}
|
||
|
||
var an = options.animation = sread('ganim', anims) || anims[ANIM ? 0 : 2];
|
||
btnAnim.textContent = ['⇄', '⮺', '⚡'][anims.indexOf(an)];
|
||
btnAnim.setAttribute('tt', 'animation: ' + an);
|
||
|
||
options.readDirRtl = sread('greaddir') === "rtl";
|
||
var msg;
|
||
if (options.readDirRtl) {
|
||
btnReadDir.innerText = "rtl";
|
||
msg = "browse from right to left";
|
||
slider.style.display = "flex";
|
||
slider.style.flexDirection = "row-reverse";
|
||
} else {
|
||
btnReadDir.innerText = "ltr";
|
||
msg = "browse from left to right";
|
||
slider.style.flexDirection = "";
|
||
slider.style.display = "block";
|
||
}
|
||
btnReadDir.setAttribute("tt", msg);
|
||
btnReadDir.setAttribute("aria-label", msg);
|
||
|
||
|
||
slider.style.transition = (options.animation === 'fadeIn' ? 'opacity .3s ease' :
|
||
options.animation === 'slideIn' ? '' : 'none');
|
||
|
||
if (options.buttons === 'auto' && ('ontouchstart' in window || currentGallery.length === 1))
|
||
options.buttons = false;
|
||
|
||
QS('#bbox-next .btn').style.display = QS('#bbox-prev .btn').style.display = (options.buttons ? '' : 'none');
|
||
}
|
||
|
||
function showOverlay(chosenImageIndex) {
|
||
if (options.noScrollbars) {
|
||
var a = document.documentElement.style.overflowY,
|
||
b = document.body.style.overflowY;
|
||
|
||
if (a != 'hidden' || b != 'scroll')
|
||
scrollCSS = [a, b];
|
||
|
||
document.documentElement.style.overflowY = 'hidden';
|
||
document.body.style.overflowY = 'scroll';
|
||
}
|
||
if (overlay.style.display === 'block')
|
||
return;
|
||
|
||
bindEvents();
|
||
currentIndex = chosenImageIndex;
|
||
touch = {
|
||
count: 0,
|
||
startX: null,
|
||
startY: null
|
||
};
|
||
loadImage(currentIndex, function () {
|
||
preloadNext(currentIndex);
|
||
preloadPrev(currentIndex);
|
||
});
|
||
|
||
show_buttons(0);
|
||
|
||
updateOffset();
|
||
overlay.style.display = 'block';
|
||
// Fade in overlay
|
||
setTimeout(function () {
|
||
clmod(overlay, 'visible', 1);
|
||
if (options.bodyClass && document.body.classList)
|
||
document.body.classList.add(options.bodyClass);
|
||
|
||
if (options.afterShow)
|
||
options.afterShow();
|
||
}, 50);
|
||
|
||
if (options.onChange && !url_ts)
|
||
options.onChange.call(currentGallery, currentIndex, imagesElements.length);
|
||
|
||
url_ts = null;
|
||
documentLastFocus = document.activeElement;
|
||
btnClose.focus();
|
||
isOverlayVisible = true;
|
||
}
|
||
|
||
function hideOverlay(e, dtor) {
|
||
ev(e);
|
||
playvid(false);
|
||
removeFromCache('#files');
|
||
if (options.noScrollbars) {
|
||
document.documentElement.style.overflowY = scrollCSS[0];
|
||
document.body.style.overflowY = scrollCSS[1];
|
||
}
|
||
|
||
try {
|
||
if (document.fullscreenElement)
|
||
document.exitFullscreen();
|
||
}
|
||
catch (ex) { }
|
||
isFullscreen = false;
|
||
|
||
if (toast.tag == 'bb-ded')
|
||
toast.hide();
|
||
|
||
if (dtor || overlay.style.display === 'none')
|
||
return;
|
||
|
||
if (options.duringHide)
|
||
options.duringHide();
|
||
|
||
sethash('');
|
||
unbindEvents();
|
||
|
||
// Fade out and hide the overlay
|
||
clmod(overlay, 'visible');
|
||
setTimeout(function () {
|
||
overlay.style.display = 'none';
|
||
if (options.bodyClass && document.body.classList)
|
||
document.body.classList.remove(options.bodyClass);
|
||
|
||
qsr('#bbox-halp');
|
||
|
||
if (options.afterHide)
|
||
options.afterHide();
|
||
|
||
options.refocus && documentLastFocus && documentLastFocus.focus();
|
||
isOverlayVisible = false;
|
||
unvid();
|
||
unfig();
|
||
}, 250);
|
||
}
|
||
|
||
function unvid(keep) {
|
||
var vids = QSA('#bbox-overlay video');
|
||
for (var a = vids.length - 1; a >= 0; a--) {
|
||
var v = vids[a];
|
||
if (v == keep)
|
||
continue;
|
||
|
||
unbind(v, 'touchstart', vtouch, nonPassiveEvent);
|
||
unbind(v, 'error', lerr);
|
||
v.src = '';
|
||
v.load();
|
||
|
||
var p = v.parentNode;
|
||
p.removeChild(v);
|
||
p.parentNode.removeChild(p);
|
||
}
|
||
}
|
||
|
||
function unfig(keep) {
|
||
var figs = QSA('#bbox-overlay figure'),
|
||
npre = options.preload || 0,
|
||
k = [];
|
||
|
||
if (keep === undefined)
|
||
keep = -9;
|
||
|
||
for (var a = keep - npre; a <= keep + npre; a++)
|
||
k.push('bbox-figure-' + a);
|
||
|
||
for (var a = figs.length - 1; a >= 0; a--) {
|
||
var f = figs[a];
|
||
if (!has(k, f.getAttribute('id')))
|
||
f.parentNode.removeChild(f);
|
||
}
|
||
}
|
||
|
||
function lerr() {
|
||
var t;
|
||
try {
|
||
t = this.getAttribute('src');
|
||
t = uricom_dec(t.split('/').pop().split('?')[0]);
|
||
}
|
||
catch (ex) { }
|
||
|
||
t = 'Failed to open ' + (t?t:'file');
|
||
console.log('bb-ded', t);
|
||
t += '\n\nEither the file is corrupt, or your browser does not understand the file format or codec';
|
||
|
||
try {
|
||
t += "\n\nerr#" + this.error.code + ", " + this.error.message;
|
||
}
|
||
catch (ex) { }
|
||
|
||
this.ded = esc(t);
|
||
if (this === vidimg())
|
||
toast.err(20, this.ded, 'bb-ded');
|
||
}
|
||
|
||
function loadImage(index, callback) {
|
||
var imageContainer = imagesElements[index];
|
||
var galleryItem = currentGallery[index];
|
||
|
||
if (typeof imageContainer === 'undefined' || typeof galleryItem === 'undefined')
|
||
return; // out-of-bounds or gallery dirty
|
||
|
||
if (imageContainer.querySelector('img, video'))
|
||
// was loaded, cb and bail
|
||
return callback ? callback() : null;
|
||
|
||
// maybe unloaded video
|
||
while (imageContainer.firstChild)
|
||
imageContainer.removeChild(imageContainer.firstChild);
|
||
|
||
var imageElement = galleryItem.imageElement,
|
||
imageSrc = galleryItem.href || imageElement.href,
|
||
is_vid = re_v.test(imageSrc),
|
||
thumbnailElement = imageElement.querySelector('img, video'),
|
||
imageCaption = typeof options.captions === 'function' ?
|
||
options.captions.call(currentGallery, imageElement, index) :
|
||
imageElement.getAttribute('data-caption') || imageElement.title;
|
||
|
||
imageSrc = addq(imageSrc, 'cache');
|
||
|
||
if (is_vid && index != currentIndex)
|
||
return; // no preload
|
||
|
||
var figure = mknod('figure', 'bbox-figure-' + index);
|
||
figure.innerHTML = '<div class="bbox-spinner">' +
|
||
'<div class="bbox-double-bounce1"></div>' +
|
||
'<div class="bbox-double-bounce2"></div>' +
|
||
'</div>';
|
||
|
||
if (options.captions && imageCaption) {
|
||
var figcaption = mknod('figcaption', 'bbox-figcaption-' + index);
|
||
figcaption.innerHTML = imageCaption;
|
||
figure.appendChild(figcaption);
|
||
}
|
||
imageContainer.appendChild(figure);
|
||
|
||
var image = mknod(is_vid ? 'video' : 'img');
|
||
clmod(imageContainer, 'vid', is_vid);
|
||
|
||
bind(image, 'error', lerr);
|
||
bind(image, is_vid ? 'loadedmetadata' : 'load', function () {
|
||
// Remove loader element
|
||
qsr('#baguette-img-' + index + ' .bbox-spinner');
|
||
if (!options.async && callback)
|
||
callback();
|
||
});
|
||
image.setAttribute('src', imageSrc);
|
||
if (is_vid) {
|
||
image.volume = clamp(fcfg_get('vol', dvol / 100), 0, 1);
|
||
image.setAttribute('controls', 'controls');
|
||
image.setAttribute('playsinline', '1');
|
||
// ios ignores poster
|
||
image.onended = vidEnd;
|
||
image.onplay = image.onpause = ppHandler;
|
||
}
|
||
image.alt = thumbnailElement ? thumbnailElement.alt || '' : '';
|
||
if (options.titleTag && imageCaption)
|
||
image.title = imageCaption;
|
||
|
||
figure.appendChild(image);
|
||
|
||
if (is_vid && window.afilt)
|
||
afilt.apply(undefined, image);
|
||
|
||
if (options.async && callback)
|
||
callback();
|
||
}
|
||
|
||
function ppHandler() {
|
||
var now = Date.now();
|
||
if (now - un_pp < 50) {
|
||
un_pp = 0;
|
||
return playpause(); // browser undid space hotkey
|
||
}
|
||
show_buttons(this.paused ? 0 : 1);
|
||
}
|
||
|
||
function showRightImage(e) {
|
||
ev(e);
|
||
var dir = options.readDirRtl ? -1 : 1;
|
||
return show(currentIndex + dir);
|
||
}
|
||
|
||
function showLeftImage(e) {
|
||
ev(e);
|
||
var dir = options.readDirRtl ? 1 : -1;
|
||
return show(currentIndex + dir);
|
||
}
|
||
|
||
function showNextImageIgnoreReadDir(e) {
|
||
ev(e);
|
||
return show(currentIndex + 1);
|
||
}
|
||
|
||
function showPreviousImageIgnoreReadDir(e) {
|
||
ev(e);
|
||
return show(currentIndex - 1);
|
||
}
|
||
|
||
function showFirstImage(e) {
|
||
if (e)
|
||
e.preventDefault();
|
||
|
||
return show(0);
|
||
}
|
||
|
||
function showLastImage(e) {
|
||
if (e)
|
||
e.preventDefault();
|
||
|
||
return show(currentGallery.length - 1);
|
||
}
|
||
|
||
function show(index, gallery) {
|
||
gallery = gallery || currentGallery;
|
||
if (!isOverlayVisible && index >= 0 && index < gallery.length) {
|
||
prepareOverlay(gallery, options);
|
||
showOverlay(index);
|
||
return true;
|
||
}
|
||
|
||
if (index < 0)
|
||
return bounceAnimation(options.readDirRtl ? 'right' : 'left');
|
||
|
||
if (index >= imagesElements.length)
|
||
return bounceAnimation(options.readDirRtl ? 'left' : 'right');
|
||
|
||
var orot;
|
||
try {
|
||
orot = vidimg().getAttribute('rot');
|
||
vid().pause();
|
||
}
|
||
catch (ex) { }
|
||
|
||
currentIndex = index;
|
||
loadImage(currentIndex, function () {
|
||
preloadNext(currentIndex);
|
||
preloadPrev(currentIndex);
|
||
});
|
||
updateOffset();
|
||
|
||
var im = vidimg();
|
||
if (im && im.ded)
|
||
toast.err(20, im.ded, 'bb-ded');
|
||
else if (toast.tag == 'bb-ded')
|
||
toast.hide();
|
||
|
||
if (orot && im.getAttribute('rot') === null)
|
||
rotn(orot / 90, 1);
|
||
|
||
if (options.animation == 'none')
|
||
unvid(vid());
|
||
else
|
||
setTimeout(function () {
|
||
unvid(vid());
|
||
}, 100);
|
||
|
||
unfig(index);
|
||
|
||
if (options.onChange)
|
||
options.onChange.call(currentGallery, currentIndex, imagesElements.length);
|
||
|
||
return true;
|
||
}
|
||
|
||
var prev_cw = 0, prev_ch = 0, unrot_timer = null;
|
||
function rotn(n, asap) {
|
||
var el = vidimg(),
|
||
orot = parseInt(el.getAttribute('rot') || 0),
|
||
frot = orot + (n || 0) * 90;
|
||
|
||
if (!frot && !orot)
|
||
return; // reflow noop
|
||
|
||
var co = ebi('bbox-overlay'),
|
||
cw = co.clientWidth,
|
||
ch = co.clientHeight;
|
||
|
||
if (!n && prev_cw === cw && prev_ch === ch)
|
||
return; // reflow noop
|
||
|
||
clmod(el, 'asap', asap);
|
||
|
||
prev_cw = cw;
|
||
prev_ch = ch;
|
||
var rot = frot,
|
||
iw = el.naturalWidth || el.videoWidth,
|
||
ih = el.naturalHeight || el.videoHeight,
|
||
magic = 4, // idk, works in enough browsers
|
||
dl = el.closest('div').querySelector('figcaption a'),
|
||
vw = cw,
|
||
vh = ch - dl.offsetHeight + magic,
|
||
pmag = Math.min(vw / ih, vh / iw),
|
||
wmag = Math.min(vw / iw, vh / ih);
|
||
|
||
if (!options.bbzoom) {
|
||
pmag = Math.min(1, pmag);
|
||
wmag = Math.min(1, wmag);
|
||
}
|
||
|
||
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);
|
||
timer.add(rotn);
|
||
if (!rot) {
|
||
clearTimeout(unrot_timer);
|
||
unrot_timer = setTimeout(unrot, 300);
|
||
}
|
||
}
|
||
function rotl() {
|
||
rotn(-1);
|
||
}
|
||
function rotr() {
|
||
rotn(1);
|
||
}
|
||
function unrot() {
|
||
var el = vidimg(),
|
||
orot = el.getAttribute('rot'),
|
||
rot = parseInt(orot || 0);
|
||
|
||
while (rot < 0) rot += 360;
|
||
while (rot >= 360) rot -= 360;
|
||
if (rot || orot === null)
|
||
return;
|
||
|
||
clmod(el, 'nt', 1);
|
||
el.setAttribute('rot', 0);
|
||
el.removeAttribute("style");
|
||
rot = el.offsetHeight;
|
||
clmod(el, 'nt');
|
||
timer.rm(rotn);
|
||
}
|
||
|
||
function vid() {
|
||
if (currentIndex >= imagesElements.length)
|
||
return;
|
||
|
||
return imagesElements[currentIndex].querySelector('video');
|
||
}
|
||
|
||
function vidimg() {
|
||
if (currentIndex >= imagesElements.length)
|
||
return;
|
||
|
||
return imagesElements[currentIndex].querySelector('img, video');
|
||
}
|
||
|
||
function playvid(play) {
|
||
if (!play) {
|
||
timer.rm(loopchk);
|
||
loopA = loopB = null;
|
||
}
|
||
|
||
var v = vid();
|
||
if (!v)
|
||
return;
|
||
|
||
v[play ? 'play' : 'pause']();
|
||
if (play && loopA !== null && v.currentTime < loopA)
|
||
v.currentTime = loopA;
|
||
}
|
||
|
||
function playpause() {
|
||
var v = vid();
|
||
if (v)
|
||
v[v.paused ? "play" : "pause"]();
|
||
}
|
||
|
||
function relseek(sec) {
|
||
var v = vid(),
|
||
t = v && v.currentTime;
|
||
|
||
if (isNum(t))
|
||
v.currentTime = t + sec;
|
||
}
|
||
|
||
function vidEnd() {
|
||
if (this == vid() && vnext)
|
||
showNextImageIgnoreReadDir();
|
||
}
|
||
|
||
function setloop(side) {
|
||
var v = vid();
|
||
if (!v)
|
||
return;
|
||
|
||
var t = v.currentTime;
|
||
if (side == 1) loopA = t;
|
||
if (side == 2) loopB = t;
|
||
if (side)
|
||
toast.inf(5, 'Loop' + (side == 1 ? 'A' : 'B') + ': ' + f2f(t, 2));
|
||
|
||
if (loopB !== null) {
|
||
timer.add(loopchk);
|
||
sethash(location.hash.slice(1).split('&')[0] + '&t=' + (loopA || 0) + '-' + loopB);
|
||
}
|
||
}
|
||
|
||
function loopchk() {
|
||
if (loopB === null)
|
||
return;
|
||
|
||
var v = vid();
|
||
if (!v || v.paused || v.currentTime < loopB)
|
||
return;
|
||
|
||
v.currentTime = loopA || 0;
|
||
}
|
||
|
||
function urltime(txt) {
|
||
url_ts = txt;
|
||
}
|
||
|
||
function mp_ctl() {
|
||
var v = vid();
|
||
if (!vmute && v && mp.au && !mp.au.paused) {
|
||
mp.fade_out();
|
||
resume_mp = true;
|
||
}
|
||
else if (resume_mp && (vmute || !v) && mp.au && mp.au.paused) {
|
||
mp.fade_in();
|
||
resume_mp = false;
|
||
}
|
||
}
|
||
|
||
function show_buttons(v) {
|
||
clmod(ebi('bbox-overlay'), 'immersive', v);
|
||
}
|
||
|
||
function bounceAnimation(direction) {
|
||
slider.className = options.animation == 'slideIn' ? 'bounce-from-' + direction : 'eog';
|
||
setTimeout(function () {
|
||
slider.className = '';
|
||
}, 300);
|
||
return false;
|
||
}
|
||
|
||
function updateOffset(noTransition) {
|
||
var dir = options.readDirRtl ? 1 : -1,
|
||
offset = dir * currentIndex * 100 + '%',
|
||
xform = slider.style.perspective !== undefined;
|
||
|
||
if (options.animation === 'fadeIn' && !noTransition) {
|
||
slider.style.opacity = 0;
|
||
setTimeout(function () {
|
||
xform ?
|
||
slider.style.transform = 'translate3d(' + offset + ',0,0)' :
|
||
slider.style.left = offset;
|
||
slider.style.opacity = 1;
|
||
}, 100);
|
||
} else {
|
||
xform ?
|
||
slider.style.transform = 'translate3d(' + offset + ',0,0)' :
|
||
slider.style.left = offset;
|
||
}
|
||
playvid(false);
|
||
var v = vid();
|
||
if (v) {
|
||
playvid(true);
|
||
v.muted = vmute;
|
||
v.loop = vloop;
|
||
if (url_ts) {
|
||
var seek = ('' + url_ts).split('-');
|
||
v.currentTime = seek[0];
|
||
if (seek.length > 1) {
|
||
loopA = parseFloat(seek[0]);
|
||
loopB = parseFloat(seek[1]);
|
||
setloop();
|
||
}
|
||
}
|
||
bind(v, 'touchstart', vtouch, nonPassiveEvent);
|
||
}
|
||
selbg();
|
||
mp_ctl();
|
||
setVmode();
|
||
|
||
var el = vidimg();
|
||
if (el.getAttribute('rot'))
|
||
timer.add(rotn);
|
||
else
|
||
timer.rm(rotn);
|
||
|
||
var ctime = 0;
|
||
el.onclick = v ? null : function (e) {
|
||
var rc = e.target.getBoundingClientRect(),
|
||
x = e.clientX - rc.left,
|
||
fx = x / (rc.right - rc.left);
|
||
|
||
// if (fx < 0.3)
|
||
// return showLeftImage();
|
||
|
||
// if (fx > 0.7)
|
||
// return showRightImage();
|
||
|
||
show_buttons('t');
|
||
|
||
if (Date.now() - ctime <= 500 && !IPHONE)
|
||
tglfull();
|
||
|
||
ctime = Date.now();
|
||
};
|
||
|
||
var prev = QS('.full-image.vis');
|
||
if (prev)
|
||
clmod(prev, 'vis');
|
||
|
||
clmod(el.closest('div'), 'vis', 1);
|
||
}
|
||
|
||
function preloadNext(index) {
|
||
if (index - currentIndex >= options.preload)
|
||
return;
|
||
|
||
loadImage(index + 1, function () {
|
||
preloadNext(index + 1);
|
||
});
|
||
}
|
||
|
||
function preloadPrev(index) {
|
||
if (currentIndex - index >= options.preload)
|
||
return;
|
||
|
||
loadImage(index - 1, function () {
|
||
preloadPrev(index - 1);
|
||
});
|
||
}
|
||
|
||
function bind(element, event, callback, options) {
|
||
element.addEventListener(event, callback, options);
|
||
}
|
||
|
||
function unbind(element, event, callback, options) {
|
||
element.removeEventListener(event, callback, options);
|
||
}
|
||
|
||
function destroyPlugin() {
|
||
hideOverlay(undefined, true);
|
||
unbindEvents();
|
||
clearCachedData();
|
||
document.getElementsByTagName('body')[0].removeChild(ebi('bbox-overlay'));
|
||
data = {};
|
||
currentGallery = [];
|
||
currentIndex = 0;
|
||
}
|
||
|
||
return {
|
||
run: run,
|
||
show: show,
|
||
showNext: showRightImage,
|
||
showPrevious: showLeftImage,
|
||
relseek: relseek,
|
||
urltime: urltime,
|
||
playpause: playpause,
|
||
hide: hideOverlay,
|
||
destroy: destroyPlugin
|
||
};
|
||
})();
|
||
|
||
J_BBX = 2;
|