您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
"Мне повезёт" + навигация по дате/публикации
// ==UserScript== // @name Proza.ru & Stihi.ru Randomizer 22 // @namespace http://tampermonkey.net/ // @version 1.3 // @description "Мне повезёт" + навигация по дате/публикации // @match https://stihi.ru/* // @match https://proza.ru/* // @icon https://i.postimg.cc/3r2Z3hSs/psic.png // @grant GM_addStyle // @license MIT // ==/UserScript== (function () { 'use strict'; const isProza = window.location.hostname.includes('proza.ru'); const publicationLimits = isProza ? [1, 2000] : [1, 11000]; const startLimits = isProza ? [1, 250] : [1, 450]; saveToLocalStorage('publication_min', publicationLimits[0]); saveToLocalStorage('publication_max', publicationLimits[1]); saveToLocalStorage('start_min', startLimits[0]); saveToLocalStorage('start_max', startLimits[1]); // Helper functions function parseUrl() { const urlParts = window.location.pathname.split('/'); return { year: urlParts[1] || '', month: urlParts[2] || '', day: urlParts[3] || '', publication: urlParts[4] || '' }; } function updateUrl(year, month, day, publication) { const baseUrl = window.location.origin + '/'; window.location.href = `${baseUrl}${year}/${month}/${day}/${publication}`; } function saveToLocalStorage(key, value) { localStorage.setItem(key, value); } function loadFromLocalStorage(key, defaultValue) { return localStorage.getItem(key) || defaultValue; } function validateInput(field, value) { let min, max; switch (field) { case 'publication': min = parseInt(loadFromLocalStorage('publication_min', publicationLimits[0]), 10); max = parseInt(loadFromLocalStorage('publication_max', publicationLimits[1]), 10); break; case 'start': min = parseInt(loadFromLocalStorage('start_min', startLimits[0]), 10); max = parseInt(loadFromLocalStorage('start_max', startLimits[1]), 10); break; case 'year': min = Math.max(parseInt(loadFromLocalStorage('year_min', '2000') || '2000', 10), 1); max = Math.min(parseInt(loadFromLocalStorage('year_max', '9999') || '9999', 10), new Date().getFullYear()); break; default: min = parseInt(loadFromLocalStorage(`${field}_min`, '1') || '1', 10); max = parseInt(loadFromLocalStorage(`${field}_max`, '9999') || '9999', 10); } return Math.min(Math.max(value, min), max); } // Create UI container const container = document.createElement('div'); container.id = 'navigation-enhancer'; container.innerHTML = ` <table><tr><td> <input id="year" type="number" placeholder="Year" style="width: 80px;"> <input id="month" type="number" placeholder="Month" style="width: 60px;"> <input id="day" type="number" placeholder="Day" style="width: 60px;"> <input id="publication" type="number" placeholder="Publication" style="width: 100px;"> <button id="reset-main-button">🔄</button> <button id="settings-button">⚙️</button> </td><td> <button id="lucky-button">🍀</button> </td></tr></table> <div id="settings-menu" style="display:none;"> <div class="settings-header"> <label>Set Limits:</label> <button id="reset-settings-button">🔄</button> </div> <div class="settings-group"> <label>Year:</label> <input id="year_min" type="number" style="width: 60px;"> <input id="year_max" type="number" style="width: 60px;"><br> <label>Month:</label> <input id="month_min" type="number" style="width: 60px;"> <input id="month_max" type="number" style="width: 60px;"><br> <label>Day:</label> <input id="day_min" type="number" style="width: 60px;"> <input id="day_max" type="number" style="width: 60px;"><br> <div> <label>Publication:</label> <input id="publication_min" type="number" style="width: 60px;"> <input id="publication_max" type="number" style="width: 60px;"><br> </div><div> <label>Start:</label> <input id="start_min" type="number" style="width: 60px;"> <input id="start_max" type="number" style="width: 60px;"><br> </div> </div> <div class="settings-group"> <label>Redirect on Change:</label> <input id="redirect-on-change" type="checkbox" style="width: 20px;"> </div> <div class="settings-group"> <label>Retry on Error 🍀:</label> <input id="retry-on-error" type="checkbox" style="width: 20px;"> </div> </div> `; let accessedThroughLucky = false; function feelingLuckyList() { accessedThroughLucky = true; const yearMin = parseInt(loadFromLocalStorage('year_min', '2000'), 10); const yearMax = parseInt(loadFromLocalStorage('year_max', String(new Date().getFullYear())), 10); const randomYear = Math.floor(Math.random() * (yearMax - yearMin + 1)) + yearMin; // Respect user's month limits const monthMin = parseInt(loadFromLocalStorage('month_min', '1'), 10); const monthMax = parseInt(loadFromLocalStorage('month_max', '12'), 10); const randomMonth = Math.floor(Math.random() * (monthMax - monthMin + 1)) + monthMin; let maxDays = new Date(randomYear, randomMonth, 0).getDate(); const randomDay = '01'; // List pages use '01' for day const randomStart = Math.floor(Math.random() * (parseInt(loadFromLocalStorage('start_max', startLimits[1]), 10) - parseInt(loadFromLocalStorage('start_min', startLimits[0]), 10) + 1)) + parseInt(loadFromLocalStorage('start_min', startLimits[0]), 10); // Parse the topic from the current URL const currentUrl = new URL(window.location.href); const topic = currentUrl.searchParams.get('topic'); const baseUrl = window.location.origin + '/texts/list.html'; const urlParams = `?day=${randomDay}&month=${String(randomMonth).padStart(2, '0')}&year=${randomYear}&topic=${topic}&start=${randomStart}`; window.location.href = baseUrl + urlParams; } function feelingLucky() { accessedThroughLucky = true; const yearMin = parseInt(loadFromLocalStorage('year_min', '2000'), 10); const yearMax = parseInt(loadFromLocalStorage('year_max', String(new Date().getFullYear())), 10); const randomYear = Math.floor(Math.random() * (yearMax - yearMin + 1)) + yearMin; const randomMonth = Math.floor(Math.random() * 12) + 1; let maxDays = new Date(randomYear, randomMonth, 0).getDate(); const randomDay = Math.floor(Math.random() * maxDays) + 1; const randomPublication = Math.floor(Math.random() * 100) + 1; updateUrl(randomYear, String(randomMonth).padStart(2, '0'), String(randomDay).padStart(2, '0'), randomPublication); } // Find a suitable location for the navigation panel const header = document.querySelector('#header'); const headerAlt = document.querySelector('body > div:nth-child(1) > table:nth-child(1) > tbody:nth-child(1) > tr:nth-child(1) > td:nth-child(1) > table:nth-child(1)'); if (header) { header.insertAdjacentElement('afterend', container); container.style.marginTop = '32px'; } else if (headerAlt) { headerAlt.insertAdjacentElement('afterend', container); } else { document.body.insertBefore(container, document.body.firstChild); } // Select the retryOnError element const retryOnError = document.getElementById('retry-on-error'); // Restore the retryOnError state from localStorage retryOnError.checked = localStorage.getItem('checkboxState') === 'true'; // Add an event listener to save the retryOnError state whenever it changes retryOnError.addEventListener('change', () => { localStorage.setItem('checkboxState', retryOnError.checked); }); // Try again if the page is not found and the retryOnError is checked const notFound = document.querySelector('h1[align="center"], div[align="center"] > h1')?.textContent.match(/найд|удал|закр|ошиб|стр/i); if (notFound && retryOnError.checked && accessedThroughLucky) { feelingLucky(); } // Load initial values into inputs const urlInfo = parseUrl(); const inputFields = ['year', 'month', 'day', 'publication']; inputFields.forEach(field => { const input = document.getElementById(field); input.value = validateInput(field, urlInfo[field]); input.addEventListener('focus', () => saveToLocalStorage('lastFocusedField', field)); }); // Restore focus after reload const lastFocusedField = loadFromLocalStorage('lastFocusedField', ''); if (lastFocusedField) { const focusedInput = document.getElementById(lastFocusedField); if (focusedInput) focusedInput.focus(); } // Function to handle redirect logic let redirectOnChange = loadFromLocalStorage('redirectOnChange', false) === 'true'; const redirectCheckbox = document.getElementById('redirect-on-change'); redirectCheckbox.checked = redirectOnChange; redirectCheckbox.addEventListener('change', () => { redirectOnChange = redirectCheckbox.checked; saveToLocalStorage('redirectOnChange', redirectOnChange); }); function updateAndRedirect() { accessedThroughLucky = false; if (redirectOnChange) { updateUrl( validateInput('year', document.getElementById('year').value), String(validateInput('month', document.getElementById('month').value)).padStart(2, '0'), String(validateInput('day', document.getElementById('day').value)).padStart(2, '0'), validateInput('publication', document.getElementById('publication').value) ); } } let previousValues = {}; inputFields.forEach(field => { const input = document.getElementById(field); previousValues[field] = input.value; // Initialize previous value input.addEventListener('keydown', (e) => { if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { e.preventDefault(); handleArrowKeys(field, e.key === 'ArrowUp' ? 'up' : 'down'); } else if (e.key === 'Enter') { e.preventDefault(); saveToLocalStorage(field, input.value); previousValues[field] = input.value; updateUrl( validateInput('year', document.getElementById('year').value), String(validateInput('month', document.getElementById('month').value)).padStart(2, '0'), String(validateInput('day', document.getElementById('day').value)).padStart(2, '0'), validateInput('publication', document.getElementById('publication').value) ); } }); input.addEventListener('blur', () => { const validatedValue = validateInput(field, input.value); input.value = validatedValue; if (input.value !== previousValues[field]) { previousValues[field] = input.value; // Update previous value updateAndRedirect(); } }); // Add change event listener for year and month fields if (field === 'year' || field === 'month') { input.addEventListener('change', () => { updateDayLimits(); }); } }); function updateDayLimits() { const year = parseInt(document.getElementById('year').value); const month = parseInt(document.getElementById('month').value); const dayInput = document.getElementById('day'); const maxDays = new Date(year, month, 0).getDate(); dayInput.max = maxDays; dayInput.value = Math.min(parseInt(dayInput.value), maxDays); // Update day limits in settings const dayMinInput = document.getElementById('day_min'); const dayMaxInput = document.getElementById('day_max'); dayMaxInput.max = maxDays; dayMaxInput.value = Math.min(parseInt(dayMaxInput.value), maxDays); saveToLocalStorage('day_max', dayMaxInput.value); // Ensure day_max is always greater than or equal to day_min if (parseInt(dayMaxInput.value) < parseInt(dayMinInput.value)) { dayMaxInput.value = dayMinInput.value; saveToLocalStorage('day_max', dayMaxInput.value); } } function handleArrowKeys(field, direction) { accessedThroughLucky = false; const input = document.getElementById(field); let step = event.shiftKey ? 5 : 1; let newValue = parseInt(input.value) + (direction === 'up' ? step : -step); if (field === 'year') { input.value = validateInput(field, newValue); } else if (field === 'month') { if (newValue > 12) { document.getElementById('year').value = validateInput('year', parseInt(document.getElementById('year').value) + Math.floor((newValue - 1) / 12)); input.value = validateInput('month', ((newValue - 1) % 12) + 1); } else if (newValue < 1) { document.getElementById('year').value = validateInput('year', parseInt(document.getElementById('year').value) - Math.ceil(-newValue / 12)); input.value = validateInput('month', 12 - ((-newValue) % 12)); } else { input.value = validateInput('month', newValue); } } else if (field === 'day') { const year = parseInt(document.getElementById('year').value); const month = parseInt(document.getElementById('month').value); let maxDays = new Date(year, month, 0).getDate(); if (newValue > maxDays) { document.getElementById('month').value = validateInput('month', month + 1); if (parseInt(document.getElementById('month').value) > 12) { document.getElementById('year').value = validateInput('year', year + 1); document.getElementById('month').value = '1'; } input.value = '1'; } else if (newValue < 1) { document.getElementById('month').value = validateInput('month', month - 1); if (parseInt(document.getElementById('month').value) < 1) { document.getElementById('year').value = validateInput('year', year - 1); document.getElementById('month').value = '12'; } maxDays = new Date(parseInt(document.getElementById('year').value), parseInt(document.getElementById('month').value), 0).getDate(); input.value = maxDays; } else { input.value = validateInput('day', newValue); } } else if (field === 'publication') { input.value = validateInput('publication', newValue); } updateDayLimits(); previousValues[field] = input.value; updateAndRedirect(); } // "Feeling Lucky" button event listener document.getElementById('lucky-button').addEventListener('click', () => { if (window.location.pathname.includes('list.html')) { feelingLuckyList(); } else { feelingLucky(); } }); // Settings button functionality (toggle visibility) const settingsButton = document.getElementById('settings-button'); const settingsMenu = document.getElementById('settings-menu'); settingsButton.addEventListener('click', () => { settingsMenu.style.display = settingsMenu.style.display === 'none' ? 'block' : 'none'; const limitFields = ['year_min', 'year_max', 'month_min', 'month_max', 'day_min', 'day_max']; if (window.location.pathname.includes('list.html')) { limitFields.push('start_min', 'start_max'); } else { limitFields.push('publication_min', 'publication_max'); } limitFields.forEach(field => { let defaultValue; if (field === 'publication_min' || field === 'publication_max') { defaultValue = field.includes('min') ? '1' : publicationLimits[1]; } else if (field === 'start_min' || field === 'start_max') { defaultValue = field.includes('min') ? '1' : startLimits[1]; } else if (field === 'day_max') { defaultValue = '31'; // Default max day } else { defaultValue = field.includes('min') ? '1' : '9999'; } document.getElementById(field).value = loadFromLocalStorage(field, defaultValue); const input = document.getElementById(field); input.addEventListener('change', () => { saveToLocalStorage(field, input.value); }); }); updateDayLimits(); // Add this line }); // Close settings when clicking outside document.addEventListener('click', (event) => { if (!container.contains(event.target) && event.target !== settingsButton) { settingsMenu.style.display = 'none'; } }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && settingsMenu.style.display !== 'none') { settingsMenu.style.display = 'none'; } }); const currentYear = new Date().getFullYear(); const defaultSettings = { year_min: '2000', year_max: String(currentYear), month_min: '1', month_max: '12', day_min: '1', day_max: '31', publication_min: '1', publication_max: publicationLimits[1], start_min: '1', start_max: startLimits[1], }; const isListPage = window.location.pathname.includes('list.html'); const listPageSettings = document.getElementById('start_min').parentElement; const publicationSettings = document.getElementById('publication_min').parentElement; if (isListPage) { listPageSettings.style.display = 'block'; publicationSettings.style.display = 'none'; } else { listPageSettings.style.display = 'none'; publicationSettings.style.display = 'block'; } document.getElementById('reset-settings-button').addEventListener('click', resetSettings); function resetSettings() { const limitFields = ['year_min', 'year_max', 'month_min', 'month_max', 'day_min', 'day_max', 'publication_min', 'publication_max', 'start_min', 'start_max']; limitFields.forEach(field => { let defaultValue; if (field === 'publication_min' || field === 'publication_max') { defaultValue = defaultSettings[field]; } else if (field === 'start_min' || field === 'start_max') { defaultValue = defaultSettings[field] || (field.includes('min') ? '1' : startLimits[1]); } else { defaultValue = defaultSettings[field]; } document.getElementById(field).value = defaultValue; saveToLocalStorage(field, defaultValue); }); } document.getElementById('reset-main-button').addEventListener('click', resetMainPanel); function resetMainPanel() { const currentYear = new Date().getFullYear(); const currentMonth = new Date().getMonth() + 1; const currentDay = new Date().getDate(); const inputFields = ['year', 'month', 'day', 'publication']; inputFields.forEach(field => { let defaultValue; switch (field) { case 'year': defaultValue = currentYear; break; case 'month': defaultValue = currentMonth; break; case 'day': defaultValue = currentDay; break; case 'publication': defaultValue = 1; break; } const input = document.getElementById(field); input.value = validateInput(field, defaultValue); saveToLocalStorage(field, input.value); }); updateUrl(currentYear, String(currentMonth).padStart(2, '0'), String(currentDay).padStart(2, '0'), 1); } // Add CSS styles directly in the script GM_addStyle(` #navigation-enhancer { background-color: #111111; padding: 5px; font-family: Arial, sans-serif; width: 320px; margin: 0 24px 0 auto; color:white } #navigation-enhancer input { width: 40px; margin-right: 5px; background: #111111; color: #ffffff; } #navigation-enhancer input#publication { width: 60px; } #navigation-enhancer button { margin-right: 5px; color: white; border: none; border-radius: 4px; padding: 5px 10px; margin-top: 5px; background:black } #navigation-enhancer button:hover { background: #444444 } #lucky-button{ font-size:200% } .settings-header { } #reset-settings-button{ } #settings-menu { border-left: 4px solid white; padding:8px } .settings-group { margin-top: 16px; } `); })();