JAVBUS larger thumbnails test

replace thumbnails of javbus,javdb,javlibrary and avmoo with source images

  1. // ==UserScript==
  2. // @name JAVBUS larger thumbnails test
  3. // @name:zh-CN JAVBUS封面大图 测试
  4. // @namespace https://github.com/kygo233/tkjs
  5. // @homepage https://sleazyfork.org/zh-CN/scripts/409874-javbus-larger-thumbnails-test
  6. // @version 20250311
  7. // @author kygo233
  8. // @license MIT
  9. // @description replace thumbnails of javbus,javdb,javlibrary and avmoo with source images
  10. // @description:zh-CN javbus,javdb,javlibrary,avmoo替换封面为源图
  11.  
  12. // @include *javbus.com/*
  13. // @include *javdb.com/*
  14. // @include *avmoo.cyou/*
  15. // @include *javlibrary.com/*
  16. // @include /^.*(javbus|busjav|busfan|fanbus|buscdn|cdnbus|dmmsee|seedmm|busdmm|dmmbus|javsee|seejav)\..*$/
  17. // @include /^.*(javdb)[0-9]*\..*$/
  18. // @include /^.*(avmoo)\..*$/
  19.  
  20. // @grant GM_addStyle
  21. // @grant GM_xmlhttpRequest
  22. // @grant GM_getValue
  23. // @grant GM_setValue
  24. // @grant GM_download
  25. // @grant GM_setClipboard
  26. // @connect *
  27.  
  28. // 2025-03-11 修复视频截图的报错
  29. // 2025-02-15 调整部分大图的替换规则siro-5418
  30. // 2024-06-29 修复图片下载失败的问题;新增更新内容通知弹窗
  31. // 2022-09-18 修复视频截图报错
  32. // 2022-05-26 调整lazyload插件为本地加载; 加载插件方法loadJS增加备用源
  33. // 2022-04-29 适配javdb的新页面; 查看视频截图: 增加blogjav的防攻击跳转提示
  34. // 2022-04-17 调整javdb的磁力元素选择器;查看视频截图:显示所有的结果
  35. // 2022-03-28 匹配dmmbus;修复标题不可点击的bug;屏蔽词:支持逗号和单个作品,调整界面到右下角
  36. // 2022-03-18 修复欧美区磁力按钮打开重复的问题;javlibrary添加将左侧菜单上移的功能
  37. // 2022-03-04 新增屏蔽词功能
  38. // 2022-03-03 调整设置按钮到左上角;删除javdb磁力列表里的广告
  39. // 2021-10-07 调整下载界面样式;下载文件名调整为番号+标题
  40. // 2021-09-03 匹配javdb更多网址 例如javdb30
  41. // 2021-08-18 调整blogjav视频截图获取方法
  42. // 2021-06-07 添加封面图片的批量下载功能
  43. // 2021-06-03 修复javdb磁力弹窗预告片播放bug;番号变成可点击
  44. // 2021-06-01 修复多列布局下 图片样式失效的问题
  45. // 2021-05-31 JavDb添加磁力功能;解决已点击链接颜色失效问题;对大于标准宽高比的图片进行缩放;
  46. // 2021-05-06 适配javlibrary;添加标题全显样式控制;自动翻页开关无需刷新页面;删除高清图标的显示控制
  47. // 2021-04-04 适配JAVDB;点击图片弹出新窗口;标题默认显示一行;调整样式;增加英文显示
  48. // 2021-03-09 恢复高清字幕图标的显示
  49. // 2021-02-06 新增图片懒加载插件;重调样式;优化按钮效果,切换样式不刷新页面;磁力界面新增演员表样品图显示;
  50. // 2021-01-18 适配AVMOO网站;无码页面屏蔽竖图模式;调整域名匹配规则
  51. // 2021-01-01 新增宽度调整功能;
  52. // 2020-12-29 解决半图模式下 竖图显示不全的问题;
  53. // 2020-10-16 解决功能开关取默认值为undefined的bug
  54. // 2020-10-16 解决和"JAV老司机"同时运行时样式冲突问题,需关闭老司机的瀑布流
  55. // 2020-10-14 收藏界面只匹配影片;下载图片文件名添加标题;新增复制番号、标题功能;视频截图文件下载;封面显示半图;增加样式开关
  56. // 2020-09-20 收藏界面的适配
  57. // 2020-08-27 适配更多界面
  58. // 2020-08-26 修复查询结果为1个时,item宽度为100%的问题
  59. // 2020-08-26 添加瀑布流
  60. // 2020-08-24 第一版:封面大图、下载封面、查看视频截图
  61. // ==/UserScript==
  62.  
  63. (function () {
  64. 'use strict';
  65. // @require https://cdn.jsdelivr.net/npm/vanilla-lazyload@17.8.2/dist/lazyload.min.js
  66. !function(n,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(n="undefined"!=typeof globalThis?globalThis:n||self).LazyLoad=t()}(this,(function(){"use strict";function n(){return n=Object.assign||function(n){for(var t=1;t<arguments.length;t++){var e=arguments[t];for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&(n[i]=e[i])}return n},n.apply(this,arguments)}var t="undefined"!=typeof window,e=t&&!("onscroll"in window)||"undefined"!=typeof navigator&&/(gle|ing|ro)bot|crawl|spider/i.test(navigator.userAgent),i=t&&"IntersectionObserver"in window,o=t&&"classList"in document.createElement("p"),a=t&&window.devicePixelRatio>1,r={elements_selector:".lazy",container:e||t?document:null,threshold:300,thresholds:null,data_src:"src",data_srcset:"srcset",data_sizes:"sizes",data_bg:"bg",data_bg_hidpi:"bg-hidpi",data_bg_multi:"bg-multi",data_bg_multi_hidpi:"bg-multi-hidpi",data_bg_set:"bg-set",data_poster:"poster",class_applied:"applied",class_loading:"loading",class_loaded:"loaded",class_error:"error",class_entered:"entered",class_exited:"exited",unobserve_completed:!0,unobserve_entered:!1,cancel_on_exit:!0,callback_enter:null,callback_exit:null,callback_applied:null,callback_loading:null,callback_loaded:null,callback_error:null,callback_finish:null,callback_cancel:null,use_native:!1,restore_on_error:!1},c=function(t){return n({},r,t)},l=function(n,t){var e,i="LazyLoad::Initialized",o=new n(t);try{e=new CustomEvent(i,{detail:{instance:o}})}catch(n){(e=document.createEvent("CustomEvent")).initCustomEvent(i,!1,!1,{instance:o})}window.dispatchEvent(e)},u="src",s="srcset",d="sizes",f="poster",_="llOriginalAttrs",g="data",v="loading",b="loaded",m="applied",p="error",h="native",E="data-",I="ll-status",y=function(n,t){return n.getAttribute(E+t)},k=function(n){return y(n,I)},w=function(n,t){return function(n,t,e){var i="data-ll-status";null!==e?n.setAttribute(i,e):n.removeAttribute(i)}(n,0,t)},A=function(n){return w(n,null)},L=function(n){return null===k(n)},O=function(n){return k(n)===h},x=[v,b,m,p],C=function(n,t,e,i){n&&(void 0===i?void 0===e?n(t):n(t,e):n(t,e,i))},N=function(n,t){o?n.classList.add(t):n.className+=(n.className?" ":"")+t},M=function(n,t){o?n.classList.remove(t):n.className=n.className.replace(new RegExp("(^|\\s+)"+t+"(\\s+|$)")," ").replace(/^\s+/,"").replace(/\s+$/,"")},z=function(n){return n.llTempImage},T=function(n,t){if(t){var e=t._observer;e&&e.unobserve(n)}},R=function(n,t){n&&(n.loadingCount+=t)},G=function(n,t){n&&(n.toLoadCount=t)},j=function(n){for(var t,e=[],i=0;t=n.children[i];i+=1)"SOURCE"===t.tagName&&e.push(t);return e},D=function(n,t){var e=n.parentNode;e&&"PICTURE"===e.tagName&&j(e).forEach(t)},H=function(n,t){j(n).forEach(t)},V=[u],F=[u,f],B=[u,s,d],J=[g],P=function(n){return!!n[_]},S=function(n){return n[_]},U=function(n){return delete n[_]},$=function(n,t){if(!P(n)){var e={};t.forEach((function(t){e[t]=n.getAttribute(t)})),n[_]=e}},q=function(n,t){if(P(n)){var e=S(n);t.forEach((function(t){!function(n,t,e){e?n.setAttribute(t,e):n.removeAttribute(t)}(n,t,e[t])}))}},K=function(n,t,e){N(n,t.class_applied),w(n,m),e&&(t.unobserve_completed&&T(n,t),C(t.callback_applied,n,e))},Q=function(n,t,e){N(n,t.class_loading),w(n,v),e&&(R(e,1),C(t.callback_loading,n,e))},W=function(n,t,e){e&&n.setAttribute(t,e)},X=function(n,t){W(n,d,y(n,t.data_sizes)),W(n,s,y(n,t.data_srcset)),W(n,u,y(n,t.data_src))},Y={IMG:function(n,t){D(n,(function(n){$(n,B),X(n,t)})),$(n,B),X(n,t)},IFRAME:function(n,t){$(n,V),W(n,u,y(n,t.data_src))},VIDEO:function(n,t){H(n,(function(n){$(n,V),W(n,u,y(n,t.data_src))})),$(n,F),W(n,f,y(n,t.data_poster)),W(n,u,y(n,t.data_src)),n.load()},OBJECT:function(n,t){$(n,J),W(n,g,y(n,t.data_src))}},Z=["IMG","IFRAME","VIDEO","OBJECT"],nn=function(n,t){!t||function(n){return n.loadingCount>0}(t)||function(n){return n.toLoadCount>0}(t)||C(n.callback_finish,t)},tn=function(n,t,e){n.addEventListener(t,e),n.llEvLisnrs[t]=e},en=function(n,t,e){n.removeEventListener(t,e)},on=function(n){return!!n.llEvLisnrs},an=function(n){if(on(n)){var t=n.llEvLisnrs;for(var e in t){var i=t[e];en(n,e,i)}delete n.llEvLisnrs}},rn=function(n,t,e){!function(n){delete n.llTempImage}(n),R(e,-1),function(n){n&&(n.toLoadCount-=1)}(e),M(n,t.class_loading),t.unobserve_completed&&T(n,e)},cn=function(n,t,e){var i=z(n)||n;on(i)||function(n,t,e){on(n)||(n.llEvLisnrs={});var i="VIDEO"===n.tagName?"loadeddata":"load";tn(n,i,t),tn(n,"error",e)}(i,(function(o){!function(n,t,e,i){var o=O(t);rn(t,e,i),N(t,e.class_loaded),w(t,b),C(e.callback_loaded,t,i),o||nn(e,i)}(0,n,t,e),an(i)}),(function(o){!function(n,t,e,i){var o=O(t);rn(t,e,i),N(t,e.class_error),w(t,p),C(e.callback_error,t,i),e.restore_on_error&&q(t,B),o||nn(e,i)}(0,n,t,e),an(i)}))},ln=function(n,t,e){!function(n){return Z.indexOf(n.tagName)>-1}(n)?function(n,t,e){!function(n){n.llTempImage=document.createElement("IMG")}(n),cn(n,t,e),function(n){P(n)||(n[_]={backgroundImage:n.style.backgroundImage})}(n),function(n,t,e){var i=y(n,t.data_bg),o=y(n,t.data_bg_hidpi),r=a&&o?o:i;r&&(n.style.backgroundImage='url("'.concat(r,'")'),z(n).setAttribute(u,r),Q(n,t,e))}(n,t,e),function(n,t,e){var i=y(n,t.data_bg_multi),o=y(n,t.data_bg_multi_hidpi),r=a&&o?o:i;r&&(n.style.backgroundImage=r,K(n,t,e))}(n,t,e),function(n,t,e){var i=y(n,t.data_bg_set);if(i){var o=i.split("|"),a=o.map((function(n){return"image-set(".concat(n,")")}));n.style.backgroundImage=a.join(),""===n.style.backgroundImage&&(a=o.map((function(n){return"-webkit-image-set(".concat(n,")")})),n.style.backgroundImage=a.join()),K(n,t,e)}}(n,t,e)}(n,t,e):function(n,t,e){cn(n,t,e),function(n,t,e){var i=Y[n.tagName];i&&(i(n,t),Q(n,t,e))}(n,t,e)}(n,t,e)},un=function(n){n.removeAttribute(u),n.removeAttribute(s),n.removeAttribute(d)},sn=function(n){D(n,(function(n){q(n,B)})),q(n,B)},dn={IMG:sn,IFRAME:function(n){q(n,V)},VIDEO:function(n){H(n,(function(n){q(n,V)})),q(n,F),n.load()},OBJECT:function(n){q(n,J)}},fn=function(n,t){(function(n){var t=dn[n.tagName];t?t(n):function(n){if(P(n)){var t=S(n);n.style.backgroundImage=t.backgroundImage}}(n)})(n),function(n,t){L(n)||O(n)||(M(n,t.class_entered),M(n,t.class_exited),M(n,t.class_applied),M(n,t.class_loading),M(n,t.class_loaded),M(n,t.class_error))}(n,t),A(n),U(n)},_n=["IMG","IFRAME","VIDEO"],gn=function(n){return n.use_native&&"loading"in HTMLImageElement.prototype},vn=function(n,t,e){n.forEach((function(n){return function(n){return n.isIntersecting||n.intersectionRatio>0}(n)?function(n,t,e,i){var o=function(n){return x.indexOf(k(n))>=0}(n);w(n,"entered"),N(n,e.class_entered),M(n,e.class_exited),function(n,t,e){t.unobserve_entered&&T(n,e)}(n,e,i),C(e.callback_enter,n,t,i),o||ln(n,e,i)}(n.target,n,t,e):function(n,t,e,i){L(n)||(N(n,e.class_exited),function(n,t,e,i){e.cancel_on_exit&&function(n){return k(n)===v}(n)&&"IMG"===n.tagName&&(an(n),function(n){D(n,(function(n){un(n)})),un(n)}(n),sn(n),M(n,e.class_loading),R(i,-1),A(n),C(e.callback_cancel,n,t,i))}(n,t,e,i),C(e.callback_exit,n,t,i))}(n.target,n,t,e)}))},bn=function(n){return Array.prototype.slice.call(n)},mn=function(n){return n.container.querySelectorAll(n.elements_selector)},pn=function(n){return function(n){return k(n)===p}(n)},hn=function(n,t){return function(n){return bn(n).filter(L)}(n||mn(t))},En=function(n,e){var o=c(n);this._settings=o,this.loadingCount=0,function(n,t){i&&!gn(n)&&(t._observer=new IntersectionObserver((function(e){vn(e,n,t)}),function(n){return{root:n.container===document?null:n.container,rootMargin:n.thresholds||n.threshold+"px"}}(n)))}(o,this),function(n,e){t&&(e._onlineHandler=function(){!function(n,t){var e;(e=mn(n),bn(e).filter(pn)).forEach((function(t){M(t,n.class_error),A(t)})),t.update()}(n,e)},window.addEventListener("online",e._onlineHandler))}(o,this),this.update(e)};return En.prototype={update:function(n){var t,o,a=this._settings,r=hn(n,a);G(this,r.length),!e&&i?gn(a)?function(n,t,e){n.forEach((function(n){-1!==_n.indexOf(n.tagName)&&function(n,t,e){n.setAttribute("loading","lazy"),cn(n,t,e),function(n,t){var e=Y[n.tagName];e&&e(n,t)}(n,t),w(n,h)}(n,t,e)})),G(e,0)}(r,a,this):(o=r,function(n){n.disconnect()}(t=this._observer),function(n,t){t.forEach((function(t){n.observe(t)}))}(t,o)):this.loadAll(r)},destroy:function(){this._observer&&this._observer.disconnect(),t&&window.removeEventListener("online",this._onlineHandler),mn(this._settings).forEach((function(n){U(n)})),delete this._observer,delete this._settings,delete this._onlineHandler,delete this.loadingCount,delete this.toLoadCount},loadAll:function(n){var t=this,e=this._settings;hn(n,e).forEach((function(n){T(n,t),ln(n,e,t)}))},restoreAll:function(){var n=this._settings;mn(n).forEach((function(t){fn(t,n)}))}},En.load=function(n,t){var e=c(t);ln(n,e)},En.resetStatus=function(n){A(n)},t&&function(n,t){if(t)if(t.length)for(var e,i=0;e=t[i];i+=1)l(n,e);else l(n,t)}(En,window.lazyLoadOptions),En}));
  67.  
  68. let statusDefault = {
  69. autoPage: false,
  70. copyBtn :true,
  71. toolBar: true,
  72. avInfo:true,
  73. halfImg:false,
  74. fullTitle:false,
  75. waterfallWidth:100,
  76. columnNumFull:3,
  77. columnNumHalf:4,
  78. menutoTop : false,
  79. hiddenWord :[],
  80. hiddenAvid :[]
  81. };
  82. const VERSION = "20250311";
  83. const NOTICE = "2025-03-11 修复视频截图的报错";
  84. const SCREENSHOT_SUFFIX = "-screenshot-tag";
  85. const AVINFO_SUFFIX = "-avInfo-tag";
  86. const blogjavSelector= "h2.entry-title>a";
  87. const fullImgCSS=`width: 100%!important;height:100%!important;`;
  88. const halfImgCSS=`position: relative;left: -112%;width: 212% !important;height: 100% !important;max-width: 212%;`;
  89.  
  90. const copy_Svg = `<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" width="16" height="16" viewBox="0 0 16 16"><path d="M2 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v13.5a.5.5 0 0 1-.777.416L8 13.101l-5.223 2.815A.5.5 0 0 1 2 15.5V2zm2-1a1 1 0 0 0-1 1v12.566l4.723-2.482a.5.5 0 0 1 .554 0L13 14.566V2a1 1 0 0 0-1-1H4z"/></svg>`;
  91. const download_Svg = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" class="tool-svg" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8.5 4.5a.5.5 0 0 0-1 0v5.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V4.5z"/></svg>`;
  92. const picture_Svg = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" class="tool-svg" viewBox="0 0 16 16"><path d="M6.002 5.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z"/><path d="M2.002 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2h-12zm12 1a1 1 0 0 1 1 1v6.5l-3.777-1.947a.5.5 0 0 0-.577.093l-3.71 3.71-2.66-1.772a.5.5 0 0 0-.63.062L1.002 12V3a1 1 0 0 1 1-1h12z"/></svg>`;
  93. const magnet_Svg = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" class="tool-svg" x="0px" y="0px" viewBox="0 0 1000 1000" ><g><g transform="translate(0.000000,460.000000) scale(0.100000,-0.100000)"><path d="M4171.6,3994c-183.9-13.4-515.3-67.1-706.9-113c-770.2-187.7-1448.4-563.3-2021.2-1118.8c-707-685.9-1130.3-1494.4-1299-2481c-59.4-358.3-59.4-1002,0-1360.2c157.1-923.4,546-1705.1,1172.5-2354.6c695.4-722.3,1534.6-1159.1,2548.1-1325.7c174.4-28.7,388.9-34.5,1643.8-40.2l1440.7-7.7v1302.8v1302.8l-1354.5,7.6c-1207,5.7-1369.8,9.6-1480.9,40.2c-448.3,116.9-785.5,335.3-1036.5,666.7c-252.9,339.1-364,666.7-364,1088.2s111.1,749.1,364,1088.2c241.4,318,595.8,551.8,1000.1,659.1c157.1,40.2,191.6,42.1,1517.3,47.9l1354.5,7.7v1302.8v1300.9l-1344.9-3.8C4863.3,4001.6,4219.5,3997.8,4171.6,3994z"/><path d="M7620.1,2704.6V1401.8h1139.9H9900v1302.8v1302.8H8760.1H7620.1V2704.6z"/><path d="M7620.1-3502.7v-1302.8h1139.9H9900v1302.8v1302.8H8760.1H7620.1V-3502.7z"/></g></g></svg>`;
  94.  
  95. const LOCALE = {
  96. zh: {
  97. menuText :'设置',
  98. menu_autoPage: '自动下一页',
  99. menu_copyBtn :'复制图标',
  100. menu_toolBar: '功能图标',
  101. menu_avInfo:'弹窗中的演员和样品图',
  102. menu_halfImg:'竖图模式',
  103. menu_fullTitle:'标题全显',
  104. menu_columnNum:'列',
  105. menu_menutoTop:'左侧菜单移至上方',
  106. copyButton:'复制',
  107. copySuccess:'复制成功',
  108. getAvImg_norespond:'blogjav.net网站暂时无法响应',
  109. getAvImg_none:'未搜索到',
  110. tool_magnetTip:'磁力',
  111. tool_downloadTip:'下载封面',
  112. tool_pictureTip:'视频截图(blogjav.net)需代理',
  113. scrollerPlugin_end:'完'
  114. },
  115. en: {
  116. menuText :'Settings',
  117. menu_autoPage:'auto Next Page',
  118. menu_copyBtn:'copy icon',
  119. menu_toolBar:'tools icon',
  120. menu_avInfo:'actors and sample images in pop-ups',
  121. menu_halfImg:'Vertical image mode',
  122. menu_fullTitle:'Full Title',
  123. menu_columnNum:'columns',
  124. menu_menutoTop:'Move the left menu to the top',
  125. copyButton:'Copy',
  126. copySuccess:'Copy successful',
  127. getAvImg_norespond:'blogjav.net is temporarily unable to respond',
  128. getAvImg_none:'Not found',
  129. tool_magnetTip:'Magnet',
  130. tool_downloadTip:'Download cover',
  131. tool_pictureTip:'Video screenshot from blogjav.net',
  132. scrollerPlugin_end:'End'
  133. }
  134. }
  135. let getlanguage = () => {
  136. let local= navigator.language;
  137. local = local.toLowerCase().replace('_', '-');
  138. if (local in LOCALE){
  139. return LOCALE[local];
  140. }else if (local.split('-')[0] in LOCALE){
  141. return LOCALE[local.split('-')[0]];
  142. }else {
  143. return LOCALE.en;
  144. }
  145. }
  146. let lang = getlanguage();
  147.  
  148. // 弹出提示框
  149. let showAlert = (msg,close) => {
  150. let $alert=$(`<div class="alert-zdy" >${msg}</div>`);
  151. if(close){
  152. let $close = $(`<div style="display: inline-block;padding: 0 0 0 10px;color:gray;cursor: pointer;">X</div>`);
  153. $alert.append($close);
  154. $close.on("click",()=>$alert.hide());
  155. }
  156. $('body').append($alert);
  157. $alert.show({start:function(){
  158. $(this).css({'margin-top': -$(this).height() / 2 ,'margin-left': -$(this).width() / 2 });
  159. }});
  160. if(!close){$alert.delay(3000).fadeOut()};
  161. }
  162.  
  163. //图片加载时的回调函数
  164. let imgCallback = (img)=> {
  165. if (Status.isHalfImg()) {
  166. if(img.height < img.width){
  167. img.style= halfImgCSS ;
  168. }else{
  169. img.style= fullImgCSS ;
  170. }
  171. }else{
  172. //大图模式下,对大于标准比例(以ipx的封面为准)的图片进行缩小
  173. if(img.height/img.width>=0.7){
  174. img.style= `width:${img.width*67.25/img.height}%;` ;
  175. }else{
  176. img.style= fullImgCSS ;
  177. }
  178. }
  179. }
  180.  
  181. let Status = {
  182. halfImg_block:false,//是否屏蔽竖图模式,默认为否
  183. set : function(key,value){
  184. if(key=="columnNum") {
  185. key=key+(this.isHalfImg()?"Half":"Full");
  186. }else if(key=="waterfallWidth"){
  187. key=key+"_"+currentWeb;//宽度为各网站独立属性
  188. }
  189. return GM_setValue(key, value);
  190. },
  191. get : function(key){
  192. return GM_getValue(key=="waterfallWidth"?(key+"_"+currentWeb):key, statusDefault[key]);
  193. },
  194. //是否为竖图模式
  195. isHalfImg: function () {
  196. return this.get("halfImg") && (!this.halfImg_block);
  197. },
  198. //获取列数
  199. getColumnNum: function () {
  200. var key= 'columnNum'+(this.isHalfImg()?"Half":"Full");
  201. return this.get(key);
  202. }
  203. };
  204. //弹窗类,用于展示演员,样品图和磁力
  205. class Popover{
  206. show(){
  207. document.documentElement.classList.add("scrollBarHide");
  208. this.element.show({duration:0,start:function(){
  209. var t=$(this).find('#modal-div');
  210. t.css({'margin-top': Math.max(0, ($(window).height() - t.height()) / 2) });
  211. }});
  212. }
  213. hide(){
  214. document.documentElement.classList.remove("scrollBarHide");
  215. this.element.hide();
  216. this.element.find('.pop-up-tag').hide();
  217. }
  218. init(){
  219. var me=this;
  220. me.element = $('<div id="myModal"><div id="modal-div" > </div></div>');
  221. me.element.on('click',function(e){
  222. if($(e.target).closest("#modal-div").length==0){
  223. me.hide();
  224. }
  225. });
  226. me.scrollBarWidth = me.getScrollBarWidth();
  227. GM_addStyle('.scrollBarHide{ padding-right: ' + me.scrollBarWidth + 'px;overflow:hidden;}');
  228. $('body').append(me.element);
  229. //加载javbus的图片浏览插件
  230. if(currentWeb=="javbus"){
  231. me.element.magnificPopup({
  232. delegate: 'a.sample-box-zdy:visible',
  233. type: 'image',
  234. closeOnContentClick: false,
  235. closeBtnInside: false,
  236. mainClass: 'mfp-with-zoom mfp-img-mobile',
  237. image: {verticalFit: true},
  238. gallery: { enabled: true},
  239. zoom: {enabled: true,duration: 300,opener: function (element) {return element.find('img');}}
  240. });
  241. }
  242. }
  243. append(elem){
  244. if(!this.element){ this.init();}
  245. this.element.find("#modal-div").append(elem);
  246. return this;
  247. }
  248. //获取滚动条的宽度
  249. getScrollBarWidth() {
  250. var el = document.createElement("p");
  251. var styles = {width: "100px",height: "100px",overflowY: "scroll" };
  252. for (var i in styles) {
  253. el.style[i] = styles[i];
  254. }
  255. document.body.appendChild(el);
  256. var scrollBarWidth = el.offsetWidth - el.clientWidth;
  257. el.remove();
  258. return scrollBarWidth;
  259. }
  260. }
  261. class SettingMenu {
  262. onChange = {
  263. autoPage: function() {
  264. if (scroller) {
  265. scroller.destroy();scroller = null;
  266. } else {
  267. scroller = new ScrollerPlugin($('#grid-b'), lazyLoad);
  268. }
  269. },
  270. copyBtn: function() {
  271. $("#grid-b .copy-span").toggle();
  272. },
  273. toolBar: function() {
  274. $("#grid-b .toolbar-b").toggle();
  275. },
  276. halfImg: function() {
  277. let me = this;
  278. $("#grid-b .box-b img.loaded").each(function(index, el) {
  279. imgCallback(el);
  280. });
  281. var columnNum = Status.getColumnNum();
  282. GM_addStyle(`#grid-b .item-b{ width: ${100/columnNum}%;}`);
  283. $("#columnNum_range").val(columnNum);
  284. $("#columnNum_range+span").text(columnNum);
  285. },
  286. fullTitle: function() {
  287. $("#grid-b a[name='av-title']").toggleClass("titleNowrap");
  288. },
  289. avInfo: function() {},
  290. menutoTop : function() {location.reload();},
  291. columnNum: function(columnNum) {
  292. GM_addStyle(`#grid-b .item-b{ width: ${100/columnNum}%;}`);
  293. },
  294. waterfallWidth: function(width) {
  295. $(currentObj.widthSelector).css({"width":`${width}%`,"margin": `0 ${width>100?(100-width)/2+"%":"auto"}`});
  296. },
  297. downloadPanel : ()=>{
  298. TabPanel.getInstance().show(0);
  299. },
  300. addHiddenWords :()=>{
  301. TabPanel.getInstance().show(1);
  302. }
  303. }
  304. constructor() {
  305. let columnNum = Status.getColumnNum();
  306. let $menu = $('<div id="menu-div" ></div>');
  307. $menu.append(this.creatCheckbox("autoPage", lang.menu_autoPage));
  308. $menu.append(this.creatCheckbox("copyBtn", lang.menu_copyBtn));
  309. $menu.append(this.creatCheckbox("toolBar", lang.menu_toolBar));
  310. $menu.append(this.creatCheckbox("halfImg", lang.menu_halfImg, Status.halfImg_block));
  311. $menu.append(this.creatCheckbox("fullTitle", lang.menu_fullTitle));
  312. if (["javbus", "javdb"].includes(currentWeb)) {
  313. $menu.append(this.creatCheckbox("avInfo", lang.menu_avInfo));
  314. }
  315. if (currentWeb == 'javlibrary') {
  316. $menu.append(this.creatCheckbox("menutoTop", lang.menu_menutoTop));
  317. }
  318. $menu.append(this.creatRange("columnNum", lang.menu_columnNum, columnNum, 8));
  319. $menu.append(this.creatRange("waterfallWidth", '%', Status.get("waterfallWidth") , currentObj.maxWidth ? currentObj.maxWidth : 100));
  320. $menu.append(this.creatButton("downloadPanel","批量下载封面"));
  321. $menu.append(this.creatButton("addHiddenWords","添加屏蔽词"));
  322. let $circle = $(`<div style="position: ${currentWeb=="javlibrary"?"absolute":"fixed"};z-index: 1030;left:0;top:${currentWeb=="javlibrary"?"36px":"0px"};"><div style="width: 36px;height: 36px;background-color: rgb(208 176 176 / 30%);border-radius: 18px;"></div></div>`);
  323. $circle.append($menu);
  324. $circle.mouseenter(() => $menu.show()).mouseleave(() => $menu.hide());
  325. $("body").append($circle);
  326. notice($menu);
  327. }
  328. creatCheckbox(tagName, name, disabled) {
  329. let me =this;
  330. let $checkbox = $(`<div class="switch-div"><input ${disabled?'disabled="disabled"':''} type="checkbox" id="${tagName}_checkbox" /><label for="${tagName}_checkbox" >${name}</label></div>`);
  331. $checkbox.find("input")[0].checked = Status.get(tagName);
  332. $checkbox.find("input").eq(0).click(function() {
  333. Status.set(tagName, this.checked);
  334. me.onChange[tagName]();
  335. });
  336. return $checkbox;
  337. }
  338. creatRange(tagName, name, value, max) {
  339. let me =this;
  340. let $range = $(`<div class="range-div"><input type="range" id="${tagName}_range" min="1" max="${max}" step="1" value="${value}" /><span name="value">${value}</span><span>${name}</span></div>`);
  341. $range.bind('input propertychange', function() {
  342. var val = $(this).find("input").eq(0).val();
  343. $(this).find("span[name=value]").html(val);
  344. Status.set(tagName, val);
  345. me.onChange[tagName](val);
  346. });
  347. return $range;
  348. }
  349. creatButton(tagName,name) {
  350. let me =this;
  351. var $button = $(`<div style="margin:3px;"><button style="width: 100%;padding: 3px;">${name}</button></div>`);
  352. $button.bind('click', () =>{ me.onChange[tagName](); });
  353. return $button;
  354. }
  355. }
  356. const notice = ($menu)=>{
  357. let version = Status.get("version");
  358. if(version != VERSION){
  359. if(!version){
  360. $menu.slideDown();
  361. }
  362. showAlert(NOTICE,true);
  363. Status.set("version",VERSION);
  364. }
  365. }
  366. function showMagnetTable(itemID,avid,href,elem) {
  367. if ($(elem).hasClass("span-loading")) {return;}
  368. let tagName = `${itemID}${AVINFO_SUFFIX}`;
  369. let $el=$(`.pop-up-tag[name='${tagName}']`);
  370. if ($el.length > 0) {
  371. $el.show();myModal.show();
  372. } else {
  373. $(elem).addClass("span-loading");
  374. Promise.resolve().then(()=>{
  375. switch(currentWeb) {
  376. case "javbus": {
  377. return getMagnet4JavBus(href,tagName)
  378. }
  379. case "javdb": {
  380. return getMagnet4JavDB(href,tagName,itemID)
  381. }
  382. }
  383. }).then((dom)=>{
  384. myModal.append(dom).show();
  385. }).catch(err=>alert(err)).then(()=>$(elem).removeClass("span-loading"));
  386. }
  387. }
  388. //获取javdb的演员磁力信息
  389. async function getMagnet4JavDB(href,tagName,itemID) {
  390. let doc = await fetch(href).then(response => response.text());
  391. let $doc=$($.parseHTML(doc));
  392. let info = $(`<div class="pop-up-tag" name="${tagName}"></div>`);
  393. if(Status.get("avInfo")){
  394. let actors= $doc.find("div.video-meta-panel .panel-block").toArray().find(el=> $(el).find("a[href^='/actors/']").length>0);
  395. $(actors).find("a").attr("target","_blank");
  396. let preview_images= $doc.find(".columns").toArray().find(el=> $(el).find("div.tile-images.preview-images").length>0);
  397. let $preview_images = $(preview_images);
  398. $preview_images.find(".preview-video-container").attr("href",`#preview-video-${itemID}`);
  399. $preview_images.find("#preview-video").attr("id",`preview-video-${itemID}`);
  400. $preview_images.find("img[data-src]").each((i,el)=> $(el).attr("src",$(el).attr("data-src")));
  401. info.append(actors);info.append(preview_images);
  402. }
  403. let magnetTable = $doc.find(`div.columns[data-controller="movie-tab"]`);
  404. magnetTable.find("div.top-meta").remove();// 移除广告
  405. info.append(magnetTable);
  406. return info;
  407. };
  408. // javbus:获取演员磁力信息
  409. async function getMagnet4JavBus(href, tagName) {
  410. let {gid,dom} = await avInfofetch(href,tagName);
  411. //有码和欧美 0 无码 1
  412. let uc_code = location.pathname.search(/(uncensored|mod=uc)/) < 1 ? 0 : 1;
  413. let url = `${location.protocol}//${location.hostname}/ajax/uncledatoolsbyajax.php?gid=${gid}&lang=zh&img=&uc=${uc_code}&floor=` + Math.floor(Math.random() * 1e3 + 1);
  414. let doc = await fetch(url).then(response => response.text());
  415. let table_html = doc.substring(0, doc.indexOf('<script')).trim();
  416. let table_tag = $(`<table class="table pop-up-tag" name="${tagName}" style="background-color:#FFFFFF;" ></table>`);
  417. table_tag.append($(table_html));
  418. table_tag.find("tr").each(function (i) { // 遍历 tr
  419. let $a = $(this).find('a');
  420. if ($a.length) {
  421. let magent_url = $a[0].href;
  422. $(this).prepend(creatCopybutton(magent_url));
  423. }
  424. });
  425. dom.push(table_tag);
  426. return dom;
  427. };
  428. //javbus:磁力链接添加复制按钮
  429. function creatCopybutton(text) {
  430. let $copyButton = $(`<td><button class="center-block">${lang.copyButton}</button></td>`);
  431. $copyButton.find("button").click(function () {
  432. GM_setClipboard(text);
  433. showAlert(lang.copySuccess);
  434. });
  435. return $copyButton;
  436. }
  437. //javbus:获取详情页面的 演员表和样品图元素
  438. async function avInfofetch(href,tagName) {
  439. let doc = await fetch(href).then(response => response.text())
  440. let str = /var\s+gid\s+=\s+(\d{1,})/.exec(doc);
  441. let avInfo = {gid:str[1],dom:[]};
  442. if(Status.get("avInfo")){
  443. let sample_waterfall = $($.parseHTML(doc)).find("#sample-waterfall");
  444. let avatar_waterfall = $($.parseHTML(doc)).find("#avatar-waterfall");
  445. if(avatar_waterfall.length>0){
  446. avatar_waterfall[0].id = "";
  447. avatar_waterfall.addClass("pop-up-tag");
  448. avatar_waterfall.attr("name",tagName);
  449. avatar_waterfall.find("a.avatar-box span").each((i,el)=> {
  450. let $copySvg = $(`<div style="width:24px;height:24px;display: flex;align-items: center;justify-content: center;">${copy_Svg}</div>`);
  451. $copySvg.click(function () {
  452. GM_setClipboard($(el).text());
  453. showAlert(lang.copySuccess);
  454. return false;
  455. });
  456. $(el).prepend($copySvg);
  457. });
  458. avatar_waterfall.find("a.avatar-box").attr("target","_blank").removeClass("avatar-box").addClass("avatar-box-zdy");
  459. avInfo.dom.push(avatar_waterfall);
  460. }
  461. if(sample_waterfall.length>0){
  462. sample_waterfall[0].id = "";
  463. sample_waterfall.addClass("pop-up-tag");
  464. sample_waterfall.attr("name",tagName);
  465. sample_waterfall.find(".sample-box").removeClass("sample-box").addClass("sample-box-zdy");
  466. avInfo.dom.push(sample_waterfall);
  467. }
  468. }
  469. return avInfo;
  470. };
  471.  
  472. //弹出视频截图
  473. function showBigImg(itemID,avid,elem) {
  474. if ($(elem).hasClass("span-loading")) {return;}
  475. let tagName = `${itemID}${SCREENSHOT_SUFFIX}`;
  476. let $selector = $(`.pop-up-tag[name='${tagName}']`);
  477. if ($selector.length > 0) {
  478. $selector.show();
  479. myModal.show();
  480. } else {
  481. $(elem).addClass("span-loading");
  482. getAvImg(avid,tagName).then(($img)=>{
  483. myModal.append($img).show();
  484. }).catch(err=>err && showAlert(err)).then(()=>{
  485. $(elem).removeClass("span-loading");
  486. });
  487. }
  488. }
  489. const getRequest = (url,params) => {
  490. return new Promise((resolve, reject)=>{
  491. GM_xmlhttpRequest(Object.assign({
  492. method: "GET",
  493. url: url,
  494. timeout: 20000,
  495. onload: (r)=>resolve(r),
  496. onerror : (r)=> reject(`error`),
  497. ontimeout : (r)=> reject(`timeout`)
  498. },params));
  499. })
  500. }
  501. const downloadImg = (url,name,elem) => {
  502. if ($(elem).hasClass("span-loading")) return;
  503. $(elem).addClass("span-loading");
  504. new Promise((resolve, reject)=>{
  505. GM_download({
  506. url: url,
  507. name :name,
  508. headers : {Referer : url},
  509. onload: (r)=>resolve(r),
  510. onerror : (error,detail)=> reject(`error 错误`),
  511. ontimeout : (r)=> reject(`timeout 超时`)
  512. })})
  513. .catch(err=>showAlert(err))
  514. .then(()=>$(elem).removeClass("span-loading"));
  515. }
  516. /**根据番号获取blogjav的视频截图,使用fetch会产生跨域问题*/
  517. async function getAvImg(avid,tagName) {
  518. const r = await getRequest(`https://blogjav.net/?s=${avid}`);
  519. if(r.status == 503){
  520. showAlert(`blogjav.net有防攻击机制, <a target="_blank" href="https://blogjav.net">点击跳转</a>解除 `,`close`);
  521. return Promise.reject();
  522. }else if(r.status != 200){
  523. return Promise.reject(lang.getAvImg_norespond);
  524. }
  525. let resultList = $($.parseHTML(r.responseText)).find(blogjavSelector).toArray().map((v)=> {return {title:v.innerHTML,href:v.href} });
  526. if (resultList.length==0) {
  527. return Promise.reject(lang.getAvImg_none);
  528. }
  529. let indexTo = -1;
  530. for (let i = 0; i < resultList.length; i++) {
  531. const r = resultList[i];
  532. let src = await ScreenshotPanel.getScreenshotUrl(r.href);
  533. if(src) {
  534. r.src = src;
  535. indexTo = i;
  536. break;
  537. }
  538. }
  539. if(indexTo == -1) {
  540. return Promise.reject(lang.getAvImg_none);
  541. }
  542. let $img = new ScreenshotPanel(tagName,resultList.slice(indexTo),avid);
  543. return $img;
  544. };
  545. class ScreenshotPanel{
  546. constructor(tagName,resultList,avid){
  547. let me =this;
  548. let liHtml = resultList.map((v,i) => {
  549. if(i==0){
  550. return `<li class="imgResult-li imgResult-Current" index=${i}>${v.title}</li>`
  551. } else {
  552. return `<li class="imgResult-li" index=${i}>${v.title}</li>`
  553. }
  554. }).join('');
  555. let imgsHtml = resultList.map((v,i)=> {
  556. if(i==0){
  557. return `<img index=${i} src="${v.src}" name="screenshot" style="width:100%" />`
  558. } else {
  559. return `<img index=${i} name="screenshot" style="display:none;width:100%" />`
  560. }
  561. }).join('');
  562. let $panel = $(`<div name="${tagName}" class="pop-up-tag" style="min-height:${$(window).height()}px;">
  563. <ul>${liHtml}</ul>
  564. <span class="download-icon" >${download_Svg}</span>
  565. ${imgsHtml}</div>`);
  566. $panel.find("li.imgResult-li").click(async function(){
  567. if ($(this).hasClass("imgResult-loading")) {return;}
  568. let index_to = $(this).attr('index');
  569. let index_from = $panel.find("img:visible").attr(`index`);
  570. if( index_to != index_from){
  571. $panel.find("li.imgResult-li.imgResult-Current").removeClass('imgResult-Current');
  572. $(this).addClass(`imgResult-loading`).addClass("imgResult-Current");
  573. $panel.find("img").hide();
  574. let $img_to = $panel.find(`img[index=${index_to}]`);
  575. $img_to.show();
  576. try {
  577. let data = resultList[index_to];
  578. if(!data.src){
  579. if(data.src === undefined){
  580. let src = await me.constructor.getScreenshotUrl(data.href);
  581. data.src = src;
  582. if(src === null){
  583. throw lang.getAvImg_none;
  584. }
  585. $img_to.attr(`src`,src);
  586. }else if(data.src === null){
  587. throw lang.getAvImg_none;
  588. }
  589. }
  590.  
  591. } catch (error) {
  592. showAlert(error)
  593. }finally{
  594. $(this).removeClass(`imgResult-loading`);
  595. }
  596. }
  597. })
  598. $panel.find("span.download-icon").click(function(){
  599. let url = $panel.find("img:visible").attr(`src`);
  600. let name = `${avid || "screenshot"}.jpg`;
  601. downloadImg(url,name,this);
  602. });
  603. return $panel;
  604. }
  605. static async getScreenshotUrl(imgUrl){
  606. const result = await getRequest(imgUrl);
  607. const $img = $($.parseHTML(result.responseText)).find(`#primary .entry-content>p img[src*="pixhost.to/thumbs"]`);
  608. if ($img.length == 0) {
  609. return null;
  610. }else{
  611. let src = $img[0].src.replace('thumbs', 'images').replace('//t', '//img');
  612. console.log(src);
  613. return src;
  614. }
  615. }
  616. }
  617.  
  618. let lazyLoad;
  619. let scroller;
  620. let myModal;//弹窗插件实例
  621. let currentWeb = "javbus";//网站域名标识,用于判断当前在什么网站
  622. let currentObj ;//当前网站对应的属性对象
  623. /**
  624. * 通用属性对象
  625. * domainReg: 域名正则式 用于判断当前在什么网站
  626. * excludePages: 排除的页面
  627. * halfImg_block_Pages 屏蔽竖图的页面
  628. * gridSelector 源网页的网格选择器
  629. * itemSelector 源网页的子元素选择器
  630. * widthSelector 源网页的宽度设置元素选择器
  631. * pageNext 源网页的下一页元素选择器
  632. * pageSelector 源网页的翻页元素选择器
  633. * getAvItem 解析源网页item的数据
  634. * init_Style 加载各网页的特殊css
  635. */
  636. let ConstCode = {
  637. javbus: {
  638. domainReg: /(javbus|busjav|busfan|fanbus|buscdn|cdnbus|dmmsee|seedmm|busdmm|dmmbus|javsee|seejav)\./i,
  639. excludePages: ['/actresses', 'mdl=favor&sort=1', 'mdl=favor&sort=2', 'mdl=favor&sort=3', 'mdl=favor&sort=4', 'searchstar'],
  640. halfImg_block_Pages:['/uncensored','javbus.one','mod=uc','javbus.red'],
  641. gridSelector: 'div#waterfall',
  642. itemSelector: 'div#waterfall div.item',
  643. widthSelector : '#grid-b',
  644. pageNext:'a#next',
  645. pageSelector:'.pagination',
  646. getAvItem: function (elem) {
  647. var photoDiv = elem.find("div.photo-frame")[0];
  648. var href = elem.find("a")[0].href;
  649. var img = $(photoDiv).children("img")[0];
  650. var src = img.src;
  651. if (src.match(/pics.dmm.co.jp/)) {
  652. src = src.replace(/ps.jpg/, "pl.jpg");
  653. }else if(src.match(/image.mgstage.com/)){
  654. src = src.replace(/pf_o1_|pb_p_/, "pb_e_");
  655. } else {
  656. src = src.replace(/thumbs/, "cover").replace(/thumb/, "cover").replace(/.jpg/, "_b.jpg");
  657. }
  658. var title = img.title;
  659. var AVID = elem.find("date").eq(0).text();
  660. var date = elem.find("date").eq(1).text();
  661. var itemTag = "";elem.find("div.photo-info .btn").toArray().forEach( x=> itemTag+=x.outerHTML);
  662. return {AVID,href,src,title,date,itemTag};
  663. }
  664. },
  665. javdb: {
  666. domainReg: /(javdb)[0-9]*\./i,
  667. excludePages: ['/users/'],
  668. halfImg_block_Pages:['/uncensored','/western','/video_uncensored','/video_western'],
  669. gridSelector: 'div.movie-list.h',
  670. itemSelector: 'div.movie-list.h>div.item',
  671. widthSelector : '#grid-b',
  672. pageNext: 'a.pagination-next',
  673. pageSelector:'.pagination-list',
  674. init_Style: function(){
  675. GM_addStyle(`#grid-b .info-bottom-two{flex-grow:1}
  676. [data-theme=light] .pop-up-tag[name$='${AVINFO_SUFFIX}'] {background-color: rgb(255 255 255 / 90%);}
  677. [data-theme=dark] .scroll-request span{background:white;}
  678. [data-theme=dark] #grid-b .box-b a:link {color : inherit;}
  679. [data-theme=dark] #grid-b .box-b{background-color:#222;}
  680. [data-theme=dark] .alert-zdy {color: black;background-color: rgb(255 255 255 / 90%);}
  681. #myModal #modal-div article.message {margin-bottom: 0}`);
  682. },
  683. maxWidth: 150,//javdb允许的最大宽度为150%,其他网站默认最大宽度为100%
  684. getAvItem: function (elem) {
  685. var href = elem.find("a")[0].href;
  686. var src = elem.find("div.cover>img").eq(0).attr("src");
  687. var title = elem.find("a")[0].title;
  688. var AVID = elem.find("div.video-title>strong").eq(0).text();
  689. var date = elem.find("div.meta").eq(0).text();
  690. var score = elem.find("div.score").html();
  691. var itemTag = elem.find(".tags.has-addons").html();
  692. return {AVID,href,src,title,date,itemTag,score};
  693. }
  694. //init: function(){ if(location.href.includes("/users/")){ this.widthSelector="div.section";} }
  695. },
  696. avmoo: {
  697. domainReg: /avmoo\./i,
  698. excludePages: ['/actresses'],
  699. gridSelector: 'div#waterfall',
  700. itemSelector: 'div#waterfall div.item',
  701. widthSelector : '#grid-b',
  702. pageNext: 'a[name="nextpage"]',
  703. pageSelector:'.pagination',
  704. getAvItem: function (elem) {
  705. var photoDiv = elem.find("div.photo-frame")[0];
  706. var href = elem.find("a")[0].href;
  707. var img = $(photoDiv).children("img")[0];
  708. var src = img.src.replace(/ps.jpg/, "pl.jpg");
  709. var title = img.title;
  710. var AVID = elem.find("date").eq(0).text();
  711. var date = elem.find("date").eq(1).text();
  712. var itemTag = "";elem.find("div.photo-info .btn").toArray().forEach( x=> itemTag+=x.outerHTML);
  713. return {AVID,href,src,title,date,itemTag};
  714. }
  715. },
  716. javlibrary: {
  717. domainReg: /javlibrary\./i,
  718. gridSelector: 'div.videothumblist',
  719. itemSelector: 'div.videos div.video',
  720. widthSelector : '#grid-b',
  721. pageNext: 'a.page.next',
  722. pageSelector:'.page_selector',
  723. getAvItem: function (elem) {
  724. var href = elem.find("a")[0].href;
  725. var src = elem.find("img")[0].src;
  726. if(src.indexOf("pixhost")<0){//排除含有pixhost的src,暂时未发现规律
  727. src= src.replace(/ps.jpg/, "pl.jpg");
  728. }
  729. var title = elem.find("div.title").eq(0).text();
  730. var AVID = elem.find("div.id").eq(0).text();
  731. return {AVID,href,src,title,date: '',itemTag:''};
  732. },
  733. init_Style: function(){
  734. GM_addStyle(`${Status.get("menutoTop")?`
  735. #leftmenu {width : 100%;float: none;}
  736. #leftmenu>table { display : none;}
  737. #leftmenu .menul1,#leftmenu .menul1>ul{display: flex;align-items: center;justify-content: center;flex-wrap: wrap;}
  738. #leftmenu .menul1{padding: 5px;}
  739. #rightcolumn{margin: 0 5px;padding : 10px 5px;}`:``}
  740. #grid-b div{box-sizing: border-box;}`);
  741. },
  742. }
  743. };
  744.  
  745. /** 用于屏蔽老司机脚本的代码*/
  746. function oldDriverBlock(){
  747. if(['javbus','avmoo'].includes(currentWeb)){ //屏蔽老司机脚本,改写id
  748. if ($('.masonry').length > 0) {
  749. $('.masonry').removeClass("masonry");
  750. }
  751. let $waterfall = $('#waterfall');
  752. if($waterfall.length){
  753. $waterfall.get(0).id = "waterfall-destroy";
  754. }
  755. if($waterfall.find("#waterfall").length){ //javbus首页有2个'waterfall' ID
  756. $waterfall.find("#waterfall").get(0).id = "";
  757. }
  758. //解决 JAV老司机 $pages[0].parentElement.parentElement.id = "waterfall_h";
  759. //女优作品界面此代码会把id设置到class=row层
  760. if ($('#waterfall_h.row').length > 0) {
  761. $('#waterfall_h.row').removeAttr("id");
  762. }
  763. let $waterfall_h= $('#waterfall_h');
  764. if ($waterfall_h.length) {
  765. $waterfall_h.get(0).id = "waterfall-destroy";
  766. }
  767. if(location.pathname.search(/search/) > 0){//解决"改写id后,搜索页面自动跳转到无码页面"的bug
  768. $('body').append('<div id="waterfall"></div>');
  769. }
  770. currentObj.gridSelector = "#waterfall-destroy";
  771. }
  772. if(['javlibrary'].includes(currentWeb)){ //屏蔽老司机脚本,改写id
  773. let $waterfall = $('div.videothumblist');
  774. if($waterfall.length){
  775. $waterfall.removeClass("videothumblist");
  776. $waterfall.find(".videos").removeClass("videos");
  777. $waterfall.get(0).id = "waterfall-destroy";
  778. }
  779. currentObj.gridSelector = "#waterfall-destroy";
  780. }
  781. }
  782. class Page{
  783. constructor(){
  784. for (let key in ConstCode) {
  785. let domainReg = ConstCode[key].domainReg;
  786. if (domainReg && domainReg.test(location.href)) {
  787. currentWeb = key;//首先判断当前是什么网站
  788. break;
  789. }
  790. }
  791. currentObj = ConstCode[currentWeb];
  792. //排除页面的判断
  793. if (currentObj.excludePages) {
  794. for (let page of currentObj.excludePages) {
  795. if (location.pathname.includes(page)) return;
  796. }
  797. }
  798. //调用初始化方法 未使用 if (currentObj.init) { currentObj.init();}
  799. //屏蔽竖图模式的页面判断
  800. if (currentObj.halfImg_block_Pages) {
  801. for (let blockPage of currentObj.halfImg_block_Pages) {
  802. if (location.href.includes(blockPage)) {
  803. Status.halfImg_block = true;
  804. break;
  805. };
  806. }
  807. }
  808. this.render();
  809. }
  810. render(){
  811. let $items = $(currentObj.itemSelector);
  812. if ($items.length<1) return;
  813. oldDriverBlock();
  814. addStyle();
  815. currentObj.init_Style?.();
  816. let menu = new SettingMenu();
  817. //加载图片懒加载插件
  818. lazyLoad = new LazyLoad({
  819. callback_loaded: function (img) {
  820. $(img).removeClass("minHeight-200");
  821. imgCallback(img);
  822. }
  823. });
  824. let gridPanel = new GridPanel($items,lazyLoad);
  825. myModal = new Popover();//弹出插件
  826. //加载滚动翻页插件
  827. if(Status.get("autoPage") && $(currentObj.pageSelector).length ){
  828. scroller=new ScrollerPlugin(gridPanel.$dom,lazyLoad);
  829. }
  830. }
  831. }
  832. class GridPanel{
  833. constructor($items,lazyLoad){
  834. this.$dom=$(`<div id= 'grid-b'></div>`);
  835. $(currentObj.gridSelector).hide().eq(0).before(this.$dom);
  836. let $elems = this.constructor.parseItems($items);
  837. this.$dom.append($elems);
  838. lazyLoad.update();
  839. }
  840. static parseItems(elems){
  841. let elemsHtml = "";
  842. let {imgStyle,getAvItem,toolBar,copyBtn,fullTitle,magnet,magnetTip,downloadTip,pictureTip} = {
  843. imgStyle: Status.isHalfImg() ? halfImgCSS : fullImgCSS,
  844. getAvItem: currentObj.getAvItem,
  845. toolBar: Status.get("toolBar")?'':'hidden-b',
  846. copyBtn: Status.get("copyBtn")?'':'hidden-b',
  847. fullTitle: Status.get("fullTitle")?'':'titleNowrap',
  848. magnet: ['javbus','javdb'].includes(currentWeb)?'':'hidden-b',
  849. magnetTip : lang.tool_magnetTip,
  850. downloadTip: lang.tool_downloadTip,
  851. pictureTip: lang.tool_pictureTip,
  852. };
  853. let [hiddenWords,hiddenAvids] = [Status.get("hiddenWord"),Status.get("hiddenAvid")];
  854. for (let i = 0; i < elems.length; i++) {
  855. let tag = elems.eq(i);
  856. let html = "";
  857. //判断是否为 女优个人资料item
  858. if (currentWeb!="javdb" && tag.find(".avatar-box").length) {
  859. tag.find(".avatar-box").addClass("avatar-box-b").removeClass("avatar-box");
  860. html = `<div class='item-b'>${tag.html()}</div>`;
  861. }else{
  862. let AvItem = getAvItem(tag);
  863. if (!(hiddenWords.find((v, i) => AvItem.title.includes(v)) ||
  864. hiddenAvids.find((v, i) => AvItem.AVID.toUpperCase().startsWith(v.toUpperCase()+"-")|| AvItem.AVID.toUpperCase()==v.toUpperCase() ))) {
  865. html = `<div class="item-b">
  866. <div class="box-b">
  867. <div class="cover-b">
  868. <a href="${AvItem.href}" target="_blank"><img style="${imgStyle}" class="lazy minHeight-200" data-src="${AvItem.src}" ></a>
  869. </div>
  870. <div class="detail-b">
  871. <a name="av-title" href="${AvItem.href}" target="_blank" title="${AvItem.title}" class="${fullTitle}"><span class="tool-span copy-span ${copyBtn}" name="copy">${copy_Svg}</span> <span>${AvItem.title}</span></a>
  872. <div class="info-bottom">
  873. <div class="info-bottom-one">
  874. <a href="${AvItem.href}" target="_blank"><span class="tool-span copy-span ${copyBtn}" name="copy">${copy_Svg}</span><date name="avid">${AvItem.AVID}</date>${AvItem.date?` / ${AvItem.date}`:""}</a>
  875. </div>
  876. ${AvItem.score?`<a href="${AvItem.href}" target="_blank"><div class="score">${AvItem.score}</div></a>`:``}
  877. <div class="info-bottom-two">
  878. <div class="item-tag">${AvItem.itemTag}</div>
  879. <div class="toolbar-b ${toolBar}" item-id="${AvItem.AVID}${Math.random().toString(16).slice(2)}" >
  880. <span name="magnet" class="tool-span ${magnet}" title="${magnetTip}" AVID="${AvItem.AVID}" data-href="${AvItem.href}">${magnet_Svg}</span>
  881. <span name="download" class="tool-span" title="${downloadTip}" src="${AvItem.src}" src-title="${AvItem.AVID} ${AvItem.title}">${download_Svg}</span>
  882. <span name="picture" class="tool-span" title="${pictureTip}" AVID="${AvItem.AVID}" >${picture_Svg}</span>
  883. </div>
  884. </div>
  885. </div>
  886. </div>
  887. </div>
  888. </div>`;
  889. }
  890. }
  891. elemsHtml = elemsHtml + html;
  892. }
  893. let $elems = $(elemsHtml);
  894. $elems.find("span[name]").click(function () {
  895. let name = $(this).attr("name");
  896. switch (name) {
  897. case "copy":GM_setClipboard($(this).next().text());showAlert(lang.copySuccess);return false;
  898. case "download":
  899. let [url,name] = [$(this).attr("src"),$(this).attr("src-title")+".jpg"];
  900. downloadImg(url,name,this);break;
  901. case "magnet":showMagnetTable($(this).parent("div").attr("item-id"),$(this).attr("AVID").replace(/\./g, '-'),$(this).attr("data-href"),this);break;
  902. case "picture":showBigImg($(this).parent("div").attr("item-id"),$(this).attr("AVID"),this);break;
  903. default:break;
  904. }
  905. });
  906. return $elems;
  907. }
  908. }
  909. class ScrollerPlugin{
  910. constructor(waterfall,lazyLoad){
  911. let me=this;
  912. me.waterfall=waterfall;
  913. me.lazyLoad=lazyLoad;
  914. let $pageNext=$(currentObj.pageNext);
  915. me.nextURL = $pageNext.attr('href');
  916. me.scroller_status=$(`<div class = "scroller-status" style="text-align:center;display:none"><div class="scroll-request"><span></span><span></span><span></span><span></span></div><h2 class="scroll-last">${lang.scrollerPlugin_end}</h2></div>`);
  917. me.waterfall.after(me.scroller_status);
  918. me.locked=false;
  919. me.canLoad=true;
  920. me.$page=$(currentObj.pageSelector);
  921. me.domWatch_func=me.domWatch.bind(me);
  922. document.addEventListener('scroll',me.domWatch_func);
  923. if (history.scrollRestoration) {
  924. history.scrollRestoration = 'manual';//防止自动恢复页面位置
  925. }
  926. }
  927. domWatch (){
  928. let me = this;
  929. if (me.$page.get(0).getBoundingClientRect().top - $(window).height() < 300 && (!me.locked) && (me.canLoad)) {
  930. me.locked=true;
  931. me.loadNextPage(me.nextURL).then(()=>{me.locked=false});
  932. }
  933. }
  934. async loadNextPage(url){
  935. this.showStatus('request');
  936. console.log(url);
  937. let responseText = await fetch(url, { credentials: 'same-origin' }).then(respond=>respond.text());
  938. let $body = $(new DOMParser().parseFromString(responseText, 'text/html'));
  939. let elems = GridPanel.parseItems($body.find(currentObj.itemSelector));
  940. if (currentWeb != "javdb" && location.pathname.includes('/star/') && elems) {
  941. elems=elems.slice(1);
  942. }
  943. this.scroller_status.hide();
  944. this.waterfall.append(elems);
  945. this.lazyLoad.update();
  946. //history.pushState({}, "", url);
  947. this.nextURL = $body.find(currentObj.pageNext).attr('href');
  948. if(!this.nextURL){
  949. this.canLoad=false;
  950. this.showStatus("last");
  951. }
  952. }
  953. showStatus(status){
  954. this.scroller_status.children().each( (i,e)=>{$(e).hide()});
  955. this.scroller_status.find(`.scroll-${status}`).show();
  956. this.scroller_status.show();
  957. }
  958. destroy (){
  959. this.scroller_status.remove();
  960. document.removeEventListener('scroll',this.domWatch_func);
  961. }
  962. }
  963.  
  964. const addStyle = () => {
  965. let columnNum = Status.getColumnNum();
  966. let waterfallWidth=Status.get("waterfallWidth");
  967. let css_waterfall = `
  968. ${currentObj.widthSelector}{width:${waterfallWidth}%;margin:0 ${waterfallWidth>100?(100-waterfallWidth)/2+'%':'auto'};transition:.5s ;}
  969. #grid-b{display:flex;flex-direction:row;flex-wrap:wrap;}
  970. #grid-b .item-b{padding:5px;width:${100 / columnNum}%;transition:.5s ;animation: fadeInUp .5s ease-out;}
  971. #grid-b .box-b{border-radius:5px;background-color:white;border:1px solid rgba(0,0,0,0.2);box-shadow:0 2px 3px 0 rgba(0,0,0,0.1);overflow:hidden}
  972. #grid-b .box-b a:link{color:black}
  973. #grid-b .box-b a:visited{color:gray}
  974. #grid-b .box-b .cover-b{text-align:center}
  975. #grid-b .box-b .detail-b{padding:7px}
  976. #grid-b .box-b .detail-b a{display:block}
  977. #grid-b .info-bottom,.info-bottom-two{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap}
  978. #grid-b .avatar-box-b{display:flex;flex-direction:column;background-color:white;border-radius:5px;align-items:center;border:1px solid rgba(0,0,0,0.2)}
  979. #grid-b .avatar-box-b p{margin:0 !important}
  980. #grid-b date:first-of-type{font-size:18px !important}
  981. #grid-b .toolbar-b{float:right;padding:2px;white-space:nowrap}
  982. #grid-b .toolbar-b span{margin-right:2px}
  983. #grid-b .copy-span{vertical-align:middle;display:inline-block}
  984. #grid-b span.tool-span{cursor:pointer;opacity:.3}
  985. #grid-b span.tool-span:hover{opacity:1}
  986. #grid-b .item-tag{display:inline-block;white-space:nowrap}
  987. #grid-b .hidden-b{display:none}
  988. #grid-b .minHeight-200{min-height:200px}
  989. #grid-b .cover-b img:not([src]) {visibility: hidden;}
  990. svg.tool-svg{fill:currentColor;width:22px;height:22px;vertical-align:middle}
  991. span.span-loading{display:inline-block;animation:span-loading 2s infinite}
  992.  
  993. #myModal{overflow-x:hidden;overflow-y:auto;display:none;position:fixed;top:0;left:0;right:0;bottom:0;z-index:1050;background-color:rgba(0,0,0,0.5)}
  994. #myModal #modal-div{position:relative;width:80%;margin:0 auto;background-color:rgb(6 6 6 / 50%);border-radius:8px;animation:fadeInDown .5s}
  995. #modal-div .pop-up-tag{border-radius:8px;overflow:hidden}
  996. #modal-div .sample-box-zdy,.avatar-box-zdy{display:inline-block;border-radius:8px;background-color:#fff;overflow:hidden;margin:5px;width:140px}
  997. #modal-div .sample-box-zdy .photo-frame{overflow:hidden;margin:10px}
  998. #modal-div .sample-box-zdy img{height:90px}
  999. #modal-div .avatar-box-zdy .photo-frame{overflow:hidden;height:120px;margin:10px}
  1000. #modal-div .avatar-box-zdy img{height:120px}
  1001. #modal-div .avatar-box-zdy span{font-weight:bold;text-align:center;word-wrap:break-word;display:flex;justify-content:center;align-items:center;padding:5px;line-height:22px;color:#333;background-color:#fafafa;border-top:1px solid #f2f2f2}
  1002.  
  1003. #menu-div{white-space:nowrap;background-color:white;color:black;display:none;min-width:200px;border-radius:5px;padding:10px;box-shadow:0 10px 20px 0 rgb(0 0 0 / 50%)}
  1004. #menu-div>div:hover{background-color:gainsboro}
  1005. #menu-div .switch-div{display:flex;align-items:center;font-size:large;font-weight:bold}
  1006. #menu-div .switch-div *{margin:0;padding:4px}
  1007. #menu-div .switch-div label{flex-grow:1}
  1008. #menu-div .range-div{display:flex;flex-direction:row;flex-wrap:nowrap}
  1009. #menu-div .range-div input{cursor:pointer;width:80%;max-width:200px}
  1010. .alert-zdy{position:fixed;top:50%;left:50%;padding:12px 20px;font-size:20px;color:white;background-color:rgb(0,0,0,.75);border-radius:4px;animation:itemShow .3s;z-index:1051}
  1011. .titleNowrap{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}
  1012. .download-icon{position:absolute;right:0;z-index:2;cursor:pointer}
  1013. .download-icon>svg{width:30px;height:30px;fill:aliceblue}
  1014. @keyframes fadeInUp{0%{transform:translate3d(0,10%,0);opacity:.5}100%{transform:none;opacity:1}}
  1015. @keyframes fadeInDown{0%{transform:translate3d(0,-100%,0);opacity:0}100%{transform:none;opacity:1}}
  1016. @keyframes itemShow{0%{transform:scale(0)}100%{transform:scale(1)}}
  1017. @keyframes span-loading{0%{transform:scale(1);opacity:1}50%{transform:scale(1.2);opacity:1}100%{transform:scale(1);opacity:1}}
  1018. .scroll-request{text-align:center;height:15px;margin:15px auto}
  1019. .scroll-request span{display:inline-block;width:15px;height:100%;margin-right:8px;border-radius:50%;background:rgb(16,19,16);animation:scroll-load 1s ease infinite}
  1020. @keyframes scroll-load{0%,100%{transform:scale(1)} 50%{transform:scale(0)}}
  1021. .scroll-request span:nth-child(2){animation-delay:0.125s}
  1022. .scroll-request span:nth-child(3){animation-delay:0.25s}
  1023. .scroll-request span:nth-child(4){animation-delay:0.375s}
  1024. .imgResult-li{color:rgb(255,255,255,50%);font-size:20px}
  1025. .imgResult-li.imgResult-Current{color:white}
  1026. .imgResult-loading{animation:changeTextColor 1s ease-in infinite}
  1027. .imgResult-li:hover{cursor:pointer;color:white}
  1028. @keyframes changeTextColor{0%{color:rgba(255,255,255,1)}50%{color:rgba(255,255,255,.5)}100%{color:rgba(255,255,255,1)}}`;
  1029. GM_addStyle(css_waterfall);
  1030. }
  1031.  
  1032. class DownloadPanel{
  1033. constructor(){
  1034. this.addPanel();
  1035. }
  1036. async loadJS(){
  1037. let me =this;
  1038. const urlList = [['https://unpkg.com/jszip@3.6.0/dist/jszip.min.js','https://unpkg.com/file-saver@2.0.5/dist/FileSaver.min.js'],
  1039. ['https://cdn.jsdelivr.net/npm/jszip@3.6.0/dist/jszip.min.js','https://cdn.jsdelivr.net/npm/file-saver@2.0.5/dist/FileSaver.min.js'],
  1040. ['https://cdnjs.cloudflare.com/ajax/libs/jszip/3.6.0/jszip.min.js','https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js']];
  1041. const getJSFile = url => {
  1042. console.log(url);
  1043. return getRequest(url,{timeout: 10000,responseType: 'text'})
  1044. }
  1045. let index = GM_getValue('downloadPanel_url',0);
  1046. for (let k = 0; k < urlList.length; k++) {
  1047. const values = await Promise.all([getJSFile(urlList[index][0]), getJSFile(urlList[index][1])])
  1048. .catch(reason => {console.log(reason); return false});
  1049. if(values) {
  1050. values.forEach(v=> eval(v.responseText));
  1051. me.element.find("button[name=download]").attr("disabled",false);
  1052. GM_setValue('downloadPanel_url',index);
  1053. break;
  1054. }else{
  1055. index++;
  1056. if(index>=urlList.length){ index = urlList.length-index; }
  1057. continue;
  1058. }
  1059. }
  1060. }
  1061. addPanel(){
  1062. let me=this;
  1063. GM_addStyle(`#downloadPanel{margin:5px;}#downloadPanel button[name="download"]{height:38px;border:1px solid #ce9c9c;padding:0 9px;border-radius:4px;font-size:20px;}#downloadPanel input{height:30px;padding:4px;border-radius:4px;border:1px solid #ce9c9c;font-size:20px;}#downloadPanel input:focus{outline:0px;}#downloadPanel button[disabled]{color:#0006;cursor:not-allowed !important}.downloadform{font-size:20px;display:flex;height:40px;align-items:center;}#file-Info div[name=filename]{width:70%;display:inline-block;text-align:right}#file-Info div[name=state]{width:30%;display:inline}`)
  1064. me.element = $(`<div id="downloadPanel">
  1065. <div class="downloadform">
  1066. <button name="download" disabled="true">下载</button>
  1067. <span style="margin-left:10px;">番号: </span><input placeholder="ssni,abp" autocomplete="off" name="key"></input>
  1068. <span style="display:none">线程数</span><input style="display:none" name="poolLimit" value="3"></input>
  1069. <span class="progress-Info" style="margin-left:10px;">
  1070. <span name="sum"></span><span name="total"></span><span name="msg"></span>
  1071. </span>
  1072. </div>
  1073. <div id="file-Info"></div>
  1074. </div>`);
  1075. me.loadJS();
  1076. me.element.find("button[name=download]").on("click", function () {
  1077. let button = this;
  1078. button.disabled= true;
  1079. me.resetInfo();
  1080. let arrayList = me.getResultList();
  1081. if(arrayList.length){
  1082. me.element.find("span[name=total]").text(arrayList.length);
  1083. let poolLimit = me.element.find("input[name=poolLimit]").val();
  1084. me.downloadZip(poolLimit?poolLimit:5,arrayList).then(()=>(button.disabled=false));
  1085. }else{
  1086. me.element.find("span[name=msg]").text("无过滤结果");button.disabled=false;
  1087. }
  1088. });
  1089. }
  1090. getResultList(){
  1091. let list =[];
  1092. let key = this.element.find("input[name=key]").val().toUpperCase();
  1093. let keyArray = key.replace(",",",").split(",").filter(k=> k && k.trim());
  1094. $("div.box-b").each(function () {
  1095. let avid = $(this).find("date[name=avid]").text();
  1096. if(keyArray.length && (! keyArray.find(k=> avid.toUpperCase().indexOf(k)>-1)) ){
  1097. return ;
  1098. }
  1099. let url = $(this).find("img.lazy").attr("data-src");
  1100. let title = $(this).find("a[name='av-title']").attr("title");
  1101. let filename = `${avid} ${title.replace(/\//g,'_')}.jpg`;//标题中含有斜杠时,压缩包创建文件夹
  1102. list.push({avid:avid,url:url,filename:filename});
  1103. });
  1104. return list;
  1105. }
  1106. downloadZip(poolLimit,arrayList){
  1107. let me=this;
  1108. let sum = 0;
  1109. let zip = new JSZip();
  1110. return me.asyncPool(poolLimit,arrayList,function(item,array){
  1111. let $state=me.addFileInfo(item.avid);
  1112. return me.getImgResource(item.url).then(r =>{
  1113. if (r.status == '200') {
  1114. zip.file(item.filename, r.response);
  1115. $state.text(`✔`);
  1116. me.element.find(`span[name="sum"]`).text(`${++sum}/`);
  1117. } else {
  1118. $state.text(`❎`);
  1119. }
  1120. }).catch(err =>$state.text(`❎`));
  1121. }).then(() => zip.generateAsync({type:"blob"}).then(blob => saveAs(blob, "download.zip") ))
  1122. }
  1123. getImgResource(url){
  1124. return getRequest(url,{responseType: 'blob',headers : {Referer : url}})
  1125. }
  1126. //https://blog.csdn.net/ghostlpx/article/details/106431837
  1127. async asyncPool(poolLimit, array, iteratorFn) {
  1128. const ret = []
  1129. const executing = []
  1130. for (const item of array) {
  1131. const p = Promise.resolve().then(() => iteratorFn(item, array));
  1132. ret.push(p)
  1133. const e = p.then(() => executing.splice(executing.indexOf(e), 1))
  1134. executing.push(e)
  1135. if (executing.length >= poolLimit) {
  1136. await Promise.race(executing)
  1137. }
  1138. }
  1139. return Promise.all(ret)
  1140. }
  1141. addFileInfo(avid){
  1142. let $fileInfo=$(`<div style="width:50%;display:inline-block;float:left;"><div name="filename">${avid}:</div><div name="state">--></div></div>`);
  1143. this.element.find("#file-Info").append($fileInfo);
  1144. return $fileInfo.find("div[name=state]");
  1145. }
  1146. resetInfo(){
  1147. this.element.find("span.progress-Info span").text("");
  1148. this.element.find("#file-Info").empty();
  1149. }
  1150. }
  1151.  
  1152. class InputTagPanel{
  1153. constructor(key, placeholder) {
  1154. let me = this;
  1155. me.key = key;
  1156. me.data = Status.get(key) || [];
  1157. me.$panel = $(`<div class="input-tag-panel" name="${key}"></div>`);
  1158. me.$input = $(`<input type="text" autocomplete="off" value="" placeholder="${placeholder}">`);
  1159. me.$panel.append(me.$input);
  1160. me.data.forEach(function(value, index, array) {
  1161. me.$panel.append(`<div class="tag-div"><span>${value}</span><a href="#">X</a></div>`);
  1162. });
  1163. me.$panel.on('click', 'a', function(){
  1164. me.delete($(this));
  1165. });
  1166. me.$input.keyup(function(event) {
  1167. let key = me.$input.val().trim();
  1168. if (key && ((event.keyCode ? event.keyCode : event.which) === 13)) {
  1169. let keyArray = key.replace(",",",").split(",").filter(k=> k && k.trim());
  1170. me.add(keyArray);
  1171. }
  1172. });
  1173. GM_addStyle(`.input-tag-panel{display:flex;flex-direction:row;flex-wrap:wrap;margin:5px;align-content:flex-start;align-items:stretch;}.input-tag-panel>div{font-size:25px;height:25px;box-shadow:5px 5px 4px 0 rgb(0 0 0 / 10%);display:flex;line-height:25px;background-color:burlywood;float:left;padding:5px;margin-right:5px;margin-top:5px;border-radius:4px;align-items:center;}.input-tag-panel>div>a{color:white !important;font-size:20px;text-decoration:none;padding:0 5px 0 5px;}.input-tag-panel>div>a:hover{cursor:pointer;color:red;}.input-tag-panel input{width:100%;height:30px;border:solid 1px burlywood;border-radius:5px;padding:5px;font-size:20px;}.input-tag-panel input:focus{outline:none;}`);
  1174. }
  1175. add(keyArray) {
  1176. let me = this;
  1177. let $tag = [];
  1178. keyArray.forEach(key=>{
  1179. $tag.push(`<div class="tag-div"><span>${key}</span><a>X</a></div>`);
  1180. me.data.push(key);
  1181. })
  1182. me.$panel.append($tag).fadeIn();
  1183. Status.set(me.key, me.data);
  1184. }
  1185. delete($a) {
  1186. let me = this;
  1187. let key = $a.prev('span').text();
  1188. $a.parent('div').fadeOut();
  1189. let index = me.data.findIndex(v => key == v);
  1190. index > -1 && me.data.splice(index, 1);
  1191. Status.set(me.key, me.data);
  1192. }
  1193. }
  1194. class TabPanel{
  1195. constructor(){
  1196. let me=this;
  1197. GM_addStyle(`#tabPanel{display:none;width:600px;height:400px;background-color:white;border-radius:5px;position:fixed;right:15px;bottom:5px;color:black;text-align:center;border:1px solid #ccc;box-shadow:5px 5px 4px 0 rgb(0 0 0 / 10%);z-index:1000}#tabPanel *{box-sizing:content-box;}#tabPanel ul{padding:0;margin:0;}.tab_list{height:40px;background-color:#facbcb;}.tab_list ul li{list-style:none;float:left;height:40px;padding:0 20px;font-size:20px;border-radius:5px 5px 0 0;text-align:center;line-height:40px;cursor:pointer;}.tab_list .tab_current{background-color:white;}.tab_content{height:355px;}.tab_content_item{overflow-y:auto;display:none;width:100%;height:100%;background-color:white;}.tab_content_item::-webkit-scrollbar{width:7px}.tab_content_item::-webkit-scrollbar-track{border-radius:8px;background-color:#f5f5f5}.tab_content_item::-webkit-scrollbar-thumb{border-radius:8px;background-color:#c8c8c8}.close-div{position:absolute;right:0px;width:40px;height:40px;font-size:40px;line-height:30px;cursor:pointer;color:gray;transform:rotate(45deg);}.close-div:hover{color:black;}`)
  1198. me.element = $(`<div id="tabPanel">
  1199. <div class="tab_list">
  1200. <ul><li>批量下载</li><li>屏蔽词</li></ul>
  1201. <div class="close-div">+</div>
  1202. </div>
  1203. <div class="tab_content">
  1204. <div class="tab_content_item"></div>
  1205. <div class="tab_content_item"></div>
  1206. </div>
  1207. </div>`);
  1208. me.$li = me.element.find(".tab_list ul>li");
  1209. me.$item = me.element.find(".tab_content_item");
  1210. me.$li.on("click", function () {
  1211. me.show(me.$li.index(this));
  1212. });
  1213. me.element.find(".close-div").on("click", function () {
  1214. me.element.toggle();
  1215. });
  1216. $('body').append(me.element);
  1217. }
  1218. show(index=0){
  1219. let me =this;
  1220. me.$li.each((i,el)=>{$(el).removeClass("tab_current")});
  1221. me.$li.eq(index).addClass("tab_current");
  1222. me.$item.each((i,el)=>{$(el).hide()});
  1223. if(me.$item.eq(index).children().length==0){
  1224. me.addItem(index);
  1225. }
  1226. me.$item.eq(index).show();
  1227. me.element.show();
  1228. }
  1229. addItem(index){
  1230. let me= this;
  1231. switch (index) {
  1232. case 0:
  1233. let downloadPanel =new DownloadPanel();
  1234. me.$item.eq(index).append(downloadPanel.element);
  1235. break;
  1236. case 1:
  1237. let tag1 =new InputTagPanel("hiddenWord","标题:支持逗号隔开");
  1238. let tag2 =new InputTagPanel("hiddenAvid",`番号:支持逗号隔开,单个或系列如SSIS,OPX-123`);
  1239. me.$item.eq(index).append(tag1.$panel).append(tag2.$panel);
  1240. break;
  1241. }
  1242. }
  1243. static getInstance(){
  1244. if(!this.instance){
  1245. this.instance = new TabPanel();
  1246. }
  1247. return this.instance;
  1248. }
  1249. }
  1250. new Page();
  1251. })();