您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Automatically sort teaser images on /videos, /images, /subscriptions, /users, and sidebars using customizable sort function. Can load and sort multiple pages at once.
当前为
// ==UserScript== // @name Iwara Custom Sort // @name:ja Iwara Custom ソート // @version 0.196 // @grant GM.setValue // @grant GM.getValue // @grant GM.deleteValue // @run-at document-start // @match https://ecchi.iwara.tv/* // @match https://www.iwara.tv/* // @match http://ecchi.iwara.tv/* // @match http://www.iwara.tv/* // @description Automatically sort teaser images on /videos, /images, /subscriptions, /users, and sidebars using customizable sort function. Can load and sort multiple pages at once. // @description:ja / videos、/ images、/ subscriptions、/ usersとサイドバーのサムネイルを自動的にソートします。ソート方法はカスタマイズすることができます、一度に複数のページを読み込んでソートすることができます。 // @license AGPL-3.0-or-later // @namespace https://gf.qytechs.cn/users/245195 // ==/UserScript== /* jshint esversion: 6 */ /* global GM */ 'use strict'; const logDebug = (...args) => { const debugging = true; if (debugging) { console.log(...args); } }; const teaserDivSelector = '.node-teaser, .node-sidebar_teaser'; const getTeaserGrids = (node) => { const teaserGridSelector = '.views-responsive-grid'; return Array.from(node.querySelectorAll(teaserGridSelector)) .filter(grid => grid.querySelector(teaserDivSelector)); }; const timeout = delay => new Promise(resolve => setTimeout(resolve, delay)); const sortTeasers = (grid, valueExpression) => { const viewsIconSelector = '.glyphicon-eye-open'; const likesIconSelector = '.glyphicon-heart'; const imageFieldSelector = '.field-type-image'; const galleryIconSelector = '.glyphicon-th-large'; const privateDivSelector = '.private-video'; const teaserDivs = Array.from(grid.querySelectorAll(teaserDivSelector)); const getNearbyNumber = (element) => { const parsePrefixed = str => Number.parseFloat(str) * (str.includes('k') ? 1000 : 1); return element ? parsePrefixed(element.parentElement.textContent) : 0; }; const teaserItems = teaserDivs.map(div => ({ div, viewCount: getNearbyNumber(div.querySelector(viewsIconSelector)), likeCount: getNearbyNumber(div.querySelector(likesIconSelector)), imageFactor: div.querySelector(imageFieldSelector) ? 1 : 0, galleryFactor: div.querySelector(galleryIconSelector) ? 1 : 0, privateFactor: div.querySelector(privateDivSelector) ? 1 : 0, })); const evalSortValue = (item, expression) => // eslint-disable-next-line no-new-func new Function( 'views', 'likes', 'ratio', 'image', 'gallery', 'private', `return (${expression})`, )( item.viewCount, item.likeCount, Math.min(item.likeCount / Math.max(1, item.viewCount), 1), item.imageFactor, item.galleryFactor, item.privateFactor, ); teaserItems.forEach((item) => { // eslint-disable-next-line no-param-reassign item.sortValue = evalSortValue(item, valueExpression); }); teaserItems.sort((itemA, itemB) => itemB.sortValue - itemA.sortValue); teaserDivs.map((div) => { const anchor = document.createElement('div'); div.before(anchor); return anchor; }).forEach((div, index) => div.replaceWith(teaserItems[index].div)); }; const sortAllTeasers = (valueExpression) => { GM.setValue('sortValue', valueExpression); let sortedCount = 0; try { getTeaserGrids(document).forEach((grid) => { sortTeasers(grid, valueExpression); sortedCount += 1; }); } catch (message) { alert(message); } logDebug(`${sortedCount} grids sorted`); }; const getNumberParam = (URL, name) => { const params = URL.searchParams; return params.has(name) ? Number.parseInt(params.get(name)) : 0; }; const getPageParam = URL => getNumberParam(URL, 'page'); const currentPage = getPageParam(new URL(window.location)); const createAdditionalPages = (URL, additionalPageCount) => { const params = URL.searchParams; let page = getPageParam(URL); const pages = []; for (let pageLeft = additionalPageCount; pageLeft > 0; pageLeft -= 1) { page += 1; params.set('page', page); const nextPage = (() => (navigator.userAgent.indexOf('Firefox') > -1 ? document.createElement('embed') : document.createElement('iframe')) )(); nextPage.src = URL; nextPage.style.display = 'none'; logDebug('Add page:', nextPage.src); pages.push(nextPage); } return pages; }; const createTextInput = (text, maxLength, size) => { const input = document.createElement('input'); input.value = text; input.maxLength = maxLength; input.size = size; return input; }; const createButton = (text, clickHandler) => { const button = document.createElement('button'); button.innerHTML = text; button.addEventListener('click', clickHandler); return button; }; const createNumberInput = (value, min, max, step, width) => { const input = document.createElement('input'); Object.assign(input, { type: 'number', value, min, max, step, }); input.setAttribute('required', ''); input.style.width = width; return input; }; const createSpan = (text, color) => { const span = document.createElement('span'); span.innerHTML = text; span.style.color = color; return span; }; const createUI = async (pageCount) => { const lable1 = createSpan('1 of', 'white'); const pageCountInput = createNumberInput(pageCount, 1, 15, 1, '3em'); const lable2 = createSpan('pages loaded.', 'white'); pageCountInput.addEventListener('change', (event) => { GM.setValue('pageCount', Number.parseInt(event.target.value)); lable2.innerHTML = 'pages loaded. Refresh to apply the change'; }); const defaultValue = '(ratio / (private * 2.0 + 1) + Math.log(likes) / 250) / (image + 8.0)'; const sortValueInput = createTextInput(await GM.getValue('sortValue', defaultValue), 120, 60); sortValueInput.classList.add('form-text'); const sortButton = createButton('Sort', () => sortAllTeasers(sortValueInput.value)); sortButton.classList.add('btn', 'btn-sm', 'btn-primary'); sortValueInput.addEventListener('keyup', (event) => { if (event.key === 'Enter') { sortButton.click(); } }); const resetDefaultButton = createButton('Default', () => { sortValueInput.value = defaultValue; }); resetDefaultButton.classList.add('btn', 'btn-sm', 'btn-info'); return { lable1, pageCountInput, lable2, sortValueInput, sortButton, resetDefaultButton, }; }; const addUI = (UI) => { const UIDiv = document.createElement('div'); UIDiv.style.display = 'inline-block'; UIDiv.append( UI.sortValueInput, UI.resetDefaultButton, UI.sortButton, UI.lable1, UI.pageCountInput, UI.lable2, ); UIDiv.childNodes.forEach((node) => { // eslint-disable-next-line no-param-reassign node.style.margin = '5px 2px'; }); document.querySelector('#user-links').after(UIDiv); }; const addTeasersToParent = (teaserGrids) => { const parentGrids = getTeaserGrids(window.parent.document); for (let i = 0, j = 0; i < parentGrids.length; i += 1) { if (teaserGrids[j].className === parentGrids[i].className) { // eslint-disable-next-line no-param-reassign teaserGrids[j].className = ''; teaserGrids[j].setAttribute('originPage', currentPage); parentGrids[i].prepend(teaserGrids[j]); j += 1; } } }; const adjustPageAnchors = (container, pageCount) => { const changePageParam = (anchor, value) => { const anchorURL = new URL(anchor.href, window.location); anchorURL.searchParams.set('page', value); // eslint-disable-next-line no-param-reassign anchor.href = anchorURL.pathname + anchorURL.search; }; if (currentPage > 0) { const previousPageAnchor = container.querySelector('.pager-previous a'); changePageParam(previousPageAnchor, Math.max(0, currentPage - pageCount)); } const nextPage = currentPage + pageCount; { const lastPageAnchor = container.querySelector('.pager-last a'); const nextPageAnchor = container.querySelector('.pager-next a'); if (nextPageAnchor) { changePageParam(nextPageAnchor, nextPage); } if (lastPageAnchor) { if (getPageParam(new URL(lastPageAnchor.href, window.location)) < nextPage) { nextPageAnchor.remove(); lastPageAnchor.remove(); } } } const loadedPageAnchors = Array.from(container.querySelectorAll('.pager-item a')) .filter((anchor) => { const page = getPageParam(new URL(anchor.href, window.location)); return page >= currentPage && page < nextPage; }); if (loadedPageAnchors.length > 0) { const parentItem = document.createElement('li'); const groupList = document.createElement('ul'); groupList.style.display = 'inline'; groupList.style.backgroundColor = 'hsla(0, 0%, 75%, 50%)'; loadedPageAnchors[0].parentElement.before(parentItem); const currentPageItem = container.querySelector('.pager-current'); currentPageItem.style.marginLeft = '0'; groupList.append(currentPageItem); loadedPageAnchors.forEach((anchor) => { anchor.parentNode.classList.remove('pager-item'); anchor.parentNode.classList.add('pager-current'); groupList.append(anchor.parentElement); }); parentItem.append(groupList); } }; const adjustAnchors = (pageCount) => { const pageAnchorList = document.querySelectorAll('.pager'); pageAnchorList.forEach((list) => { adjustPageAnchors(list, pageCount); }); }; const initParent = async (teasersAddedMeesage) => { const pageCount = await GM.getValue('pageCount', 1); const UI = await createUI(pageCount); addUI(UI); let pages = []; if (document.querySelectorAll('.pager').length > 0) { pages = createAdditionalPages(new URL(window.location), pageCount - 1); document.body.append(...pages); logDebug(pages); adjustAnchors(pageCount); } let loadedPageCount = 1; window.addEventListener('message', (event) => { if ( new URL(event.origin).hostname === window.location.hostname && event.data === teasersAddedMeesage ) { sortAllTeasers(UI.sortValueInput.value); const loadedPage = pages[ getPageParam(new URL(event.source.location)) - currentPage - 1 ]; loadedPage.src = ''; loadedPage.remove(); loadedPageCount += 1; UI.lable1.innerHTML = `${loadedPageCount} of `; } }); UI.sortButton.click(); }; const init = async () => { try { const teaserGrids = getTeaserGrids(document); if (teaserGrids.length === 0) { return; } const teasersAddedMeesage = 'iwara custom sort: teasersAdded'; if (window === window.parent) { logDebug('I am a Parent.'); initParent(teasersAddedMeesage); } else { logDebug('I am a child.', window.location, window.parent.location); await timeout(500); addTeasersToParent(teaserGrids); window.parent.postMessage(teasersAddedMeesage, window.location.origin); } } catch (error) { logDebug(error); } }; logDebug(`Parsed:${window.location}, ${document.readyState} Parent:`, window.parent); document.addEventListener('DOMContentLoaded', init);
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址