This commit is contained in:
Flummi
2021-04-17 10:43:23 +02:00
parent d553e71b50
commit 65454961ce
54 changed files with 1996 additions and 25454 deletions

File diff suppressed because one or more lines are too long

109
public/s/js/f0ck.js Normal file
View File

@ -0,0 +1,109 @@
(() => {
if(elem = document.querySelector("#my-video")) {
const video = new v0ck(elem);
}
let tt = false;
const stimeout = 500;
const changePage = (e, pbwork = true) => {
pbwork && document.querySelector("nav.navbar").classList.add("pbwork");
!tt && (tt = setTimeout(() => e.click(), stimeout));
};
// <keybindings>
const clickOnElementBinding = selector => () => (elem = document.querySelector(selector)) ? elem.click() : null;
const keybindings = {
"ArrowLeft": clickOnElementBinding("#next"),
"d": clickOnElementBinding("#next"),
"ArrowRight": clickOnElementBinding("#prev"),
"a": clickOnElementBinding("#prev"),
"r": clickOnElementBinding("#random")
};
document.addEventListener("keydown", e => {
if(e.key in keybindings) {
e.preventDefault();
keybindings[e.key]();
}
});
// </keybindings>
// <image-responsive>
if(f0ckimage = document.querySelector("#f0ck-image")) {
f0ckimage.addEventListener("click", e => {
e.preventDefault();
f0ckimage.hasAttribute("style")
? f0ckimage.removeAttribute("style")
: f0ckimage.setAttribute("style", "max-height: none; height: auto; width: 100%; position: absolute; left: 0;");
});
}
// </image-responsive>
// <scroll-event-adder>
if(f0ckimagescroll = document.querySelector("#image-scroll")) {
f0ckimagescroll.addEventListener("click", e => {
e.preventDefault();
f0ckimagescroll.hasAttribute("style")
? f0ckimagescroll.removeAttribute("style")
: f0ckimagescroll.setAttribute("style", "overflow-y: scroll");
});
}
// </scroll-event-adder>
// <scroller>
if(document.querySelector("ul#posts")) {
document.addEventListener("wheel", e => {
if((window.innerHeight + window.scrollY) >= document.body.offsetHeight && e.deltaY > 0) {
if(elem = document.querySelector(".pagination > .next:not(.disabled)"))
changePage(elem);
}
else if(window.scrollY <= 0 && e.deltaY < 0) {
if(elem = document.querySelector(".pagination > .prev:not(.disabled)"))
changePage(elem);
}
});
}
// </scroller>
// <swipe>
let lastTap = 0;
let xDown = null;
let yDown = null;
document.addEventListener('touchstart', e => {
const firstTouch = (e.touches ?? e.originalEvent.touches)[0];
xDown = firstTouch.clientX;
yDown = firstTouch.clientY;
const currentTime = new Date().getTime();
const tapLength = currentTime - lastTap;
if(tapLength < 500 && tapLength > 0)
if(elem = document.querySelector("#random"))
changePage(elem);
lastTap = currentTime;
}, false);
document.addEventListener('touchmove', e => {
if(!xDown || !yDown)
return;
const xDiff = xDown - e.touches[0].clientX;
const yDiff = yDown - e.touches[0].clientY;
let elem = false;
if(Math.abs(xDiff) > Math.abs(yDiff)) {
if(xDiff > 0) // left
elem = document.querySelector(".pagination > .next:not(.disabled)");
else // right
elem = document.querySelector(".pagination > .prev:not(.disabled)");
}
else {
if(navbar = document.querySelector("nav.navbar") && document.querySelector("ul#posts")) {
if(yDiff > 0 && (window.innerHeight + window.scrollY) >= document.body.offsetHeight) // up
elem = document.querySelector(".pagination > .next:not(.disabled)");
else if(yDiff <= 0 && window.scrollY <= 0 && document.querySelector("ul#posts")) // down
elem = document.querySelector(".pagination > .prev:not(.disabled)");
}
}
if(elem)
changePage(elem);
xDown = yDown = null;
}, false);
// </swipe>
})();

View File

