您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Helper for client side customized pagination
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/424499/1580019/Brazen%20Paginator.js
// ==UserScript== // @name Brazen Paginator // @namespace brazenvoid // @version 2.0.1 // @author brazenvoid // @license GPL-3.0-only // @description Helper for client side customized pagination // ==/UserScript== class BrazenPaginator { /** * @typedef {{itemListSelector: JQuery.Selector, itemSelectors?: JQuery.Selector, lastPageUrl: string, * onGetPageNoFromUrl: PaginatorGetPageNoFromUrlHandler, onGetPageUrlFromPageNo: PaginatorGetPageUrlFromPageNoHandler, * onGetPaginationElementForPageNo: PaginatorGetPaginationElementForPageNoHandler, paginationWrapper: JQuery}} PaginatorConfiguration */ /** * @callback PaginatorAfterPaginationEventHandler * @param {BrazenPaginator} paginator */ /** * @callback PaginatorGetPageNoFromUrlHandler * @param {string} pageUrl * @param {BrazenPaginator} paginator */ /** * @callback PaginatorGetPageUrlFromPageNoHandler * @param {number} pageNo * @param {BrazenPaginator} paginator */ /** * @callback PaginatorGetPaginationElementForPageNoHandler * @param {number} pageNo * @param {BrazenPaginator} paginator */ /** * @param {PaginatorConfiguration} configuration */ constructor (configuration) { /** * @type {PaginatorConfiguration} * @private */ this._config = configuration /** * @type {number} * @private */ this._currentPageNo = 0 /** * @type {number} * @private */ this._lastPageNo = 0 /** * @type {boolean} * @private */ this._pageConcatenated = false /** * @type {number} * @private */ this._paginatedPageNo = 0 /** * @type {JQuery} * @private */ this._targetElement = null // Events and callbacks /** * @type {PaginatorAfterPaginationEventHandler} * @private */ this._onAfterPagination = null } _conformUIToNewPaginatedState () { if (this._pageConcatenated) { this._pageConcatenated = false let currentPageElement = this.getPaginationElementForPageNo(this._currentPageNo) let newSubsequentPageNo = this._paginatedPageNo + 1 let newSubsequentPageNoUrl = this.getPageUrlFromPageNo(newSubsequentPageNo) // Mutate current page no element to show paginated page numbers currentPageElement.text(this._currentPageNo + '-' + this._paginatedPageNo) // Get next pages' pagination elements let currentNextPageElements = currentPageElement.nextAll() if (this._paginatedPageNo === this._lastPageNo) { // Delete all pagination elements if last page is paginated currentNextPageElements.remove() } else { // Determine whether the paginated page immediately precedes the last page if (newSubsequentPageNo !== this._lastPageNo) { // If not so, determine whether pagination element for the page following the paginated page exists let newSubsequentPageElement = this.getPaginationElementForPageNo(newSubsequentPageNo) if (!newSubsequentPageElement.length) { // If it does not exist then try getting the old next page no element let oldSubsequentPageElement = this.getPaginationElementForPageNo(this._currentPageNo + 1) if (oldSubsequentPageElement.length) { // If it does exist then mutate it for this purpose oldSubsequentPageElement.attr('href', newSubsequentPageNoUrl).text(newSubsequentPageNo) } else { // If even that does not exist, then clone the less desirable alternative; the last page element and mutate it to this use let lastPageElement = this.getPaginationElementForPageNo(this._lastPageNo) lastPageElement.clone().insertAfter(currentPageElement).attr('href', newSubsequentPageNoUrl).text(newSubsequentPageNo) } } // Remove any other pagination elements for already paginated pages currentNextPageElements.each((index, element) => { let paginationLink = $(element) let paginationLinkUrl = paginationLink.attr('href') if (paginationLinkUrl && this.getPageNoFromUrl(paginationLinkUrl) <= this._paginatedPageNo) { paginationLink.remove() } }) } } Utilities.callEventHandler(this._onAfterPagination, [this]) } } /** * @param {number} threshold * @param {number} limit * @private */ _loadAndParseNextPage (threshold, limit) { let lastPageHasNotBeenReached = this._paginatedPageNo < this._lastPageNo let paginationLimitHasNotBeenMet = limit > 0 && (this._paginatedPageNo - this._currentPageNo) < limit let compliantItemsAreLessThanTheThreshold = this._targetElement.find(this._config.itemSelectors + ':not(.' + CLASS_NON_COMPLIANT_ITEM + ')').length < threshold if (lastPageHasNotBeenReached && paginationLimitHasNotBeenMet && compliantItemsAreLessThanTheThreshold) { this._sandbox.load(this.getPageUrlFromPageNo(++this._paginatedPageNo) + ' ' + this._config.itemListSelector, '', () => { this._pageConcatenated = true this._sandbox.find(this._config.itemSelectors).insertAfter(this._targetElement.find(this._config.itemSelectors + ':last')) this._sandbox.empty() }) } else { this._conformUIToNewPaginatedState() } } getCurrentPageNo () { return this._currentPageNo } getItemListSelector () { return this._config.itemListSelector } getLastPageNo () { return this._lastPageNo } /** * @param {string} pageUrl * @return {number} */ getPageNoFromUrl (pageUrl) { return this._config.onGetPageNoFromUrl(pageUrl , this) } /** * @param {number} pageNo * @return {string} */ getPageUrlFromPageNo (pageNo) { return this._config.onGetPageUrlFromPageNo(pageNo, this) } getPaginatedPageNo () { return this._paginatedPageNo } /** * @param {number} pageNo * @return {JQuery} */ getPaginationElementForPageNo (pageNo) { return this._config.onGetPaginationElementForPageNo(pageNo, this) } getPaginationWrapper () { return this._config.paginationWrapper } initialize () { this._currentPageNo = this.getPageNoFromUrl(window.location.href) this._lastPageNo = this.getPageNoFromUrl(this._config.lastPageUrl) this._paginatedPageNo = this._currentPageNo this._sandbox = $('<div id="brazen-paginator-sandbox" hidden/>').appendTo('body') this._targetElement = $(this._config.itemListSelector + ':first') return this } /** * @param {PaginatorAfterPaginationEventHandler} handler * @return {this} */ onAfterPagination (handler) { this._onAfterPagination = handler return this } run (threshold, limit) { if (this._config.paginationWrapper.length && threshold) { this._loadAndParseNextPage(threshold, limit) } return this } }