Brazen Paginator

Helper for client side customized pagination

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/424499/1114815/Brazen%20Paginator.js

  1. // ==UserScript==
  2. // @name Brazen Paginator
  3. // @namespace brazenvoid
  4. // @version 2.0.0
  5. // @author brazenvoid
  6. // @license GPL-3.0-only
  7. // @description Helper for client side customized pagination
  8. // ==/UserScript==
  9.  
  10. class BrazenPaginator
  11. {
  12. /**
  13. * @typedef {{itemListSelector: JQuery.Selector, itemSelectors?: JQuery.Selector, lastPageUrl: string,
  14. * onGetPageNoFromUrl: PaginatorGetPageNoFromUrlHandler, onGetPageUrlFromPageNo: PaginatorGetPageUrlFromPageNoHandler,
  15. * onGetPaginationElementForPageNo: PaginatorGetPaginationElementForPageNoHandler, paginationWrapper: JQuery}} PaginatorConfiguration
  16. */
  17. /**
  18. * @callback PaginatorAfterPaginationEventHandler
  19. * @param {BrazenPaginator} paginator
  20. */
  21.  
  22. /**
  23. * @callback PaginatorGetPageNoFromUrlHandler
  24. * @param {string} pageUrl
  25. * @param {BrazenPaginator} paginator
  26. */
  27.  
  28. /**
  29. * @callback PaginatorGetPageUrlFromPageNoHandler
  30. * @param {number} pageNo
  31. * @param {BrazenPaginator} paginator
  32. */
  33.  
  34. /**
  35. * @callback PaginatorGetPaginationElementForPageNoHandler
  36. * @param {number} pageNo
  37. * @param {BrazenPaginator} paginator
  38. */
  39.  
  40. /**
  41. * @param {PaginatorConfiguration} configuration
  42. */
  43. constructor (configuration)
  44. {
  45. /**
  46. * @type {PaginatorConfiguration}
  47. * @private
  48. */
  49. this._config = configuration
  50. /**
  51. * @type {number}
  52. * @private
  53. */
  54. this._currentPageNo = 0
  55.  
  56. /**
  57. * @type {number}
  58. * @private
  59. */
  60. this._lastPageNo = 0
  61.  
  62. /**
  63. * @type {boolean}
  64. * @private
  65. */
  66. this._pageConcatenated = false
  67.  
  68. /**
  69. * @type {number}
  70. * @private
  71. */
  72. this._paginatedPageNo = 0
  73.  
  74. /**
  75. * @type {JQuery}
  76. * @private
  77. */
  78. this._targetElement = null
  79.  
  80. // Events and callbacks
  81.  
  82. /**
  83. * @type {PaginatorAfterPaginationEventHandler}
  84. * @private
  85. */
  86. this._onAfterPagination = null
  87. }
  88.  
  89. _conformUIToNewPaginatedState ()
  90. {
  91. if (this._pageConcatenated) {
  92. this._pageConcatenated = false
  93.  
  94. let currentPageElement = this.getPaginationElementForPageNo(this._currentPageNo)
  95. let newSubsequentPageNo = this._paginatedPageNo + 1
  96. let newSubsequentPageNoUrl = this.getPageUrlFromPageNo(newSubsequentPageNo)
  97.  
  98. // Mutate current page no element to show paginated page numbers
  99.  
  100. currentPageElement.text(this._currentPageNo + '-' + this._paginatedPageNo)
  101.  
  102. // Get next pages' pagination elements
  103.  
  104. let currentNextPageElements = currentPageElement.nextAll()
  105.  
  106. if (this._paginatedPageNo === this._lastPageNo) {
  107.  
  108. // Delete all pagination elements if last page is paginated
  109.  
  110. currentNextPageElements.remove()
  111.  
  112. } else {
  113.  
  114. // Determine whether the paginated page immediately precedes the last page
  115.  
  116. if (newSubsequentPageNo !== this._lastPageNo) {
  117.  
  118. // If not so, determine whether pagination element for the page following the paginated page exists
  119.  
  120. let newSubsequentPageElement = this.getPaginationElementForPageNo(newSubsequentPageNo)
  121. if (!newSubsequentPageElement.length) {
  122.  
  123. // If it does not exist then try getting the old next page no element
  124.  
  125. let oldSubsequentPageElement = this.getPaginationElementForPageNo(this._currentPageNo + 1)
  126. if (oldSubsequentPageElement.length) {
  127.  
  128. // If it does exist then mutate it for this purpose
  129.  
  130. oldSubsequentPageElement.attr('href', newSubsequentPageNoUrl).text(newSubsequentPageNo)
  131.  
  132. } else {
  133.  
  134. // If even that does not exist, then clone the less desirable alternative; the last page element and mutate it to this use
  135.  
  136. let lastPageElement = this.getPaginationElementForPageNo(this._lastPageNo)
  137. lastPageElement.clone().insertAfter(currentPageElement).attr('href', newSubsequentPageNoUrl).text(newSubsequentPageNo)
  138.  
  139. }
  140. }
  141.  
  142. // Remove any other pagination elements for already paginated pages
  143.  
  144. currentNextPageElements.each((index, element) => {
  145. let paginationLink = $(element)
  146. let paginationLinkUrl = paginationLink.attr('href')
  147. if (paginationLinkUrl && this.getPageNoFromUrl(paginationLinkUrl) <= this._paginatedPageNo) {
  148. paginationLink.remove()
  149. }
  150. })
  151. }
  152. }
  153. Utilities.callEventHandler(this._onAfterPagination, [this])
  154. }
  155. }
  156.  
  157. /**
  158. * @param {number} threshold
  159. * @param {number} limit
  160. * @private
  161. */
  162. _loadAndParseNextPage (threshold, limit)
  163. {
  164. let lastPageHasNotBeenReached = this._paginatedPageNo < this._lastPageNo
  165. let paginationLimitHasNotBeenMet = limit > 0 && (this._paginatedPageNo - this._currentPageNo) < limit
  166. let compliantItemsAreLessThanTheThreshold =
  167. this._targetElement.find(this._config.itemSelectors + ':not(.noncompliant-item)').length < threshold
  168.  
  169. if (lastPageHasNotBeenReached && paginationLimitHasNotBeenMet && compliantItemsAreLessThanTheThreshold) {
  170.  
  171. this._sandbox.load(this.getPageUrlFromPageNo(++this._paginatedPageNo) + ' ' + this._config.itemListSelector, '', () => {
  172. this._pageConcatenated = true
  173. this._sandbox.find(this._config.itemSelectors).insertAfter(this._targetElement.find(this._config.itemSelectors + ':last'))
  174. this._sandbox.empty()
  175. })
  176. } else {
  177. this._conformUIToNewPaginatedState()
  178. }
  179. }
  180.  
  181. getCurrentPageNo ()
  182. {
  183. return this._currentPageNo
  184. }
  185.  
  186. getItemListSelector ()
  187. {
  188. return this._config.itemListSelector
  189. }
  190.  
  191. getLastPageNo ()
  192. {
  193. return this._lastPageNo
  194. }
  195.  
  196. /**
  197. * @param {string} pageUrl
  198. * @return {number}
  199. */
  200. getPageNoFromUrl (pageUrl)
  201. {
  202. return this._config.onGetPageNoFromUrl(pageUrl , this)
  203. }
  204.  
  205. /**
  206. * @param {number} pageNo
  207. * @return {string}
  208. */
  209. getPageUrlFromPageNo (pageNo)
  210. {
  211. return this._config.onGetPageUrlFromPageNo(pageNo, this)
  212. }
  213.  
  214. getPaginatedPageNo ()
  215. {
  216. return this._paginatedPageNo
  217. }
  218.  
  219. /**
  220. * @param {number} pageNo
  221. * @return {JQuery}
  222. */
  223. getPaginationElementForPageNo (pageNo)
  224. {
  225. return this._config.onGetPaginationElementForPageNo(pageNo, this)
  226. }
  227.  
  228. getPaginationWrapper ()
  229. {
  230. return this._config.paginationWrapper
  231. }
  232.  
  233. initialize ()
  234. {
  235. this._currentPageNo = this.getPageNoFromUrl(window.location.href)
  236. this._lastPageNo = this.getPageNoFromUrl(this._config.lastPageUrl)
  237. this._paginatedPageNo = this._currentPageNo
  238. this._sandbox = $('<div id="brazen-paginator-sandbox" hidden/>').appendTo('body')
  239. this._targetElement = $(this._config.itemListSelector + ':first')
  240. return this
  241. }
  242.  
  243. /**
  244. * @param {PaginatorAfterPaginationEventHandler} handler
  245. * @return {this}
  246. */
  247. onAfterPagination (handler)
  248. {
  249. this._onAfterPagination = handler
  250. return this
  251. }
  252.  
  253. run (threshold, limit)
  254. {
  255. if (this._config.paginationWrapper.length && threshold) {
  256. this._loadAndParseNextPage(threshold, limit)
  257. }
  258. return this
  259. }
  260. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址