@ -1,55 +0,0 @@
const epochs = [
["year", 31536000],
["month", 2592000],
["day", 86400],
["hour", 3600],
["minute", 60],
["second", 1]
];
const getDuration = timeAgoInSeconds => {
for(let [name, seconds] of epochs) {
const interval = ~~(timeAgoInSeconds / seconds);
if(interval >= 1) return {
interval: interval,
epoch: name
};
}
};
const timeAgo = date => {
const { interval, epoch } = getDuration(~~((new Date() - new Date(date)) / 1000));
return `${interval} ${epoch}${interval === 1 ? "" : "s"} ago`;
};
const clickOnElementBinding = selector => () => (elem = document.querySelector(selector))?elem.click():null;
const keybindings = {
"ArrowLeft": clickOnElementBinding("#next"),
"ArrowRight": clickOnElementBinding("#prev"),
"r": clickOnElementBinding("#random")
};
(() => {
if(video = document.querySelector(".video-js")) {
const vid1 = videojs(video);
vid1.persistvolume({
namespace: "f0ck"
});
if(vid1.autoplay() && !vid1.paused() && vid1.hasClass("vjs-paused")) {
vid1.pause();
vid1.play();
}
}
document.querySelectorAll("time.timeago").forEach(e => e.innerHTML = timeAgo(e.title));
document.addEventListener("keydown", e => {
if(e.key in keybindings) {
e.preventDefault();
keybindings[e.key]();
}
});
if(f0ckimage = document.querySelector("#f0ck-image"))
f0ckimage.addEventListener("click", e => {
e.preventDefault();
f0ckimage.hasAttribute("style")?f0ckimage.removeAttribute("style"):f0ckimage.setAttribute("style", "max-height: unset;");
});
})();

View File

