Swipe left/right on images or use overlay buttons on videos to navigate between posts. Double-click images to open them in a new tab (fullscreen). Adds a favorite button below the image (only if the original heart icon exists).
// ==UserScript==
// @name Rule34 Mobile Swipe & Video Navigation
// @name:tr Rule34 Mobil Kaydırma ve Video Navigasyonu
// @namespace https://greasyfork.org/en/users/1500762-kerimdemirkaynak
// @version 1.8
// @description Swipe left/right on images or use overlay buttons on videos to navigate between posts. Double-click images to open them in a new tab (fullscreen). Adds a favorite button below the image (only if the original heart icon exists).
// @description:tr Resimlerde sola/sağa kaydırarak veya videolardaki butonları kullanarak gönderiler arasında gezinin. Tam ekran görmek için resimlere çift tıklayın. Sadece orijinal kalp ikonu varsa görselin altına favori butonu ekler.
// @author Kerim Demirkaynak
// @license MIT
// @icon https://www.google.com/s2/favicons?sz=256&domain_url=https%3A%2F%2Frule34.xxx%2F
// @match https://rule34.xxx/index.php?page=post&s=view*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// 1. KESİN ÇÖZÜM: Yalnızca alttaki orijinal mobil navigasyon çubuğunda "heart-img" (kalp ikonu) varsa butonu ekle.
const heartImg = document.getElementById('heart-img');
const originalFavLink = heartImg ? heartImg.closest('a') : null;
// 2. Alttaki gezinme çubuğunu kaldır
const bottomBar = document.getElementById('navlinksContainer');
if (bottomBar) bottomBar.style.display = 'none';
const mainImage = document.getElementById('image');
const videoPlayer = document.querySelector('video, #gelcomVideoPlayer');
const container = document.querySelector('.image-container');
// Eğer video varsa -> overlay butonlar ekle
if (videoPlayer) {
const createOverlayButton = (symbol, side, clickHandler, customStyles = {}) => {
const btn = document.createElement('div');
btn.innerHTML = symbol;
btn.style.position = 'fixed';
btn.style.top = '50%';
btn.style[side] = '20px';
btn.style.transform = 'translateY(-50%)';
btn.style.fontSize = '3rem';
btn.style.color = 'white';
btn.style.background = 'rgba(0,0,0,0.5)';
btn.style.padding = '10px 16px';
btn.style.borderRadius = '50%';
btn.style.cursor = 'pointer';
btn.style.userSelect = 'none';
btn.style.zIndex = '999999';
btn.style.transition = 'background 0.2s';
for (const [key, value] of Object.entries(customStyles)) {
btn.style[key] = value;
}
btn.addEventListener('mouseenter', () => btn.style.background = 'rgba(0,0,0,0.7)');
btn.addEventListener('mouseleave', () => btn.style.background = 'rgba(0,0,0,0.5)');
btn.addEventListener('click', clickHandler);
document.body.appendChild(btn);
return btn;
};
const nextLink = document.querySelector('a#next_search_link');
const prevLink = document.querySelector('a#prev_search_link');
if (prevLink) createOverlayButton("←", "left", () => prevLink.click());
if (nextLink) createOverlayButton("→", "right", () => nextLink.click());
// Kullanıcı giriş yapmışsa (yani kalp ikonu bulunduysa) sağ üste favori butonu ekle
if (originalFavLink) {
const favOverlayBtn = createOverlayButton("❤️", "right", (e) => {
e.preventDefault();
originalFavLink.click();
favOverlayBtn.innerHTML = '✅';
setTimeout(() => favOverlayBtn.innerHTML = '❤️', 2000);
}, { top: '20px', transform: 'none', fontSize: '2rem', padding: '8px 12px' });
}
return;
}
// Eğer resim varsa -> kaydırma aktif
if (mainImage && container) {
container.style.cursor = 'grab';
// 3. Yeni favori butonunu oluştur (SADECE KALP İKONU BULUNDUYSA EKLENECEK)
if (originalFavLink) {
const favBtnContainer = document.createElement('div');
favBtnContainer.style.textAlign = 'center';
const favBtn = document.createElement('div');
favBtn.innerHTML = '❤️ Add to Favorites';
favBtn.style.display = 'inline-block';
favBtn.style.marginTop = '15px';
favBtn.style.marginBottom = '15px';
favBtn.style.padding = '10px 24px';
favBtn.style.backgroundColor = '#ff4081';
favBtn.style.color = '#fff';
favBtn.style.borderRadius = '50px';
favBtn.style.fontWeight = 'bold';
favBtn.style.fontSize = '1.1rem';
favBtn.style.cursor = 'pointer';
favBtn.style.userSelect = 'none';
favBtn.style.boxShadow = '0 4px 6px rgba(0,0,0,0.3)';
favBtn.style.transition = 'transform 0.1s, background-color 0.2s';
favBtn.addEventListener('mousedown', () => favBtn.style.transform = 'scale(0.95)');
favBtn.addEventListener('mouseup', () => favBtn.style.transform = 'scale(1)');
favBtn.addEventListener('mouseleave', () => favBtn.style.transform = 'scale(1)');
favBtn.addEventListener('click', (e) => {
e.preventDefault();
originalFavLink.click(); // Sitenin kendi fonksiyonunu tetikler
favBtn.innerHTML = '✅ Favorited!';
favBtn.style.backgroundColor = '#4caf50';
setTimeout(() => {
favBtn.innerHTML = '❤️ Add to Favorites';
favBtn.style.backgroundColor = '#ff4081';
}, 2000);
});
// Resim kaydırma fonksiyonu ile butonun çakışmasını önle
const stopProp = (e) => e.stopPropagation();
favBtn.addEventListener('touchstart', stopProp, { passive: true });
favBtn.addEventListener('touchend', stopProp);
favBtn.addEventListener('mousedown', stopProp);
favBtn.addEventListener('mouseup', stopProp);
favBtn.addEventListener('dblclick', stopProp);
favBtnContainer.appendChild(favBtn);
container.appendChild(favBtnContainer);
}
// Çift tıklama ile tam ekran açma
mainImage.addEventListener('dblclick', function(e) {
e.preventDefault();
const originalImageUrl = mainImage.src;
if (originalImageUrl) {
window.open(originalImageUrl, '_blank');
}
});
// Kaydırma Mantığı
let startX = 0, startY = 0, isDragging = false;
const swipeThreshold = 50;
const handleGestureEnd = (endX, endY) => {
const deltaX = endX - startX;
const deltaY = endY - startY;
if (Math.abs(deltaX) > swipeThreshold && Math.abs(deltaX) > Math.abs(deltaY) * 1.5) {
if (deltaX < 0) {
const nextLink = document.querySelector('a#next_search_link');
if (nextLink) nextLink.click();
} else {
const prevLink = document.querySelector('a#prev_search_link');
if (prevLink) prevLink.click();
}
}
};
container.addEventListener('touchstart', (e) => {
startX = e.changedTouches[0].screenX;
startY = e.changedTouches[0].screenY;
}, { passive: true });
container.addEventListener('touchend', (e) => {
handleGestureEnd(e.changedTouches[0].screenX, e.changedTouches[0].screenY);
});
container.addEventListener('mousedown', (e) => {
e.preventDefault();
isDragging = true;
startX = e.screenX;
startY = e.screenY;
container.style.cursor = 'grabbing';
});
container.addEventListener('mouseup', (e) => {
if (isDragging) {
isDragging = false;
container.style.cursor = 'grab';
handleGestureEnd(e.screenX, e.screenY);
}
});
container.addEventListener('mouseleave', () => {
if (isDragging) {
isDragging = false;
container.style.cursor = 'grab';
}
});
}
})();