Publish RedGifs 0.6.23 and migrate project website to Gitea
This commit is contained in:
1 parent
01e0debd76
commit
580fd4f5b9
38 files changed
+5987
-42
No files matched your search
@@ -0,0 +1,252 @@
|
||||
/* ==========================================================================
|
||||
RedGifs Video Downloader — showcase site behaviour
|
||||
No dependencies, no network calls, no tracking.
|
||||
========================================================================== */
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var STORAGE_KEY = 'rgvd-lang';
|
||||
|
||||
/* ------------------------------------------------------- repository --
|
||||
ONE place to point the site at the public download location.
|
||||
|
||||
While the release repository is private, every link below 404s for
|
||||
visitors who are not signed in as its owner. Change REPO_URL once and
|
||||
all nine links follow: data-link="release" -> latest release,
|
||||
data-link="repo" -> repository root, data-link="license" -> LICENSE.
|
||||
|
||||
The hrefs stay in the HTML as well, so the links still work with
|
||||
JavaScript disabled; this only rewrites them when REPO_URL differs.
|
||||
-------------------------------------------------------------------------- */
|
||||
|
||||
var REPO_URL = 'https://git.lonarpgproxy.space/OwnerLink/redgifs-video-downloader';
|
||||
|
||||
function initLinks() {
|
||||
var targets = {
|
||||
repo: REPO_URL,
|
||||
release: REPO_URL + '/releases/latest',
|
||||
license: REPO_URL + '/src/branch/main/LICENSE'
|
||||
};
|
||||
|
||||
var nodes = document.querySelectorAll('[data-link]');
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var href = targets[nodes[i].getAttribute('data-link')];
|
||||
if (href) nodes[i].setAttribute('href', href);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- i18n --
|
||||
Russian is authored directly in the markup. English lives in the
|
||||
data-en / data-en-alt / data-en-label attributes and is swapped in on
|
||||
demand. The original Russian is cached in data-ru on first switch.
|
||||
-------------------------------------------------------------------------- */
|
||||
|
||||
var PAGE_META = {
|
||||
ru: {
|
||||
title: document.title,
|
||||
description: metaContent('description')
|
||||
},
|
||||
en: {
|
||||
title: document.documentElement.getAttribute('data-en-title') || '',
|
||||
description: document.documentElement.getAttribute('data-en-description') || ''
|
||||
}
|
||||
};
|
||||
|
||||
function metaContent(name) {
|
||||
var el = document.querySelector('meta[name="' + name + '"]');
|
||||
return el ? el.getAttribute('content') : '';
|
||||
}
|
||||
|
||||
function setMetaContent(name, value) {
|
||||
var el = document.querySelector('meta[name="' + name + '"]');
|
||||
if (el && value) el.setAttribute('content', value);
|
||||
}
|
||||
|
||||
function swapAttribute(el, enAttr, targetAttr, ruAttr, lang) {
|
||||
var en = el.getAttribute(enAttr);
|
||||
if (!en) return;
|
||||
if (!el.hasAttribute(ruAttr)) {
|
||||
el.setAttribute(ruAttr, el.getAttribute(targetAttr) || '');
|
||||
}
|
||||
el.setAttribute(targetAttr, lang === 'en' ? en : el.getAttribute(ruAttr));
|
||||
}
|
||||
|
||||
function applyLanguage(lang) {
|
||||
var isEn = lang === 'en';
|
||||
|
||||
document.documentElement.setAttribute('lang', isEn ? 'en' : 'ru');
|
||||
document.documentElement.setAttribute('data-lang', lang);
|
||||
|
||||
// Text / markup content
|
||||
var nodes = document.querySelectorAll('[data-en]');
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var el = nodes[i];
|
||||
if (!el.hasAttribute('data-ru')) {
|
||||
el.setAttribute('data-ru', el.innerHTML);
|
||||
}
|
||||
// Both sides are authored in this repository, never user input.
|
||||
el.innerHTML = isEn ? el.getAttribute('data-en') : el.getAttribute('data-ru');
|
||||
}
|
||||
|
||||
// Accessible names that are not text content
|
||||
var alts = document.querySelectorAll('[data-en-alt]');
|
||||
for (var a = 0; a < alts.length; a++) {
|
||||
swapAttribute(alts[a], 'data-en-alt', 'alt', 'data-ru-alt', lang);
|
||||
}
|
||||
|
||||
var labels = document.querySelectorAll('[data-en-label]');
|
||||
for (var b = 0; b < labels.length; b++) {
|
||||
swapAttribute(labels[b], 'data-en-label', 'aria-label', 'data-ru-label', lang);
|
||||
}
|
||||
|
||||
// Document metadata
|
||||
if (PAGE_META[lang] && PAGE_META[lang].title) {
|
||||
document.title = PAGE_META[lang].title;
|
||||
}
|
||||
if (PAGE_META[lang] && PAGE_META[lang].description) {
|
||||
setMetaContent('description', PAGE_META[lang].description);
|
||||
}
|
||||
|
||||
// Switch buttons
|
||||
var buttons = document.querySelectorAll('[data-set-lang]');
|
||||
for (var c = 0; c < buttons.length; c++) {
|
||||
buttons[c].setAttribute(
|
||||
'aria-pressed',
|
||||
buttons[c].getAttribute('data-set-lang') === lang ? 'true' : 'false'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function storedLanguage() {
|
||||
var stored = null;
|
||||
try {
|
||||
stored = window.localStorage.getItem(STORAGE_KEY);
|
||||
} catch (err) {
|
||||
stored = null;
|
||||
}
|
||||
if (stored === 'ru' || stored === 'en') return stored;
|
||||
|
||||
var nav = (navigator.language || '').toLowerCase();
|
||||
var list = navigator.languages || [];
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
if (String(list[i]).toLowerCase().indexOf('ru') === 0) return 'ru';
|
||||
}
|
||||
return nav.indexOf('ru') === 0 ? 'ru' : 'en';
|
||||
}
|
||||
|
||||
function initLanguage() {
|
||||
var buttons = document.querySelectorAll('[data-set-lang]');
|
||||
if (!buttons.length) return;
|
||||
|
||||
applyLanguage(storedLanguage());
|
||||
|
||||
for (var i = 0; i < buttons.length; i++) {
|
||||
buttons[i].addEventListener('click', function () {
|
||||
var lang = this.getAttribute('data-set-lang');
|
||||
applyLanguage(lang);
|
||||
try {
|
||||
window.localStorage.setItem(STORAGE_KEY, lang);
|
||||
} catch (err) {
|
||||
/* private mode — the choice simply is not remembered */
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- header -- */
|
||||
|
||||
function initHeader() {
|
||||
var header = document.getElementById('siteHeader');
|
||||
if (!header) return;
|
||||
|
||||
var ticking = false;
|
||||
function update() {
|
||||
header.classList.toggle('is-stuck', window.scrollY > 8);
|
||||
ticking = false;
|
||||
}
|
||||
window.addEventListener('scroll', function () {
|
||||
if (!ticking) {
|
||||
ticking = true;
|
||||
window.requestAnimationFrame(update);
|
||||
}
|
||||
}, { passive: true });
|
||||
update();
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------- mobile nav -- */
|
||||
|
||||
function initNav() {
|
||||
var toggle = document.getElementById('navToggle');
|
||||
var nav = document.getElementById('primaryNav');
|
||||
if (!toggle || !nav) return;
|
||||
|
||||
function close() {
|
||||
nav.classList.remove('is-open');
|
||||
toggle.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
|
||||
toggle.addEventListener('click', function () {
|
||||
var open = nav.classList.toggle('is-open');
|
||||
toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
|
||||
});
|
||||
|
||||
nav.addEventListener('click', function (event) {
|
||||
if (event.target.closest('a')) close();
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', function (event) {
|
||||
if (event.key === 'Escape') close();
|
||||
});
|
||||
|
||||
document.addEventListener('click', function (event) {
|
||||
if (!nav.classList.contains('is-open')) return;
|
||||
if (nav.contains(event.target) || toggle.contains(event.target)) return;
|
||||
close();
|
||||
});
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- reveal -- */
|
||||
|
||||
function initReveal() {
|
||||
var items = document.querySelectorAll('.reveal');
|
||||
if (!items.length) return;
|
||||
|
||||
var reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
if (reduced || !('IntersectionObserver' in window)) {
|
||||
for (var i = 0; i < items.length; i++) items[i].classList.add('is-visible');
|
||||
return;
|
||||
}
|
||||
|
||||
var observer = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (!entry.isIntersecting) return;
|
||||
entry.target.classList.add('is-visible');
|
||||
observer.unobserve(entry.target);
|
||||
});
|
||||
}, { rootMargin: '0px 0px -8% 0px', threshold: 0.08 });
|
||||
|
||||
for (var j = 0; j < items.length; j++) {
|
||||
// Stagger siblings a little so grids cascade instead of popping.
|
||||
var delay = (j % 4) * 70;
|
||||
items[j].style.transitionDelay = delay + 'ms';
|
||||
observer.observe(items[j]);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- boot -- */
|
||||
|
||||
function boot() {
|
||||
initLinks();
|
||||
initLanguage();
|
||||
initHeader();
|
||||
initNav();
|
||||
initReveal();
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', boot);
|
||||
} else {
|
||||
boot();
|
||||
}
|
||||
})();
|
||||
Reference in new issue
Block a user