// ==UserScript==
// @name GreasyFork: Check To Search
// @namespace Violentmonkey Scripts
// @match https://greasyfork.org/*
// @grant none
// @version 0.1.0
// @license MIT
// @description 1/8/2024, 6:31:18 PM
// @run-at document-start
// ==/UserScript==
function fixURL(url) {
url = url.replace(/(\/\w+\/\d+)\-[%\d\w-]+([?#]|$)/, '$1$2').replace(`${location.origin}/`, '/');
if (!url) return '';
if (url.includes('|')) return '';
return url
}
const _genericTextChars = {
1: '\x20\xA0\u2000-\u200A\u202F\u205F\u3000',
2: '\u200B-\u200D\u2060\uFEFF',
4: '\u180E\u2800\u3164',
8: '\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\u2800',
16: '\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\u2800\t\r\n' // plus tab (\x09), newline (\x0A), \r
}
const _genericTextREs = {
1: new RegExp(`[${_genericTextChars[1]}]`, 'g'),
2: new RegExp(`[${_genericTextChars[2]}]`, 'g'),
4: new RegExp(`[${_genericTextChars[4]}]`, 'g'),
8: new RegExp(`[${_genericTextChars[8]}]`, 'g'),
16: new RegExp(`[${_genericTextChars[16]}]`, 'g')
}
function genericText(text, flag) {
// https://unicode-explorer.com/articles/space-characters
// https://medium.com/@ray102467/js-regex-3fbfe4d3115
if (!text || typeof text !== 'string') return text;
// regular space to space
if (flag & 1) text = text.replace(_genericTextREs[1], (flag & (1 << 8)) ? '' : ' '); // 1 | 256
// zero-width space to empty
if (flag & 2) text = text.replace(_genericTextREs[2], '');
// space chars to space
if (flag & 4) text = text.replace(_genericTextREs[4], (flag & (1 << 8)) ? '' : ' '); // 4 | 1024
// improper chars to empty
if (flag & 8) text = text.replace(_genericTextREs[8], '');
// improper+ chars to empty
if (flag & 16) text = text.replace(_genericTextREs[16], '');
return text;
}
const cssTextFn = () => `
.r41-custom-search-input {
font-size: 16px;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: border-color 0.3s ease;
}
.r41-custom-search-input:focus {
border-color: #007bff; /* Blue border on focus */
outline: none;
}
.r41-loading-spinner {
display: none;
border: 4px solid #f3f3f3; /* Light grey */
border-top: 4px solid #3498db; /* Blue */
border-radius: 50%;
width: 20px;
height: 20px;
animation: r41-spin 2s linear infinite;
margin-left: 4px;
}
@keyframes r41-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.r41-custom-search-input-header{
display: flex;
flex-direction: row;
align-items: center;
}
.r41-custom-search-input{
opacity: 0;
position:absolute;
z-index:-1;
}
.r41-custom-search-input:focus{
opacity: 1;
position:relative;
z-index:initial;
}
.r41-custom-search-input:focus + .r41-loading-spinner {
margin-left: -34px;
}
`;
let onloadPromise = Promise.resolve();
function addElements() {
if (!document.querySelector('#gf_390_lz')) {
// Load LZ-String library
const script = document.createElement('script');
script.id = 'gf_390_lz';
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/lz-string/1.5.0/lz-string.min.js';
document.head.appendChild(script);
onloadPromise = new Promise(resolve => {
script.onload = resolve;
});
}
if (!document.querySelector('#gf_391_style')) {
const style = document.createElement('style');
style.id = 'gf_391_style';
style.textContent = cssTextFn();
document.head.appendChild(style);
}
return onloadPromise;
}
const onReadyPromise = new Promise((resolve) => {
document.addEventListener('DOMContentLoaded', resolve)
});
const delayedReady = onReadyPromise.then(() => {
if (!location.href.includes('/users/')) return false;
new Promise(resolve => setTimeout(resolve, 200))
});
const hookToFn = (w) => {
let currentPageListHtml = '';
const {sectionId, storagePrefix, listId,elementIdPrefix} = w;
delayedReady.then(async (xt) => {
if (xt === false) return;
const h3 = document.querySelector(`#${sectionId}>header>h3`);
if (h3) {
const onloadPromise = addElements();
let onload2 = onloadPromise.then((xt) => {
if(xt === false) return;
checkCache();
cacheCurrentPageList();
});
h3.addEventListener('click', function () {
clicked = true;
onload2.then(() => {
if ((getSelection() + "") === "" && h3.isConnected === true) {
if (showInput(h3) === false) replaceWithInput(h3);
}
});
});
}
});
function checkCache() {
let urls = sessionStorage.getItem(`${storagePrefix}urls`) || "";
if (urls.includes(`|${fixURL(location.href)}|`)) return;
for (const url of urls.split('|')) {
if (!url) continue;
sessionStorage.removeItem(`${storagePrefix}${url}`);
}
sessionStorage.removeItem(`${storagePrefix}urls`);
}
function cacheCurrentPageList() {
const listElement = document.querySelector(`ol#${listId}`);
if (listElement) {
currentPageListHtml = listElement.outerHTML;
saveCache(location.href, currentPageListHtml);
}
}
let globalChangeCounter = 0;
function showInput(h3Element) {
const input = document.getElementById(`${elementIdPrefix}custom-search-input`)
if (!input) return false;
// input.value = h3Element.textContent;
// input.id = `${elementIdPrefix}custom-search-input`;
// input.classList.add('r41-custom-search-input')
// h3Element.parentNode.classList.add(`${elementIdPrefix}custom-search-input-header`, 'r41-custom-search-input-header');
// h3Element.parentNode.insertBefore(input, h3Element.nextSibling)
// h3Element.parentNode.replaceChild(input, h3Element);
input.select();
// input.addEventListener('input', () => handleInputChange(input));
// Add loading spinner (hidden by default)
// const spinner = document.createElement('div');
// spinner.id = 'loading-spinner';
// input.parentNode.insertBefore(spinner, input.nextSibling);
}
function replaceWithInput(h3Element) {
const input = document.createElement('input');
// input.value = h3Element.textContent;
input.id = `${elementIdPrefix}custom-search-input`;
input.classList.add('r41-custom-search-input')
h3Element.parentNode.classList.add(`${elementIdPrefix}custom-search-input-header`, 'r41-custom-search-input-header');
h3Element.parentNode.insertBefore(input, h3Element.nextSibling)
// h3Element.parentNode.replaceChild(input, h3Element);
input.select();
input.addEventListener('input', () => handleInputChange(input));
// Add loading spinner (hidden by default)
const spinner = document.createElement('div');
spinner.classList.add('r41-loading-spinner')
spinner.id = `${elementIdPrefix}loading-spinner`;
input.parentNode.insertBefore(spinner, input.nextSibling);
}
let lastState = false;
function setLoadingState(isLoading) {
if (lastState === isLoading) return;
const spinner = document.getElementById(`${elementIdPrefix}loading-spinner`);
if (spinner) {
spinner.style.display = isLoading ? 'inline-block' : 'none';
}
lastState = isLoading;
}
let busyCache = 0;
async function handleInputChange(input) {
const currentChangeCount = ++globalChangeCounter;
if (busyCache === 0) {
setLoadingState(true);
await fetchAndCacheScripts();
await new Promise(resolve => setTimeout(resolve, 140));
} else if (busyCache === 1) {
setLoadingState(true);
await new Promise(resolve => setTimeout(resolve, 600));
} else if (busyCache === 2) {
setLoadingState(true);
await new Promise(resolve => setTimeout(resolve, 140));
}
if (currentChangeCount === globalChangeCounter) {
setLoadingState(true);
const filteredResults = await fetchAndFilterScripts(input.value);
updateCurrentList(filteredResults);
setLoadingState(false);
}
}
function saveCache(url, text) {
url = fixURL(url);
if (!url) return;
text = LZString.compress(text || "");
text = text || ""
sessionStorage.setItem(`${storagePrefix}${url}`, text);
sessionStorage.setItem(`${storagePrefix}urls`, (sessionStorage.getItem(`${storagePrefix}urls`) || "") + "|" + url + "|");
}
function restoreCache(url) {
url = fixURL(url);
if (!url) return;
let text = LZString.decompress(sessionStorage.getItem(`${storagePrefix}${url}`) || "") || "";
return text;
}
async function fetchAndCacheScripts() {
if (busyCache > 0) return;
busyCache = 1;
const pages = Array.from(document.querySelectorAll(`#${sectionId} .pagination > a[href]`))
.map(link => link.getAttribute('href'));
let mMap = new Map();
for (const url of pages) {
mMap.set(fixURL(url), url)
}
for (const [fixedURL, pageURL] of mMap) {
const page = pageURL;
let url = fixedURL;
if (url && !sessionStorage.getItem(`${storagePrefix}${url}`)) {
const response = await fetch(page);
const text = await response.text();
console.log(123, Date.now(), page)
saveCache(page, text);
}
}
busyCache = 2;
}
async function fetchAndFilterScripts(inputValue) {
inputValue = inputValue || '';
inputValue = genericText(inputValue, 1 | 2 | 8);
let pages = Array.from(document.querySelectorAll(`#${sectionId} .pagination > a[href]`))
.map(link => link.getAttribute('href'));
let mMap = new Map();
for (const url of pages) {
mMap.set(fixURL(url), url)
}
const allScripts = [];
// Get current page's list from sessionStorage
const currentPageHtml = restoreCache(location.href);
const currentDoc = new DOMParser().parseFromString(currentPageHtml, 'text/html');
filterScripts(currentDoc, inputValue || true, allScripts);
for (const [fixedURL, pageURL] of mMap) {
let page = pageURL;
let pageHtml = restoreCache(page);
if (!pageHtml) {
const response = await fetch(page);
const text = await response.text();
console.log(456, Date.now(), page)
saveCache(page, text);
pageHtml = text;
}
const doc = new DOMParser().parseFromString(pageHtml, 'text/html');
filterScripts(doc, inputValue || false, allScripts);
}
return allScripts;
}
function filterScripts(doc, inputValue, allScripts) {
if (inputValue === false) return;
const scripts = doc.querySelectorAll(`ol#${listId} li[data-script-id]`);
scripts.forEach(li => {
const html = getContentHTML(li, inputValue);
if (html) allScripts.push(html);
});
}
function getContentHTML(li, inputValue) {
const name = genericText(li.querySelector('a.script-link[href]').textContent, 1 | 2 | 8);
const description = genericText(li.querySelector('.script-description').textContent, 1 | 2 | 8);
let text = name + `\n` + description;
if (inputValue === true || text.toLowerCase().includes(inputValue.toLowerCase())) {
return li.outerHTML;
}
}
function updateCurrentList(filteredResults) {
const list = document.querySelector(`ol#${listId}`);
if (list) {
list.innerHTML = filteredResults.join('');
}
}
};
hookToFn({
listId: 'user-script-list',
sectionId: 'user-script-list-section',
storagePrefix: 'gF_7H8TV_',
elementIdPrefix: 'gimoa-'
});
hookToFn({
listId: 'user-unlisted-script-list',
sectionId: 'user-unlisted-script-list-section',
storagePrefix: 'gF_84IUu_',
elementIdPrefix: 'jexsq-'
});
hookToFn({
listId: 'user-library-script-list',
sectionId: 'user-library-list-section',
storagePrefix: 'gF_39rrO_',
elementIdPrefix: 'm01xt-'
});