@ -1,23 +0,0 @@
let load = false;
(() => {
const posts = document.querySelector("#posts");
if(posts) {
document.addEventListener("wheel", e => {
if((((document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop + window.innerHeight) + 310
>= ((document.documentElement && document.documentElement.scrollHeight) || document.body.scrollHeight)) && !load) {
load = true;
fetch(`./api/v1/p/${posts.dataset.last}`)
.then(res => res.json())
.then((msg, html = "") => {
for(let i = 0; i < msg.items.length; i++)
if(msg.items[i].id)
html += `<li class="post"><a href="./${msg.items[i].id}" title="${msg.items[i].mime}"><img class="thumb" src="./t/${msg.items[i].id}.png" onerror="this.onerror=null;this.src='/s/img/mp3.png';" /></a></li>\n`;
posts.insertAdjacentHTML("beforeend", html);
posts.dataset.last = msg.last;
load = false;
})
.catch(err => console.log(err));
}
});
}
})();

View File

@ -1,97 +1,22 @@
/* Thanks to StephenLynx, I modified his Theme Changer from the Penumbra Lynx Frontend for the Lynxchan Software https://gitgud.io/LynxChan/LynxChan and reused it to make f0ck a nicer place. */
var themes = [ {
file : 'f0ck95.css',
label : 'f0ck95',
id : 'f0ck95'
}];
let interval = null;
const clocklol = () => {
const l = document.querySelector("body");
if(localStorage.selectedTheme && localStorage.selectedTheme === "f0ck95")
interval = setInterval((d = new Date()) => l.setAttribute("data-clock", d.toLocaleTimeString()), 1e3);
else {
clearInterval(interval);
l.removeAttribute("data-clock");
(() => {
const themes = [ 'f0ck', 'p1nk', 'orange', 'amoled' ];
const acttheme = localStorage.getItem('theme') ?? "f0ck";
document.documentElement.setAttribute("theme", acttheme);
if(themecontainer = document.querySelector("#themes")) {
const sb = document.createElement("select");
sb.id = "themeselector";
themes.forEach(o => {
const option = document.createElement("option");
option.text = o;
if(acttheme === o)
option.selected = true;
sb.add(option);
});
themecontainer.insertAdjacentElement("afterend", sb);
sb.addEventListener("change", e => {
const s = e.target.options[e.target.selectedIndex].innerText;
document.documentElement.setAttribute("theme", s);
localStorage.setItem("theme", s);
});
}
};
const speaker = () => {
const k = document.querySelector("body");
if(localStorage.selectedTheme && localStorage.selectedTheme === "f0ck95")
k.document.createElement("div");
else
return;
};
var customCss;
var addedTheme;
function updateCss() {
if (addedTheme) {
addedTheme.parentNode.removeChild(addedTheme);
addedTheme = null;
}
for (var i = 0; i < themes.length; i++) {
var theme = themes[i];
if (theme.id === localStorage.selectedTheme) {
addedTheme = theme.element;
document.head.insertBefore(theme.element, customCss);
}
}
}
for (var i = 0; i < document.head.children.length; i++) {
var element = document.head.children[i];
if (element.rel === 'stylesheet' && element.href.indexOf('/css/custom.css') > -1) {
customCss = element;
break;
}
}
for (var i = 0; i < themes.length; i++) {
themes[i].element = document.createElement('link');
themes[i].element.type = 'text/css';
themes[i].element.rel = 'stylesheet';
themes[i].element.href = './s/css/' + themes[i].file;
}
clocklol();
updateCss();
var postingLink = document.getElementById('themes');
if (postingLink) {
var divider = document.createElement('span');
divider.style = ('display: none;');
divider.innerHTML = '';
var referenceNode = postingLink.nextSibling;
postingLink.parentNode.insertBefore(divider, referenceNode);
var themeSelector = document.createElement('select');
themeSelector.id = 'themeSelector';
var vanillaOption = document.createElement('option');
vanillaOption.innerHTML = 'Default';
themeSelector.appendChild(vanillaOption);
for (i = 0; i < themes.length; i++) {
var theme = themes[i];
var themeOption = document.createElement('option');
themeOption.innerHTML = theme.label;
if (theme.id === localStorage.selectedTheme) {
themeOption.selected = true;
}
themeSelector.appendChild(themeOption);
}
themeSelector.onchange = function() {
if (!themeSelector.selectedIndex) {
if (localStorage.selectedTheme) {
delete localStorage.selectedTheme;
clocklol();
updateCss();
}
return;
}
var selectedTheme = themes[themeSelector.selectedIndex - 1];
if (selectedTheme.id === localStorage.selectedTheme) {
return;
}
localStorage.selectedTheme = selectedTheme.id;
clocklol();
updateCss();
};
postingLink.parentNode.insertBefore(themeSelector, referenceNode);
}
})();

172
public/s/js/v0ck.js Normal file
View File

@ -0,0 +1,172 @@
const tpl_player = svg => `<div class="v0ck_player_controls">
<div class="v0ck_progress">
<div class="v0ck_progress_filled"></div>
</div>
<button class="v0ck_player_button v0ck_tplay v0ck_toggle" title="Play">
<svg style="width: 20px; height: 20px;">
<use id="v0ck_svg_play" href="${svg}#play"></use>
<use id="v0ck_svg_pause" class="v0ck_hidden" href="${svg}#pause"></use>
</svg>
</button>
<button class="v0ck_player_button v0ck_volume">
<svg style="width: 20px; height: 20px;">
<use id="v0ck_svg_volume_full" href="${svg}#volume_full"></use>
<use id="v0ck_svg_volume_mid" class="v0ck_hidden" href="${svg}#volume_mid"></use>
<use id="v0ck_svg_volume_mute" class="v0ck_hidden" href="${svg}#volume_mute"></use>
</svg>
</button>
<input type="range" name="volume" min="0" max="1" step="0.05" value="1" />
<button class="v0ck_player_button v0ck_playtime">00:00 / 00:00</button>
<span style="flex: 30"></span>
<button data-skip="-10" class="v0ck_player_button">
<svg style="width: 20px; height: 20px;"><use id="v0ck_svg_backward" href="${svg}#backward"></use></svg>
</button>
<button data-skip="10" class="v0ck_player_button">
<svg style="width: 20px; height: 20px;"><use id="v0ck_svg_forward" href="${svg}#forward"></use></svg>
</button>
<button class="v0ck_player_button v0ck_toggle v0ck_fullscreen" title="Full Screen">
<svg style="width: 20px; height: 20px;"><use id="v0ck_svg_fullscreen" href="${svg}#fullscreen"></use></svg>
</button>
</div>
<div class="v0ck_overlay v0ck_hidden">
<svg><use id="v0ck_svg_ol_play" href="${svg}#ol_play"></use></svg>
</div>`;
class v0ck {
constructor(elem) {
const tagName = elem.tagName.toLowerCase();
if(["video", "audio"].includes(tagName)) {
const parent = elem.parentElement;
parent.classList.add("v0ck", "paused");
elem.classList.add("v0ck_video", "viewer");
document.head.insertAdjacentHTML("beforeend", `<link rel="stylesheet" href="/s/css/v0ck.css">`); // inject css
elem.insertAdjacentHTML("afterend", tpl_player("/s/img/v0ck.svg"));
if(tagName === "audio" && elem.hasAttribute('poster')) { // set cover
const player = document.querySelector('.v0ck');
player.style.backgroundImage = `url('${elem.getAttribute('poster')}')`;
}
}
else
return console.error("nope");
this.init(elem);
}
init(elem) {
const player = document.querySelector('.v0ck');
const video = elem;
video.removeAttribute('autoplay');
video.removeAttribute('controls');
const progress = player.querySelector('.v0ck_progress');
const progressBar = player.querySelector('.v0ck_progress_filled');
const toggle = player.querySelector('.v0ck_toggle');
const skipButtons = player.querySelectorAll('.v0ck [data-skip]');
const ranges = player.querySelectorAll('.v0ck input[type="range"]');
const volumeSlider = player.querySelector('.v0ck input[type="range"][name="volume"]');
const fullscreen = player.querySelector('.v0ck_fullscreen');
const playtime = player.querySelector('.v0ck_playtime');
const overlay = player.querySelector('.v0ck_overlay');
const volumeButton = player.querySelector('.v0ck_volume');
const volumeSymbols = volumeButton.querySelectorAll('.v0ck use');
const defaultVolume = 0.5;
let mousedown = false;
let _volume;
function handleVolumeButton(vol) {
[...volumeSymbols].forEach(s => !s.classList.contains('v0ck_hidden') ? s.classList.add('v0ck_hidden') : null);
switch(true) {
case(vol === 0): [...volumeSymbols].filter(s => s.id === "v0ck_svg_volume_mute")[0].classList.toggle('v0ck_hidden'); break;
case(vol <= 0.5 && vol > 0): [...volumeSymbols].filter(s => s.id === "v0ck_svg_volume_mid")[0].classList.toggle('v0ck_hidden'); break;
case(vol > 0.5): [...volumeSymbols].filter(s => s.id === "v0ck_svg_volume_full")[0].classList.toggle('v0ck_hidden'); break;
}
localStorage.setItem("volume", vol);
}
function togglePlay() {
const status = video[video.paused ? 'play' : 'pause']();
if(status !== undefined) { // todo: merge with updatePlayIcon
status
.then(_ => {
overlay.classList[video.paused ? 'remove' : 'add']('v0ck_hidden');
})
.catch(err => {
overlay.classList.toggle('v0ck_hidden');
});
}
else
overlay.classList[video.paused ? 'remove' : 'add']('v0ck_hidden');
}
function updatePlayIcon() {
toggle.classList.toggle('playing');
player.classList.toggle('paused');
toggle.setAttribute('title', toggle.classList.contains('playing') ? 'Pause' : 'Play');
[...toggle.querySelectorAll('use')].forEach(icon => icon.classList.toggle('v0ck_hidden'));
}
function toggleMute(e) {
if(video.volume === 0)
video.volume = volumeSlider.value = _volume === 0 ? defaultVolume : _volume;
else {
_volume = video.volume;
video.volume = volumeSlider.value = 0;
}
handleVolumeButton(video.volume);
}
function skip() {
video.currentTime += +this.dataset.skip;
}
function handleRangeUpdate() {
video[this.name] = this.value;
_volume = video.volume;
handleVolumeButton(video.volume);
}
function formatTime(seconds) {
const minutes = (~~(seconds / 60)).toString().padStart(2, "0");
seconds = (~~(seconds % 60)).toString().padStart(2, "0");
return minutes + ":" + seconds;
}
function handleProgress() {
const percent = (video.currentTime / video.duration) * 100;
progressBar.style.flexBasis = percent + '%';
playtime.innerText = `${formatTime(video.currentTime)} / ${formatTime(video.duration)}`;
}
function scrub(e) {
video.currentTime = (e.offsetX / progress.offsetWidth) * video.duration;
}
function toggleFullScreen(e) {
if(document.fullscreenElement)
document.exitFullscreen();
else
player.requestFullscreen();
}
function toggleFullScreenClasses() {
player.classList.toggle('fullscreen');
}
player.addEventListener('click', e => {
const path = e.path || (e.composedPath && e.composedPath());
if(!path.filter(f => f.classList?.contains('v0ck_player_controls')).length)
togglePlay(e);
});
toggle.addEventListener('click', togglePlay);
video.addEventListener('play', updatePlayIcon);
video.addEventListener('pause', updatePlayIcon);
video.addEventListener('timeupdate', handleProgress);
volumeButton.addEventListener('click', toggleMute);
skipButtons.forEach(button => button.addEventListener('click', skip));
ranges.forEach(range => range.addEventListener('change', handleRangeUpdate));
ranges.forEach(range => range.addEventListener('mousemove', handleRangeUpdate));
progress.addEventListener('click', scrub);
progress.addEventListener('mousemove', e => mousedown && scrub(e));
progress.addEventListener('mousedown', () => mousedown = true);
progress.addEventListener('mouseup', () => mousedown = false);
fullscreen.addEventListener('click', toggleFullScreen);
document.addEventListener('fullscreenchange', toggleFullScreenClasses);
document.addEventListener('mozfullscreenchange', toggleFullScreenClasses);
document.addEventListener('webkitfullscreenchange', toggleFullScreenClasses);
document.addEventListener('msfullscreenchange', toggleFullScreenClasses);
video.volume = _volume = volumeSlider.value = +(localStorage.getItem('volume') ?? defaultVolume);
handleVolumeButton(video.volume);
togglePlay();
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,123 +0,0 @@
"use strict";
(function(factory){
/*!
* Custom Universal Module Definition (UMD)
*
* Video.js will never be a non-browser lib so we can simplify UMD a bunch and
* still support requirejs and browserify. This also needs to be closure
* compiler compatible, so string keys are used.
*/
if (typeof define === 'function' && define['amd']) {
define(['./video'], function(vjs){ factory(window, document, vjs) });
// checking that module is an object too because of umdjs/umd#35
} else if (typeof exports === 'object' && typeof module === 'object') {
factory(window, document, require('video.js'));
} else {
factory(window, document, videojs);
}
})(function(window, document, vjs) {
//cookie functions from https://developer.mozilla.org/en-US/docs/DOM/document.cookie
var
getCookieItem = function(sKey) {
if (!sKey || !hasCookieItem(sKey)) { return null; }
var reg_ex = new RegExp(
"(?:^|.*;\\s*)" +
window.escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") +
"\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*"
);
return window.unescape(document.cookie.replace(reg_ex,"$1"));
},
setCookieItem = function(sKey, sValue, vEnd, sPath, sDomain, bSecure) {
if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return; }
var sExpires = "";
if (vEnd) {
switch (vEnd.constructor) {
case Number:
sExpires = vEnd === Infinity ? "; expires=Tue, 19 Jan 2038 03:14:07 GMT" : "; max-age=" + vEnd;
break;
case String:
sExpires = "; expires=" + vEnd;
break;
case Date:
sExpires = "; expires=" + vEnd.toGMTString();
break;
}
}
document.cookie =
window.escape(sKey) + "=" +
window.escape(sValue) +
sExpires +
(sDomain ? "; domain=" + sDomain : "") +
(sPath ? "; path=" + sPath : "") +
(bSecure ? "; secure" : "");
},
hasCookieItem = function(sKey) {
return (new RegExp(
"(?:^|;\\s*)" +
window.escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") +
"\\s*\\=")
).test(document.cookie);
},
hasLocalStorage = function() {
try {
window.localStorage.setItem('persistVolume', 'persistVolume');
window.localStorage.removeItem('persistVolume');
return true;
} catch(e) {
return false;
}
},
getStorageItem = function(key) {
return hasLocalStorage() ? window.localStorage.getItem(key) : getCookieItem(key);
},
setStorageItem = function(key, value) {
return hasLocalStorage() ? window.localStorage.setItem(key, value) : setCookieItem(key, value, Infinity, '/');
},
extend = function(obj) {
var arg, i, k;
for (i = 1; i < arguments.length; i++) {
arg = arguments[i];
for (k in arg) {
if (arg.hasOwnProperty(k)) {
obj[k] = arg[k];
}
}
}
return obj;
},
defaults = {
namespace: ""
},
volumePersister = function(options) {
var player = this;
var settings = extend({}, defaults, options || {});
var key = settings.namespace + '-' + 'volume';
var muteKey = settings.namespace + '-' + 'mute';
player.on("volumechange", function() {
setStorageItem(key, player.volume());
setStorageItem(muteKey, player.muted());
});
var persistedVolume = getStorageItem(key);
if(persistedVolume !== null){
player.volume(persistedVolume);
}
var persistedMute = getStorageItem(muteKey);
if(persistedMute !== null){
player.muted('true' === persistedMute);
}
};
vjs.plugin("persistvolume", volumePersister);
});