Pixiv Previewer

显示预览图(支持单图,多图,动图);动图压缩包下载;搜索页按热门度(收藏数)排序并显示收藏数,适配11月更新。

当前为 2021-11-10 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Pixiv Previewer
  3. // @namespace https://github.com/Ocrosoft/PixivPreviewer
  4. // @version 3.6.5
  5. // @description Display preview images (support single image, multiple images, moving images); Download animation(.zip); Sorting the search page by favorite count(and display it). Updated for the latest search page.
  6. // @description:zh-CN 显示预览图(支持单图,多图,动图);动图压缩包下载;搜索页按热门度(收藏数)排序并显示收藏数,适配11月更新。
  7. // @description:ja プレビュー画像の表示(単一画像、複数画像、動画のサポート); アニメーションのダウンロード(.zip); お気に入りの数で検索ページをソートします(そして表示します)。 最新の検索ページ用に更新されました。
  8. // @description:zh_TW 顯示預覽圖像(支持單幅圖像,多幅圖像,運動圖像); 下載動畫(.zip); 按收藏夾數對搜索頁進行排序(並顯示)。 已為最新的搜索頁面適配。
  9. // @author Ocrosoft
  10. // @match *://www.pixiv.net/*
  11. // @grant unsafeWindow
  12. // @compatible Chrome
  13. // ==/UserScript==
  14.  
  15. // https://greasyfork.org/zh-CN/scripts/417761-ilog
  16. // 后面把DoLog替换掉
  17. function ILog() {
  18. this.prefix = '';
  19.  
  20. this.v = function (value) {
  21. if (level <= this.LogLevel.Verbose) {
  22. console.log(this.prefix + value);
  23. }
  24. }
  25.  
  26. this.i = function (info) {
  27. if (level <= this.LogLevel.Info) {
  28. console.info(this.prefix + info);
  29. }
  30. }
  31.  
  32. this.w = function (warning) {
  33. if (level <= this.LogLevel.Warning) {
  34. console.warn(this.prefix + warning);
  35. }
  36. }
  37.  
  38. this.e = function (error) {
  39. if (level <= this.LogLevel.Error) {
  40. console.error(this.prefix + error);
  41. }
  42. }
  43.  
  44. this.d = function (element) {
  45. if (level <= this.LogLevel.Verbose) {
  46. console.log(element);
  47. }
  48. }
  49.  
  50. this.setLogLevel = function (logLevel) {
  51. level = logLevel;
  52. }
  53.  
  54. this.LogLevel = {
  55. Verbose: 0,
  56. Info: 1,
  57. Warning: 2,
  58. Error: 3,
  59. };
  60.  
  61. let level = this.LogLevel.Verbose;
  62. }
  63. var iLog = new ILog();
  64.  
  65. // https://greasyfork.org/zh-CN/scripts/417760-checkjquery
  66. var checkJQuery = function () {
  67. let jqueryCdns = [
  68. 'http://code.jquery.com/jquery-2.1.4.min.js',
  69. 'https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.4.min.js',
  70. 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js',
  71. 'https://cdn.staticfile.org/jquery/2.1.4/jquery.min.js',
  72. 'https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js',
  73. ];
  74. function isJQueryValid() {
  75. try {
  76. let wd = unsafeWindow;
  77. if (wd.jQuery && !wd.$) {
  78. wd.$ = wd.jQuery;
  79. }
  80. $();
  81. return true;
  82. } catch (exception) {
  83. return false;
  84. }
  85. }
  86. function insertJQuery(url) {
  87. let script = document.createElement('script');
  88. script.src = url;
  89. document.head.appendChild(script);
  90. return script;
  91. }
  92. function converProtocolIfNeeded(url) {
  93. let isHttps = location.href.indexOf('https://') != -1;
  94. let urlIsHttps = url.indexOf('https://') != -1;
  95.  
  96. if (isHttps && !urlIsHttps) {
  97. return url.replace('http://', 'https://');
  98. } else if (!isHttps && urlIsHttps) {
  99. return url.replace('https://', 'http://');
  100. }
  101. return url;
  102. }
  103. function waitAndCheckJQuery(cdnIndex, resolve) {
  104. if (cdnIndex >= jqueryCdns.length) {
  105. iLog.e('无法加载 JQuery,正在退出。');
  106. resolve(false);
  107. return;
  108. }
  109. let url = converProtocolIfNeeded(jqueryCdns[cdnIndex]);
  110. iLog.i('尝试第 ' + (cdnIndex + 1) + ' 个 JQuery CDN:' + url + '。');
  111. let script = insertJQuery(url);
  112. setTimeout(function () {
  113. if (isJQueryValid()) {
  114. iLog.i('已加载 JQuery。');
  115. resolve(true);
  116. } else {
  117. iLog.w('无法访问。');
  118. script.remove();
  119. waitAndCheckJQuery(cdnIndex + 1, resolve);
  120. }
  121. }, 100);
  122. }
  123. return new Promise(function (resolve) {
  124. if (isJQueryValid()) {
  125. iLog.i('已加载 jQuery。');
  126. resolve(true);
  127. } else {
  128. iLog.i('未发现 JQuery,尝试加载。');
  129. waitAndCheckJQuery(0, resolve);
  130. }
  131. });
  132. }
  133.  
  134. let Lang = {
  135. // 自动选择
  136. auto: -1,
  137. // 中文-中国大陆
  138. zh_CN: 0,
  139. // 英语-美国
  140. en_US: 1,
  141. // 俄语-俄罗斯
  142. ru_RU: 2,
  143. };
  144. let Texts = {};
  145. Texts[Lang.zh_CN] = {
  146. // 安装或更新后弹出的提示
  147. install_title: '欢迎使用 PixivPreviewer v',
  148. install_body: '<div style="position: absolute;left: 50%;top: 30%;font-size: 20px; color: white;transform:translate(-50%,0);"><p style="text-indent: 2em;">欢迎反馈问题和提出建议!><a style="color: green;" href="https://greasyfork.org/zh-CN/scripts/30766-pixiv-previewer/feedback" target="_blank">反馈页面</a><</p><br><p style="text-indent: 2em;">如果您是第一次使用,推荐到<a style="color: green;" href="https://greasyfork.org/zh-CN/scripts/30766-pixiv-previewer" target="_blank"> 详情页 </a>查看脚本介绍。</p></div>',
  149. // 设置项
  150. setting_language: '语言',
  151. setting_preview: '预览',
  152. setting_sort: '排序(仅搜索页生效)',
  153. setting_anime: '动图下载(动图预览及详情页生效)',
  154. setting_origin: '预览时优先显示原图(慢)',
  155. setting_previewDelay: '延迟显示预览图(毫秒)',
  156. setting_maxPage: '每次排序时统计的最大页数',
  157. setting_hideWork: '隐藏收藏数少于设定值的作品',
  158. setting_hideFav: '排序时隐藏已收藏的作品',
  159. setting_hideFollowed: '排序时隐藏已关注画师作品',
  160. setting_clearFollowingCache: '清除缓存',
  161. setting_clearFollowingCacheHelp: '关注画师信息会在本地保存一天,如果希望立即更新,请点击清除缓存',
  162. setting_followingCacheCleared: '已清除缓存,请刷新页面。',
  163. setting_blank: '使用新标签页打开作品详情页',
  164. setting_turnPage: '使用键盘←→进行翻页(排序后的搜索页)',
  165. setting_save: '保存设置',
  166. setting_reset: '重置脚本',
  167. setting_resetHint: '这会删除所有设置,相当于重新安装脚本,确定要重置吗?',
  168. setting_novelSort: '小说排序',
  169. setting_novelMaxPage: '小说排序时统计的最大页数',
  170. // 搜索时过滤值太高
  171. sort_noWork: '没有可以显示的作品',
  172. sort_getWorks: '正在获取第%1/%2页作品',
  173. sort_getBookmarkCount: '获取收藏数:%1/%2',
  174. sort_getPublicFollowing: '获取公开关注画师',
  175. sort_getPrivateFollowing: '获取私有关注画师',
  176. sort_filtering: '过滤%1收藏量低于%2的作品',
  177. sort_filteringHideFavorite: '已收藏和',
  178. sort_fullSizeThumb: '排序后展示全尺寸图片',
  179. // 小说排序
  180. nsort_getWorks: '正在获取第1%/2%页作品',
  181. nsort_sorting: '正在按收藏量排序',
  182. nsort_hideFav: '排序时隐藏已收藏的作品',
  183. nsort_hideFollowed: '排序时隐藏已关注作者作品'
  184. };
  185. // translate by google
  186. Texts[Lang.en_US] = {
  187. install_title: 'Welcome to PixivPreviewer v',
  188. install_body: '<div style="position: absolute;left: 50%;top: 30%;font-size: 20px; color: white;transform:translate(-50%,0);"><p style="text-indent: 2em;">Feedback questions and suggestions are welcome! ><a style="color: green;" href="https://greasyfork.org/zh-CN/scripts/30766-pixiv-previewer/feedback" target="_blank">Feedback Page</a><</p><br><p style="text-indent: 2em;">If you are using it for the first time, it is recommended to go to the<a style="color: green;" href="https://greasyfork.org/zh-CN/scripts/30766-pixiv-previewer" target="_blank"> Details Page </a>to see the script introduction.</p></div>',
  189. setting_language: 'Language',
  190. setting_preview: 'Preview',
  191. setting_sort: 'Sorting (Search page)',
  192. setting_anime: 'Animation download (Preview and Artwork page)',
  193. setting_origin: 'Display original image when preview (slow)',
  194. setting_previewDelay: 'Delay of display preview image(Million seconds)',
  195. setting_maxPage: 'Maximum number of pages counted per sort',
  196. setting_hideWork: 'Hide works with bookmark count less than set value',
  197. setting_hideFav: 'Hide favorites when sorting',
  198. setting_hideFollowed: 'Hide artworks of followed artists when sorting',
  199. setting_clearFollowingCache: 'Cache',
  200. setting_clearFollowingCacheHelp: 'The folloing artists info. will be saved locally for one day, if you want to update immediately, please click this to clear cache',
  201. setting_followingCacheCleared: 'Success, please refresh the page.',
  202. setting_blank: 'Open works\' details page in new tab',
  203. setting_turnPage: 'Use ← → to turn pages (Search page)',
  204. setting_save: 'Save',
  205. setting_reset: 'Reset',
  206. setting_resetHint: 'This will delete all settings and set it to default. Are you sure?',
  207. setting_novelSort: 'Sorting (Novel)',
  208. setting_novelMaxPage: 'Maximum number of pages counted for novel sorting',
  209. sort_noWork: 'No works to display',
  210. sort_getWorks: 'Getting artworks of page: %1 of %2',
  211. sort_getBookmarkCount: 'Getting bookmark count of artworks:%1 of %2',
  212. sort_getPublicFollowing: 'Getting public following list',
  213. sort_getPrivateFollowing: 'Getting private following list',
  214. sort_filtering: 'Filtering%1works with bookmark count less than %2',
  215. sort_filteringHideFavorite: ' favorited works and ',
  216. sort_fullSizeThumb: 'Display not cropped images.',
  217. nsort_getWorks: 'Getting novels of page: 1% of 2%',
  218. nsort_sorting: 'Sorting by bookmark cound',
  219. nsort_hideFav: 'Hide favorites when sorting',
  220. nsort_hideFollowed: 'Hide artworks of followed authors when sorting'
  221. };
  222. // RU: перевод от vanja-san
  223. Texts[Lang.ru_RU] = {
  224. install_title: 'Добро пожаловать в PixivPreviewer v',
  225. install_body: '<div style="position: absolute;left: 50%;top: 30%;font-size: 20px; color: white;transform:translate(-50%,0);"><p style="text-indent: 2em;">Вопросы и предложения приветствуются! ><a style="color: green;" href="https://greasyfork.org/zh-CN/scripts/30766-pixiv-previewer/feedback" target="_blank">Страница обратной связи</a><</p><br><p style="text-indent: 2em;">Если вы используете это впервые, рекомендуется перейти к<a style="color: green;" href="https://greasyfork.org/zh-CN/scripts/30766-pixiv-previewer" target="_blank"> Странице подробностей </a>, чтобы посмотреть введение в скрипт.</p></div>',
  226. setting_language: 'Язык',
  227. setting_preview: 'Предпросмотр',
  228. setting_sort: 'Сортировка (Страница поиска)',
  229. setting_anime: 'Анимация скачивания (Страницы предпросмотра и Artwork)',
  230. setting_origin: 'При предпросмотре, показывать изображения с оригинальным качеством (медленно)',
  231. setting_previewDelay: 'Задержка отображения предпросмотра изображения (Миллион секунд)',
  232. setting_maxPage: 'Максимальное количество страниц, подсчитанных за сортировку',
  233. setting_hideWork: 'Скрыть работы с количеством закладок меньше установленного значения',
  234. setting_hideFav: 'При сортировке, скрыть избранное',
  235. setting_hideFollowed: 'При сортировке, скрыть работы художников на которых подписаны',
  236. setting_clearFollowingCache: 'Кэш',
  237. setting_clearFollowingCacheHelp: 'Следующая информация о художниках будет сохранена локально в течение одного дня, если вы хотите обновить её немедленно, нажмите на эту кнопку, чтобы очистить кэш',
  238. setting_followingCacheCleared: 'Готово, обновите страницу.',
  239. setting_blank: 'Открывать страницу с описанием работы на новой вкладке',
  240. setting_turnPage: 'Использовать ← → для перелистывания страниц (Страница поиска)',
  241. setting_save: 'Сохранить',
  242. setting_reset: 'Сбросить',
  243. setting_resetHint: 'Это удалит все настройки и установит их по умолчанию. Продолжить?',
  244. setting_novelSort: Texts[Lang.en_US].setting_novelSort,
  245. setting_novelMaxPage: Texts[Lang.en_US].setting_novelMaxPage,
  246. sort_noWork: 'Нет работ для отображения',
  247. sort_getWorks: 'Получение иллюстраций страницы: %1 из %2',
  248. sort_getBookmarkCount: 'Получение количества закладок artworks:%1 из %2',
  249. sort_getPublicFollowing: 'Получение публичного списка подписок',
  250. sort_getPrivateFollowing: 'Получение приватного списка подписок',
  251. sort_filtering: 'Фильтрация %1 работ с количеством закладок меньше чем %2',
  252. sort_filteringHideFavorite: ' избранные работы и ',
  253. sort_fullSizeThumb: 'Показать неотредактированное изображение',
  254. nsort_getWorks: Texts[Lang.en_US].nsort_getWorks,
  255. nsort_sorting: Texts[Lang.en_US].nsort_sorting,
  256. nsort_hideFav: Texts[Lang.en_US].nsort_hideFav,
  257. nsort_hideFollowed: Texts[Lang.en_US].nsort_hideFollowed
  258. };
  259.  
  260. let LogLevel = {
  261. None: 0,
  262. Error: 1,
  263. Warning: 2,
  264. Info: 3,
  265. Elements: 4,
  266. };
  267. function DoLog(level, msgOrElement) {
  268. if (level <= g_logLevel) {
  269. let prefix = '%c';
  270. let param = '';
  271.  
  272. if (level == LogLevel.Error) {
  273. prefix += '[Error]';
  274. param = 'color:#ff0000';
  275. } else if (level == LogLevel.Warning) {
  276. prefix += '[Warning]';
  277. param = 'color:#ffa500';
  278. } else if (level == LogLevel.Info) {
  279. prefix += '[Info]';
  280. param = 'color:#000000';
  281. } else if (level == LogLevel.Elements) {
  282. prefix += 'Elements';
  283. param = 'color:#000000';
  284. }
  285.  
  286. if (level != LogLevel.Elements) {
  287. console.log(prefix + msgOrElement, param);
  288. } else {
  289. console.log(msgOrElement);
  290. }
  291.  
  292. if (++g_logCount > 512) {
  293. //console.clear();
  294. g_logCount = 0;
  295. }
  296. }
  297. }
  298.  
  299. // 语言
  300. let g_language = Lang.zh_CN;
  301. // 版本号,第三位不需要跟脚本的版本号对上,第三位更新只有需要弹更新提示的时候才需要更新这里
  302. let g_version = '3.6.0';
  303. // 添加收藏需要这个
  304. let g_csrfToken = '';
  305. // 打的日志数量,超过一定数值清空控制台
  306. let g_logCount = 0;
  307. // 当前页面类型
  308. let g_pageType = -1;
  309. // 图片详情页的链接,使用时替换 #id#
  310. let g_artworkUrl = '/artworks/#id#';
  311. // 获取图片链接的链接
  312. let g_getArtworkUrl = '/ajax/illust/#id#/pages';
  313. // 获取动图下载链接的链接
  314. let g_getUgoiraUrl = '/ajax/illust/#id#/ugoira_meta';
  315. // 获取小说列表的链接
  316. let g_getNovelUrl = '/ajax/search/novels/#key#?word=#key#&p=#page#'
  317. // 鼠标位置
  318. let g_mousePos = { x: 0, y: 0 };
  319. // 加载中图片
  320. let g_loadingImage = 'https://pp-1252089172.cos.ap-chengdu.myqcloud.com/loading.gif';
  321. // 页面打开时的 url
  322. let initialUrl = location.href;
  323. // 默认设置,仅用于首次脚本初始化
  324. let g_defaultSettings = {
  325. 'lang': -1,
  326. 'enablePreview': 1,
  327. 'enableSort': 1,
  328. 'enableAnimeDownload': 1,
  329. 'original': 0,
  330. 'previewDelay': 200,
  331. 'pageCount': 3,
  332. 'favFilter': 0,
  333. 'hideFavorite': 0,
  334. 'hideFollowed': 0,
  335. 'linkBlank': 1,
  336. 'pageByKey': 0,
  337. 'fullSizeThumb': 0,
  338. 'enableNovelSort': 1,
  339. 'novelPageCount': 3,
  340. 'novelHideFavorite': 0,
  341. 'novelHideFollowed': 0,
  342. 'logLevel': 1,
  343. 'version': g_version,
  344. };
  345. // 设置
  346. let g_settings;
  347. // 日志等级
  348. let g_logLevel = LogLevel.Error;
  349. // 排序时同时请求收藏量的 Request 数量,没必要太多,并不会加快速度
  350. let g_maxXhr = 64;
  351. // 排序是否完成(如果排序时页面出现了非刷新切换,强制刷新)
  352. let g_sortComplete = true;
  353.  
  354. // 页面相关的一些预定义,包括处理页面元素等
  355. let PageType = {
  356. // 搜索(不包含小说搜索)
  357. Search: 0,
  358. // 关注的新作品
  359. BookMarkNew: 1,
  360. // 发现
  361. Discovery: 2,
  362. // 用户主页
  363. Member: 3,
  364. // 首页
  365. Home: 4,
  366. // 排行榜
  367. Ranking: 5,
  368. // 大家的新作品
  369. NewIllust: 6,
  370. // R18
  371. R18: 7,
  372. // 自己的收藏页
  373. BookMark: 8,
  374. // 动态
  375. Stacc: 9,
  376. // 作品详情页(处理动图预览及下载)
  377. Artwork: 10,
  378. // 小说页
  379. NovelSearch: 11,
  380.  
  381. // 总数
  382. PageTypeCount: 12,
  383. };
  384. let Pages = {};
  385. /* Pages 必须实现的函数
  386. * PageTypeString: string,字符串形式的 PageType
  387. * bool CheckUrl: function(string url),用于检查一个 url 是否是当前页面的目标 url
  388. * ReturnMap ProcessPageElements: function(),处理页面(寻找图片元素、添加属性等),返回 ReturnMap
  389. * ReturnMap GetProcessedPageElements: function(), 返回上一次 ProcessPageElements 的返回值(如果没有上次调用则调用一次)
  390. * Object GetToolBar: function(), 返回工具栏元素(右下角那个,用来放设置按钮)
  391. * HasAutoLoad: bool,表示这个页面是否有自动加载功能
  392. */
  393. let ReturnMapSample = {
  394. // 页面是否加载完成,false 意味着后面的成员无效
  395. loadingComplete: false,
  396. // 控制元素,每个图片的鼠标响应元素
  397. controlElements: [],
  398. // 可有可无,如果为 true,强制重新刷新预览功能
  399. forceUpdate: false,
  400. };
  401. let ControlElementsAttributesSample = {
  402. // 图片信息,内容如下:
  403. // [必需] 图片 id
  404. illustId: 0,
  405. // [必需] 图片类型(0:普通图片,2:动图)
  406. illustType: 0,
  407. // [必需] 页数
  408. pageCount: 1,
  409. // [可选] 标题
  410. title: '',
  411. // [可选] 作者 id
  412. userId: 0,
  413. // [可选] 作者昵称
  414. userName: '',
  415. // [可选] 收藏数
  416. bookmarkCount: 0,
  417. };
  418.  
  419. function findToolbarCommon() {
  420. // 目前第三级div,除了目标div外,子元素都是div
  421. return $('#root>div>div>ul').get(0);
  422. }
  423. function findToolbarOld() {
  424. return $('._toolmenu').get(0);
  425. }
  426. function convertThumbUrlToSmall(thumbUrl) {
  427. // 目前发现有以下两种格式的缩略图
  428. // https://i.pximg.net/c/128x128/custom-thumb/img/2021/01/31/20/35/53/87426718_p0_custom1200.jpg
  429. // https://i.pximg.net/c/128x128/img-master/img/2021/01/31/10/57/06/87425082_p0_square1200.jpg
  430. let replace1 = 'c/540x540_70/img-master';
  431. //let replace1 = 'img-master'; // 这个是转到regular的,比small的大多了,会很慢
  432. let replace2 = '_master';
  433. return thumbUrl.replace(/c\/.*\/custom-thumb/, replace1).replace('_custom', replace2)
  434. .replace(/c\/.*\/img-master/, replace1).replace('_square', replace2);
  435. }
  436. function processElementListCommon(lis) {
  437. lis.each(function (i, e) {
  438. let li = $(e);
  439.  
  440. // 只填充必须的几个,其他的目前用不着
  441. let ctlAttrs = {
  442. illustId: 0,
  443. illustType: 0,
  444. pageCount: 1,
  445. };
  446.  
  447. let img = $(li.find('img').get(0));
  448. let imageLink = img.parent().parent();
  449. let additionDiv = img.parent().prev();
  450. let animationSvg = img.parent().find('svg');
  451. let pageCountSpan = additionDiv.find('span');
  452.  
  453. if (img == null || imageLink == null) {
  454. DoLog(LogLevel.Warning, 'Can not found img or imageLink, skip this.');
  455. return;
  456. }
  457.  
  458. let link = imageLink.attr('href');
  459. if (link == null) {
  460. DoLog(LogLevel.Warning, 'Invalid href, skip this.');
  461. return;
  462. }
  463. let linkMatched = link.match(/artworks\/(\d+)/);
  464. let illustId = '';
  465. if (linkMatched) {
  466. ctlAttrs.illustId = linkMatched[1];
  467. } else {
  468. DoLog(LogLevel.Error, 'Get illustId failed, skip this list item!');
  469. return;
  470. }
  471. if (animationSvg.length > 0) {
  472. ctlAttrs.illustType = 2;
  473. }
  474. if (pageCountSpan.length > 0) {
  475. ctlAttrs.pageCount = parseInt(pageCountSpan.text());
  476. }
  477.  
  478. // 添加 attr
  479. let control = li.children('div:first').children('div:first');
  480. control.attr({
  481. 'illustId': ctlAttrs.illustId,
  482. 'illustType': ctlAttrs.illustType,
  483. 'pageCount': ctlAttrs.pageCount
  484. });
  485.  
  486. control.addClass('pp-control');
  487. });
  488. }
  489.  
  490. Pages[PageType.Search] = {
  491. PageTypeString: 'SearchPage',
  492. CheckUrl: function (url) {
  493. // 没有 /artworks 的页面不支持
  494. return /^https?:\/\/www.pixiv.net\/tags\/.*\/(artworks|illustrations|manga)/.test(url) ||
  495. /^https?:\/\/www.pixiv.net\/en\/tags\/.*\/(artworks|illustrations|manga)/.test(url);
  496. },
  497. ProcessPageElements: function () {
  498. let returnMap = {
  499. loadingComplete: false,
  500. controlElements: [],
  501. };
  502.  
  503. let sections = $('section');
  504. DoLog(LogLevel.Info, 'Page has ' + sections.length + ' <section>.');
  505. DoLog(LogLevel.Elements, sections);
  506. // 先对 section 进行评分
  507. let sectionIndex = -1;
  508. let bestScore = -99;
  509. sections.each(function (i, e) {
  510. let section = $(e);
  511. let score = 0;
  512. if (section.find('ul').length > 0) {
  513. let childrenCount = section.children().length;
  514. if (childrenCount != 2) {
  515. DoLog(LogLevel.Warning, '<ul> was found in this <section>, but it has ' + childrenCount + ' children!');
  516. score--;
  517. }
  518. let ul = section.find('ul');
  519. if (ul.length > 1) {
  520. DoLog(LogLevel.Warning, 'This section has more than one <ul>?');
  521. score--;
  522. }
  523. if ($(ul.parent().get(0)).css('display') == 'none' || $(ul.get(0)).css('display') == 'none') {
  524. DoLog(LogLevel.Info, '<ul> or it\'s parentNode is not visible now, continue waiting.');
  525. sectionIndex = -1;
  526. bestScore = 999;
  527. return false;
  528. }
  529. if ($(ul.get(0)).next().length === 0) {
  530. DoLog(LogLevel.Info, 'Page selector not exists, continue waiting.');
  531. sectionIndex = -1;
  532. bestScore = 999;
  533. return false;
  534. }
  535. let lis = ul.find('li');
  536. if (lis.length === 0) {
  537. DoLog(LogLevel.Info, 'This <ul> has 0 children, will be skipped.');
  538. return false;
  539. }
  540. if ($(lis.get(0)).find('figure').length > 0) {
  541. DoLog(LogLevel.Warning, '<figure> was found in the first <li>, continue waiting.');
  542. sectionIndex = -1;
  543. bestScore = 999;
  544. return false;
  545. }
  546. if (lis.length > 4) {
  547. score += 5;
  548. }
  549. // 正确的会在后面
  550. if (score >= bestScore) {
  551. bestScore = score;
  552. sectionIndex = i;
  553. }
  554. } else {
  555. DoLog(LogLevel.Info, 'This section(' + i + ' is not has <ul>, will be skipped.');
  556. }
  557. });
  558.  
  559. if (sectionIndex == -1) {
  560. if (bestScore < 100) {
  561. DoLog(LogLevel.Error, 'No suitable <section>!');
  562. }
  563. return returnMap;
  564. }
  565.  
  566. let lis = $(sections[sectionIndex]).find('ul').find('li');
  567. processElementListCommon(lis);
  568. returnMap.controlElements = $('.pp-control');
  569. this.private.pageSelector = $($(sections[sectionIndex]).find('ul').get(0)).next().get(0);
  570. returnMap.loadingComplete = true;
  571. this.private.imageListConrainer = $(sections[sectionIndex]).find('ul').get(0);
  572.  
  573. DoLog(LogLevel.Info, 'Process page elements complete.');
  574. DoLog(LogLevel.Elements, returnMap);
  575.  
  576. this.private.returnMap = returnMap;
  577. return returnMap;
  578. },
  579. GetProcessedPageElements: function () {
  580. if (this.private.returnMap == null) {
  581. return this.ProcessPageElements();
  582. }
  583. return this.private.returnMap;
  584. },
  585. GetToolBar: function () {
  586. return findToolbarCommon();
  587. },
  588. // 搜索页有 lazyload,不开排序的情况下,最后几张图片可能会无法预览。这里把它当做自动加载处理
  589. HasAutoLoad: true,
  590. GetImageListContainer: function () {
  591. return this.private.imageListConrainer;
  592. },
  593. GetFirstImageElement: function () {
  594. return $(this.private.imageListConrainer).find('li').get(0);
  595. },
  596. GetPageSelector: function () {
  597. return this.private.pageSelector;
  598. },
  599. private: {
  600. imageListContainer: null,
  601. pageSelector: null,
  602. returnMap: null,
  603. },
  604. };
  605. Pages[PageType.BookMarkNew] = {
  606. PageTypeString: 'BookMarkNewPage',
  607. CheckUrl: function (url) {
  608. return /^https:\/\/www.pixiv.net\/bookmark_new_illust.php.*/.test(url) ||
  609. /^https:\/\/www.pixiv.net\/bookmark_new_illust_r18.php.*/.test(url);
  610. },
  611. ProcessPageElements: function () {
  612. let returnMap = {
  613. loadingComplete: false,
  614. controlElements: [],
  615. };
  616.  
  617. let containerDiv = $('#js-mount-point-latest-following').children('div:first');
  618. if (containerDiv.length > 0) {
  619. DoLog(LogLevel.Info, 'Found container div.');
  620. DoLog(LogLevel.Elements, containerDiv);
  621. } else {
  622. DoLog(LogLevel.Error, 'Can not found container div.');
  623. return returnMap;
  624. }
  625.  
  626. containerDiv.children().each(function (i, e) {
  627. let _this = $(e);
  628.  
  629. let figure = _this.find('figure');
  630. if (figure.length === 0) {
  631. DoLog(LogLevel.Warning, 'Can not found <fingure>, skip this element.');
  632. return;
  633. }
  634.  
  635. let link = figure.children('div:first').children('a:first');
  636. if (link.length === 0) {
  637. DoLog(LogLevel.Warning, 'Can not found <a>, skip this element.');
  638. return;
  639. }
  640.  
  641. let ctlAttrs = {
  642. illustId: 0,
  643. illustType: 0,
  644. pageCount: 1,
  645. };
  646.  
  647. let href = link.attr('href');
  648. if (href == null || href === '') {
  649. DoLog(LogLevel.Warning, 'No href found, skip.');
  650. return;
  651. } else {
  652. let matched = href.match(/artworks\/(\d+)/);
  653. if (matched) {
  654. ctlAttrs.illustId = matched[1];
  655. } else {
  656. DoLog(LogLevel.Warning, 'Can not found illust id, skip.');
  657. return;
  658. }
  659. }
  660.  
  661. if (link.children().length > 1) {
  662. if (link.children('div:first').find('span').length > 0) {
  663. let span = link.children('div:first').children('span:first');
  664. if (span.length === 0) {
  665. DoLog(LogLevel.Warning, 'Can not found <span>, skip this element.');
  666. return;
  667. }
  668. ctlAttrs.pageCount = span.text();
  669. } else {
  670. ctlAttrs.illustType = 2;
  671. }
  672. }
  673.  
  674. let control = figure.children('div:first');
  675. control.attr({
  676. 'illustId': ctlAttrs.illustId,
  677. 'illustType': ctlAttrs.illustType,
  678. 'pageCount': ctlAttrs.pageCount
  679. });
  680.  
  681. returnMap.controlElements.push(control.get(0));
  682. });
  683.  
  684. DoLog(LogLevel.Info, 'Process page elements complete.');
  685. DoLog(LogLevel.Elements, returnMap);
  686.  
  687. returnMap.loadingComplete = true;
  688. this.private.returnMap = returnMap;
  689. return returnMap;
  690. },
  691. GetProcessedPageElements: function () {
  692. if (this.private.returnMap == null) {
  693. return this.ProcessPageElements();
  694. }
  695. return this.private.returnMap;
  696. },
  697. GetToolBar: function () {
  698. return findToolbarOld();
  699. },
  700. HasAutoLoad: false,
  701. private: {
  702. returnMap: null,
  703. },
  704. };
  705. Pages[PageType.Discovery] = {
  706. PageTypeString: 'DiscoveryPage',
  707. CheckUrl: function (url) {
  708. return /^https?:\/\/www.pixiv.net\/discovery.*/.test(url);
  709. },
  710. ProcessPageElements: function () {
  711. let returnMap = {
  712. loadingComplete: false,
  713. controlElements: [],
  714. };
  715.  
  716. let containerDiv = $('.gtm-illust-recommend-zone');
  717. if (containerDiv.length > 0) {
  718. DoLog(LogLevel.Info, 'Found container div.');
  719. DoLog(LogLevel.Elements, containerDiv);
  720. } else {
  721. DoLog(LogLevel.Error, 'Can not found container div.');
  722. return returnMap;
  723. }
  724.  
  725. let lis = containerDiv.find('ul').children('li');
  726. processElementListCommon(lis);
  727. returnMap.controlElements = $('.pp-control');
  728. returnMap.loadingComplete = true;
  729.  
  730. DoLog(LogLevel.Info, 'Process page elements complete.');
  731. DoLog(LogLevel.Elements, returnMap);
  732.  
  733. this.private.returnMap = returnMap;
  734. return returnMap;
  735. },
  736. GetProcessedPageElements: function () {
  737. if (this.private.returnMap == null) {
  738. return this.ProcessPageElements();
  739. }
  740. return this.private.returnMap;
  741. },
  742. GetToolBar: function () {
  743. return findToolbarCommon();
  744. },
  745. HasAutoLoad: true,
  746. private: {
  747. returnMap: null,
  748. },
  749. };
  750. Pages[PageType.Member] = {
  751. PageTypeString: 'MemberPage/MemberIllustPage/MemberBookMark',
  752. CheckUrl: function (url) {
  753. return /^https?:\/\/www.pixiv.net\/users\/\d+/.test(url);
  754. },
  755. ProcessPageElements: function () {
  756. let returnMap = {
  757. loadingComplete: false,
  758. controlElements: [],
  759. };
  760.  
  761. let sections = $('section');
  762. DoLog(LogLevel.Info, 'Page has ' + sections.length + ' <section>.');
  763. DoLog(LogLevel.Elements, sections);
  764.  
  765. let lis = sections.find('ul').find('li');
  766. processElementListCommon(lis);
  767. returnMap.controlElements = $('.pp-control');
  768. returnMap.loadingComplete = true;
  769.  
  770. DoLog(LogLevel.Info, 'Process page elements complete.');
  771. DoLog(LogLevel.Elements, returnMap);
  772.  
  773. this.private.returnMap = returnMap;
  774. return returnMap;
  775. },
  776. GetProcessedPageElements: function () {
  777. if (this.private.returnMap == null) {
  778. return this.ProcessPageElements();
  779. }
  780. return this.private.returnMap;
  781. },
  782. GetToolBar: function () {
  783. return findToolbarCommon();
  784. },
  785. // 跟搜索页一样的情况
  786. HasAutoLoad: true,
  787. private: {
  788. returnMap: null,
  789. },
  790. };
  791. Pages[PageType.Home] = {
  792. PageTypeString: 'HomePage',
  793. CheckUrl: function (url) {
  794. return /https?:\/\/www.pixiv.net\/?$/.test(url) ||
  795. /https?:\/\/www.pixiv.net\/en\/?$/.test(url);
  796. },
  797. ProcessPageElements: function () {
  798. let returnMap = {
  799. loadingComplete: false,
  800. controlElements: [],
  801. forceUpdate: false,
  802. };
  803.  
  804. let illust_div = $('div[type="illust"]');
  805.  
  806. DoLog(LogLevel.Info, 'This page has ' + illust_div.length + ' illust <div>.');
  807. if (illust_div.length < 1) {
  808. DoLog(LogLevel.Warning, 'Less than one <div>, continue waiting.');
  809. return returnMap;
  810. }
  811.  
  812. // 实际里面还套了一个 div,处理一下,方便一点
  813. let illust_div_c = [];
  814. illust_div.each(function (i, e) {
  815. illust_div_c.push($(e).children('div:first'));
  816. });
  817. illust_div = illust_div_c;
  818. $.each(illust_div, function (i, e) {
  819. let _this = $(e);
  820.  
  821. let a = _this.children('a:first');
  822. if (a.length == 0 || a.attr('href').indexOf('artworks') == -1) {
  823. DoLog(LogLevel.Warning, 'No href or an invalid href was found, skip this.');
  824. return;
  825. }
  826.  
  827. let ctlAttrs = {
  828. illustId: 0,
  829. illustType: 0,
  830. pageCount: 1,
  831. };
  832.  
  833. let illustId = a.attr('href').match(/\d+/);
  834. if (illustId == null) {
  835. DoLog(LogLevel.Warning, 'Can not found illust id of this image, skip.');
  836. return;
  837. } else {
  838. ctlAttrs.illustId = illustId[0];
  839. }
  840. let pageCount = a.children('div:first').find('span');
  841. if (pageCount.length > 0) {
  842. ctlAttrs.pageCount = parseInt($(pageCount.get(pageCount.length - 1)).text());
  843. }
  844. if ($(a.children('div').get(1)).find('svg').length > 0) {
  845. ctlAttrs.illustType = 2;
  846. }
  847.  
  848. let control = a;
  849. if (control.attr('illustId') != ctlAttrs.illustId) {
  850. returnMap.forceUpdate = true;
  851. }
  852. control.attr({
  853. 'illustId': ctlAttrs.illustId,
  854. 'illustType': ctlAttrs.illustType,
  855. 'pageCount': ctlAttrs.pageCount
  856. });
  857.  
  858. returnMap.controlElements.push(control.get(0));
  859. });
  860.  
  861. DoLog(LogLevel.Info, 'Process page elements complete.');
  862. DoLog(LogLevel.Elements, returnMap);
  863.  
  864. returnMap.loadingComplete = true;
  865. this.private.returnMap = returnMap;
  866. return returnMap;
  867. },
  868. GetProcessedPageElements: function () {
  869. if (this.private.returnMap == null) {
  870. return this.ProcessPageElements();
  871. }
  872. return this.private.returnMap;
  873. },
  874. GetToolBar: function () {
  875. return findToolbarCommon();
  876. },
  877. HasAutoLoad: true,
  878. private: {
  879. returnMap: null,
  880. },
  881. };
  882. Pages[PageType.Ranking] = {
  883. PageTypeString: 'RankingPage',
  884. CheckUrl: function (url) {
  885. return /^https?:\/\/www.pixiv.net\/ranking.php.*/.test(url);
  886. },
  887. ProcessPageElements: function () {
  888. let returnMap = {
  889. loadingComplete: false,
  890. controlElements: [],
  891. };
  892.  
  893. let works = $('._work');
  894.  
  895. DoLog(LogLevel.Info, 'Found .work, length: ' + works.length);
  896. DoLog(LogLevel.Elements, works);
  897.  
  898. works.each(function (i, e) {
  899. let _this = $(e);
  900.  
  901. let ctlAttrs = {
  902. illustId: 0,
  903. illustType: 0,
  904. pageCount: 1,
  905. };
  906.  
  907. let href = _this.attr('href');
  908.  
  909. if (href == null || href === '') {
  910. DoLog('Can not found illust id, skip this.');
  911. return;
  912. }
  913.  
  914. let matched = href.match(/artworks\/(\d+)/);
  915. if (matched) {
  916. ctlAttrs.illustId = matched[1];
  917. } else {
  918. DoLog('Can not found illust id, skip this.');
  919. return;
  920. }
  921.  
  922. if (_this.hasClass('multiple')) {
  923. ctlAttrs.pageCount = _this.find('.page-count').find('span').text();
  924. }
  925.  
  926. if (_this.hasClass('ugoku-illust')) {
  927. ctlAttrs.illustType = 2;
  928. }
  929.  
  930. // 添加 attr
  931. _this.attr({
  932. 'illustId': ctlAttrs.illustId,
  933. 'illustType': ctlAttrs.illustType,
  934. 'pageCount': ctlAttrs.pageCount
  935. });
  936.  
  937. returnMap.controlElements.push(e);
  938. });
  939.  
  940. returnMap.loadingComplete = true;
  941.  
  942. DoLog(LogLevel.Info, 'Process page elements complete.');
  943. DoLog(LogLevel.Elements, returnMap);
  944.  
  945. this.private.returnMap = returnMap;
  946. return returnMap;
  947. },
  948. GetProcessedPageElements: function () {
  949. if (this.private.returnMap == null) {
  950. return this.ProcessPageElements();
  951. }
  952. return this.private.returnMap;
  953. },
  954. GetToolBar: function () {
  955. return findToolbarOld();
  956. },
  957. HasAutoLoad: true,
  958. private: {
  959. returnMap: null,
  960. },
  961. };
  962. Pages[PageType.NewIllust] = {
  963. PageTypeString: 'NewIllustPage',
  964. CheckUrl: function (url) {
  965. return /^https?:\/\/www.pixiv.net\/new_illust.php.*/.test(url);
  966. },
  967. ProcessPageElements: function () {
  968. let returnMap = {
  969. loadingComplete: false,
  970. controlElements: [],
  971. };
  972.  
  973. let ul = $('#root').find('ul:first');
  974. if (ul.length === 0) {
  975. DoLog(LogLevel.Error, 'Can not found <ul>!');
  976. return returnMap;
  977. }
  978.  
  979. ul.find('li').each(function (i, e) {
  980. let _this = $(e);
  981.  
  982. let link = _this.find('a:first');
  983. let href = link.attr('href');
  984. if (href == null || href === '') {
  985. DoLog(LogLevel.Error, 'Can not found illust id, skip this.');
  986. return;
  987. }
  988.  
  989. let ctlAttrs = {
  990. illustId: 0,
  991. illustType: 0,
  992. pageCount: 1,
  993. };
  994.  
  995. let matched = href.match(/artworks\/(\d+)/);
  996. if (matched) {
  997. ctlAttrs.illustId = matched[1];
  998. } else {
  999. DoLog(LogLevel.Warning, 'Can not found illust id, skip this.');
  1000. return;
  1001. }
  1002.  
  1003. if (link.children().length > 1) {
  1004. let span = link.find('svg').parent().parent().next();
  1005. if (span.length > 0 && span.get(0).tagName == 'SPAN') {
  1006. ctlAttrs.pageCount = span.text();
  1007. } else if (link.find('svg').length > 0) {
  1008. ctlAttrs.illustType = 2;
  1009. }
  1010. }
  1011.  
  1012. let control = _this.children('div:first').children('div:first');
  1013. control.attr({
  1014. 'illustId': ctlAttrs.illustId,
  1015. 'illustType': ctlAttrs.illustType,
  1016. 'pageCount': ctlAttrs.pageCount
  1017. });
  1018.  
  1019. returnMap.controlElements.push(control.get(0));
  1020. });
  1021.  
  1022. returnMap.loadingComplete = true;
  1023.  
  1024. DoLog(LogLevel.Info, 'Process page elements complete.');
  1025. DoLog(LogLevel.Elements, returnMap);
  1026.  
  1027. this.private.returnMap = returnMap;
  1028. return returnMap;
  1029. },
  1030. GetProcessedPageElements: function () {
  1031. if (this.private.returnMap == null) {
  1032. return this.ProcessPageElements();
  1033. }
  1034. return this.private.returnMap;
  1035. },
  1036. GetToolBar: function () {
  1037. return findToolbarCommon();
  1038. },
  1039. HasAutoLoad: true,
  1040. private: {
  1041. returnMap: null,
  1042. },
  1043. };
  1044. Pages[PageType.R18] = {
  1045. PageTypeString: 'R18Page',
  1046. CheckUrl: function (url) {
  1047. return /^https?:\/\/www.pixiv.net\/cate_r18.php.*/.test(url);
  1048. },
  1049. ProcessPageElements: function () {
  1050. //
  1051. },
  1052. GetToolBar: function () {
  1053. //
  1054. },
  1055. HasAutoLoad: false,
  1056. };
  1057. Pages[PageType.BookMark] = {
  1058. PageTypeString: 'BookMarkPage',
  1059. CheckUrl: function (url) {
  1060. return /^https:\/\/www.pixiv.net\/bookmark.php\/?$/.test(url);
  1061. },
  1062. ProcessPageElements: function () {
  1063. let returnMap = {
  1064. loadingComplete: false,
  1065. controlElements: [],
  1066. };
  1067.  
  1068. let images = $('.image-item');
  1069. DoLog(LogLevel.Info, 'Found images, length: ' + images.length);
  1070. DoLog(LogLevel.Elements, images);
  1071.  
  1072. images.each(function (i, e) {
  1073. let _this = $(e);
  1074.  
  1075. let work = _this.find('._work');
  1076. if (work.length === 0) {
  1077. DoLog(LogLevel.Warning, 'Can not found ._work, skip this.');
  1078. return;
  1079. }
  1080.  
  1081. let ctlAttrs = {
  1082. illustId: 0,
  1083. illustType: 0,
  1084. pageCount: 1,
  1085. };
  1086.  
  1087. let href = work.attr('href');
  1088. if (href == null || href === '') {
  1089. DoLog(LogLevel.Warning, 'Can not found illust id, skip this.');
  1090. return;
  1091. }
  1092.  
  1093. let matched = href.match(/artworks\/(\d+)/);
  1094. if (matched) {
  1095. ctlAttrs.illustId = matched[1];
  1096. } else {
  1097. DoLog(LogLevel.Warning, 'Can not found illust id, skip this.');
  1098. return;
  1099. }
  1100.  
  1101. if (work.hasClass('multiple')) {
  1102. ctlAttrs.pageCount = _this.find('.page-count').find('span').text();
  1103. }
  1104.  
  1105. if (work.hasClass('ugoku-illust')) {
  1106. ctlAttrs.illustType = 2;
  1107. }
  1108.  
  1109. // 添加 attr
  1110. let control = _this.children('a:first');
  1111. control.attr({
  1112. 'illustId': ctlAttrs.illustId,
  1113. 'illustType': ctlAttrs.illustType,
  1114. 'pageCount': ctlAttrs.pageCount
  1115. });
  1116.  
  1117. returnMap.controlElements.push(control.get(0));
  1118. });
  1119.  
  1120. returnMap.loadingComplete = true;
  1121.  
  1122. DoLog(LogLevel.Info, 'Process page elements complete.');
  1123. DoLog(LogLevel.Elements, returnMap);
  1124.  
  1125. this.private.returnMap = returnMap;
  1126. return returnMap;
  1127. },
  1128. GetProcessedPageElements: function () {
  1129. if (this.private.returnMap == null) {
  1130. return this.ProcessPageElements();
  1131. }
  1132. return this.private.returnMap;
  1133. },
  1134. GetToolBar: function () {
  1135. return findToolbarOld();
  1136. },
  1137. HasAutoLoad: false,
  1138. private: {
  1139. returnMap: null,
  1140. },
  1141. };
  1142. Pages[PageType.Stacc] = {
  1143. PageTypeString: 'StaccPage',
  1144. CheckUrl: function (url) {
  1145. return /^https:\/\/www.pixiv.net\/stacc.*/.test(url);
  1146. },
  1147. ProcessPageElements: function () {
  1148. let returnMap = {
  1149. loadingComplete: false,
  1150. controlElements: [],
  1151. };
  1152.  
  1153. let works = $('._work');
  1154.  
  1155. DoLog(LogLevel.Info, 'Found .work, length: ' + works.length);
  1156. DoLog(LogLevel.Elements, works);
  1157.  
  1158. works.each(function (i, e) {
  1159. let _this = $(e);
  1160.  
  1161. let ctlAttrs = {
  1162. illustId: 0,
  1163. illustType: 0,
  1164. pageCount: 1,
  1165. };
  1166.  
  1167. let href = _this.attr('href');
  1168.  
  1169. if (href == null || href === '') {
  1170. DoLog('Can not found illust id, skip this.');
  1171. return;
  1172. }
  1173.  
  1174. let matched = href.match(/illust_id=(\d+)/);
  1175. if (matched) {
  1176. ctlAttrs.illustId = matched[1];
  1177. } else {
  1178. DoLog('Can not found illust id, skip this.');
  1179. return;
  1180. }
  1181.  
  1182. if (_this.hasClass('multiple')) {
  1183. ctlAttrs.pageCount = _this.find('.page-count').find('span').text();
  1184. }
  1185.  
  1186. if (_this.hasClass('ugoku-illust')) {
  1187. ctlAttrs.illustType = 2;
  1188. }
  1189.  
  1190. // 添加 attr
  1191. _this.attr({
  1192. 'illustId': ctlAttrs.illustId,
  1193. 'illustType': ctlAttrs.illustType,
  1194. 'pageCount': ctlAttrs.pageCount
  1195. });
  1196.  
  1197. returnMap.controlElements.push(e);
  1198. });
  1199.  
  1200. returnMap.loadingComplete = true;
  1201.  
  1202. DoLog(LogLevel.Info, 'Process page elements complete.');
  1203. DoLog(LogLevel.Elements, returnMap);
  1204.  
  1205. this.private.returnMap = returnMap;
  1206. return returnMap;
  1207. },
  1208. GetProcessedPageElements: function () {
  1209. if (this.private.returnMap == null) {
  1210. return this.ProcessPageElements();
  1211. }
  1212. return this.private.returnMap;
  1213. },
  1214. GetToolBar: function () {
  1215. return findToolbarOld();
  1216. },
  1217. HasAutoLoad: true,
  1218. private: {
  1219. returnMap: null,
  1220. },
  1221. };
  1222. Pages[PageType.Artwork] = {
  1223. PageTypeString: 'ArtworkPage',
  1224. CheckUrl: function (url) {
  1225. return /^https:\/\/www.pixiv.net\/artworks\/.*/.test(url) ||
  1226. /^https:\/\/www.pixiv.net\/en\/artworks\/.*/.test(url);
  1227. },
  1228. ProcessPageElements: function () {
  1229. let returnMap = {
  1230. loadingComplete: false,
  1231. controlElements: [],
  1232. };
  1233.  
  1234. // 是动图
  1235. let canvas = $('main').find('figure').find('canvas');
  1236. if ($('main').find('figure').find('canvas').length > 0) {
  1237. this.private.needProcess = true;
  1238. canvas.addClass('pp-canvas');
  1239. }
  1240.  
  1241. if (location.href.indexOf('#preview') == -1) {
  1242. // 相关作品,container找不到说明还没加载
  1243. let containerDiv = $('.gtm-illust-recommend-zone');
  1244. if (containerDiv.length == 0) {
  1245. let asides = $('#root').find('aside');
  1246. $.each(asides, function(i, e) {
  1247. if ($(e).children('section').length == 1) {
  1248. containerDiv = $(e);
  1249. return false;
  1250. }
  1251. });
  1252. }
  1253. if (containerDiv.length > 0) {
  1254. DoLog(LogLevel.Info, 'Found container div.');
  1255. DoLog(LogLevel.Elements, containerDiv);
  1256.  
  1257. containerDiv.find('ul:first').children().each(function (i, e) {
  1258. let _this = $(e);
  1259.  
  1260. let img = _this.find('img');
  1261. if (img.length === 0) {
  1262. DoLog(LogLevel.Warning, 'Can not found <img>, skip this element.');
  1263. return;
  1264. }
  1265.  
  1266. let link = _this.find('a:first');
  1267. if (link.length === 0) {
  1268. DoLog(LogLevel.Warning, 'Can not found <a>, skip this element.');
  1269. return;
  1270. }
  1271.  
  1272. let ctlAttrs = {
  1273. illustId: 0,
  1274. illustType: 0,
  1275. pageCount: 1,
  1276. };
  1277.  
  1278. let href = link.attr('href');
  1279. if (href == null || href === '') {
  1280. DoLog(LogLevel.Warning, 'No href found, skip.');
  1281. return;
  1282. } else {
  1283. let matched = href.match(/artworks\/(\d+)/);
  1284. if (matched) {
  1285. ctlAttrs.illustId = matched[1];
  1286. } else {
  1287. DoLog(LogLevel.Warning, 'Can not found illust id, skip.');
  1288. return;
  1289. }
  1290. }
  1291.  
  1292. if (link.children().length > 1) {
  1293. if (link.children('div:first').find('span').length > 0) {
  1294. let span = link.children('div:first').find('span:first');
  1295. if (span.length === 0) {
  1296. DoLog(LogLevel.Warning, 'Can not found <span>, skip this element.');
  1297. return;
  1298. }
  1299. ctlAttrs.pageCount = span.next().text();
  1300. } else if (link.children('div:last').find('svg').length > 0) {
  1301. ctlAttrs.illustType = 2;
  1302. }
  1303. }
  1304.  
  1305. let control = link.parent();
  1306. control.attr({
  1307. 'illustId': ctlAttrs.illustId,
  1308. 'illustType': ctlAttrs.illustType,
  1309. 'pageCount': ctlAttrs.pageCount
  1310. });
  1311.  
  1312. returnMap.controlElements.push(control.get(0));
  1313. });
  1314. }
  1315.  
  1316. DoLog(LogLevel.Info, 'Process page elements complete.');
  1317. DoLog(LogLevel.Elements, returnMap);
  1318. }
  1319.  
  1320. returnMap.loadingComplete = true;
  1321. this.private.returnMap = returnMap;
  1322. return returnMap;
  1323. },
  1324. GetProcessedPageElements: function () {
  1325. if (this.private.returnMap == null) {
  1326. return this.ProcessPageElements();
  1327. }
  1328. return this.private.returnMap;
  1329. },
  1330. GetToolBar: function () {
  1331. return findToolbarCommon();
  1332. },
  1333. HasAutoLoad: true,
  1334. Work: function () {
  1335. function AddDownloadButton(button, offsetToOffsetTop) {
  1336. if (!g_settings.enableAnimeDownload) {
  1337. return;
  1338. }
  1339.  
  1340. let cloneButton = button.clone().css({ 'bottom': '50px', 'padding': 0, 'width': '48px', 'height': '48px', 'opacity': '0.4', 'cursor': 'pointer' });
  1341. cloneButton.get(0).innerHTML = '<svg viewBox="0 0 120 120" style="width: 40px; height: 40px; stroke-width: 10; stroke-linecap: round; stroke-linejoin: round; border-radius: 24px; background-color: black; stroke: limegreen; fill: none;" class="_3Fo0Hjg"><polyline points="60,30 60,90"></polyline><polyline points="30,60 60,90 90,60"></polyline></svg></button>';
  1342.  
  1343. function MoveButton() {
  1344. function getOffset(e) {
  1345. if (e.offsetParent) {
  1346. let offset = getOffset(e.offsetParent);
  1347. return {
  1348. offsetTop: e.offsetTop + offset.offsetTop,
  1349. offsetLeft: e.offsetLeft + offset.offsetLeft,
  1350. };
  1351. } else {
  1352. return {
  1353. offsetTop: e.offsetTop,
  1354. offsetLeft: e.offsetLeft,
  1355. };
  1356. }
  1357. }
  1358.  
  1359. /*let offset = getOffset(button.get(0));
  1360. DoLog(LogLevel.Info, 'offset of download button: ' + offset.offsetTop + ', ' + offset.offsetLeft);
  1361. DoLog(LogLevel.Elements, offset);
  1362.  
  1363. cloneButton.css({ 'position': 'absolute' }).show();*/
  1364. }
  1365.  
  1366. MoveButton();
  1367. $(window).on('resize', MoveButton);
  1368. button.after(cloneButton);
  1369.  
  1370. cloneButton.mouseover(function () {
  1371. $(this).css('opacity', '0.2');
  1372. }).mouseleave(function () {
  1373. $(this).css('opacity', '0.4');
  1374. }).click(function () {
  1375. let illustId = '';
  1376.  
  1377. let matched = location.href.match(/artworks\/(\d+)/);
  1378. if (matched) {
  1379. illustId = matched[1];
  1380. DoLog(LogLevel.Info, 'IllustId=' + illustId);
  1381. } else {
  1382. DoLog(LogLevel.Error, 'Can not found illust id!');
  1383. return;
  1384. }
  1385.  
  1386. $.ajax(g_getUgoiraUrl.replace('#id#', illustId), {
  1387. method: 'GET',
  1388. success: function (json) {
  1389. DoLog(LogLevel.Elements, json);
  1390.  
  1391. if (json.error == true) {
  1392. DoLog(LogLevel.Error, 'Server response an error: ' + json.message);
  1393. return;
  1394. }
  1395.  
  1396. // 因为浏览器会拦截不同域的 open 操作,绕一下
  1397. let newWindow = window.open('_blank');
  1398. newWindow.location = json.body.originalSrc;
  1399. },
  1400. error: function () {
  1401. DoLog(LogLevel.Error, 'Request zip file failed!');
  1402. }
  1403. });
  1404. });
  1405. }
  1406.  
  1407. if (this.private.needProcess) {
  1408. let canvas = $('.pp-canvas');
  1409.  
  1410. // 预览模式,需要调成全屏,并且添加下载按钮到全屏播放的 div 里
  1411. if (location.href.indexOf('#preview') != -1) {
  1412. canvas.click();
  1413.  
  1414. $('#root').remove();
  1415.  
  1416. let callbackInterval = setInterval(function () {
  1417. let div = $('div[role="presentation"]');
  1418. if (div.length < 1) {
  1419. return;
  1420. }
  1421.  
  1422. DoLog(LogLevel.Info, 'found <div>, continue to next step.');
  1423.  
  1424. clearInterval(callbackInterval);
  1425.  
  1426. let presentationCanvas = div.find('canvas');
  1427. if (presentationCanvas.length < 1) {
  1428. DoLog(LogLevel.Error, 'Can not found canvas in the presentation div.');
  1429. return;
  1430. }
  1431.  
  1432. let width = 0, height = 0;
  1433. let tWidth = presentationCanvas.attr('width');
  1434. let tHeight = presentationCanvas.attr('height');
  1435. if (tWidth && tHeight) {
  1436. width = parseInt(tWidth);
  1437. height = parseInt(tHeight);
  1438. } else {
  1439. tWidth = presentationCanvas.css('width');
  1440. tHeight = presentationCanvas.css('height');
  1441. width = parseInt(tWidth);
  1442. height = parseInt(this);
  1443. }
  1444.  
  1445. let parent = presentationCanvas.parent();
  1446. for (let i = 0; i < 3; i++) {
  1447. parent.get(0).className = '';
  1448. parent = parent.parent();
  1449. }
  1450. presentationCanvas.css({ 'width': width + 'px', 'height': height + 'px', 'cursor': 'default' }).addClass('pp-presentationCanvas');
  1451. let divForStopClick = $('<div class="pp-disableClick"></div>').css({
  1452. 'width': width + 'px', 'height': height + 'px',
  1453. 'opacity': 0,
  1454. 'position': 'absolute', 'top': '0px', 'left': '0px', 'z-index': 99999,
  1455. });
  1456. div.append(divForStopClick);
  1457. div.append(presentationCanvas.next().css('z-index', 99999));
  1458. presentationCanvas.next().remove();
  1459. // 防止预览图消失
  1460. $('html').addClass('pp-main');
  1461.  
  1462. // 调整 canvas 大小的函数
  1463. window.ResizeCanvas = function (newWidth, newHeight) {
  1464. DoLog(LogLevel.Info, 'Resize canvas: ' + newWidth + 'x' + newHeight);
  1465. $('.pp-disableClick').css({ 'width': newWidth, 'height': newHeight });
  1466. $('.pp-presentationCanvas').css({ 'width': newWidth, 'height': newHeight });
  1467. };
  1468. window.GetCanvasSize = function () {
  1469. return {
  1470. width: width,
  1471. height: height,
  1472. };
  1473. }
  1474.  
  1475. // 添加下载按钮
  1476. AddDownloadButton(divForStopClick.next(), 0);
  1477.  
  1478. window.parent.PreviewCallback(width, height);
  1479. }, 500);
  1480. }
  1481. // 普通模式,只需要添加下载按钮到内嵌模式的 div 里
  1482. else {
  1483. let div = $('div[role="presentation"]:last');
  1484. let button = div.find('button');
  1485.  
  1486. let headerRealHeight = parseInt($('header').css('height')) +
  1487. parseInt($('header').css('padding-top')) + parseInt($('header').css('padding-bottom')) +
  1488. parseInt($('header').css('margin-top')) + parseInt($('header').css('margin-bottom')) +
  1489. parseInt($('header').css('border-bottom-width')) + parseInt($('header').css('border-top-width'));
  1490.  
  1491. AddDownloadButton(button, headerRealHeight);
  1492. }
  1493. }
  1494. },
  1495. private: {
  1496. needProcess: false,
  1497. returnMap: null,
  1498. },
  1499. };
  1500. Pages[PageType.NovelSearch] = {
  1501. PageTypeString: 'NovelSearchPage',
  1502. CheckUrl: function (url) {
  1503. return /^https:\/\/www.pixiv.net\/tags\/.*\/novels/.test(url) ||
  1504. /^https:\/\/www.pixiv.net\/en\/tags\/.*\/novels/.test(url);
  1505. },
  1506. ProcessPageElements: function () {
  1507. let returnMap = {
  1508. loadingComplete: false,
  1509. controlElements: [],
  1510. };
  1511. let ul = $('section>div>ul');
  1512. if (ul.length > 0) {
  1513. returnMap.loadingComplete = true;
  1514. }
  1515. this.private.returnMap = returnMap;
  1516. return returnMap;
  1517. },
  1518. GetProcessedPageElements: function () {
  1519. if (this.private.returnMap == null) {
  1520. return this.ProcessPageElements();
  1521. }
  1522. return this.private.returnMap;
  1523. },
  1524. GetToolBar: function () {
  1525. return findToolbarCommon();
  1526. },
  1527. GetPageSelector: function() {
  1528. return $('section:first').find('nav:first');
  1529. },
  1530. HasAutoLoad: false,
  1531. private: {
  1532. returnMap: null,
  1533. },
  1534. }
  1535.  
  1536. function CheckUrlTest() {
  1537. let urls = [
  1538. 'http://www.pixiv.net',
  1539. 'http://www.pixiv.net',
  1540. 'https://www.pixiv.net',
  1541. 'https://www.pixiv.net/',
  1542. 'https://www.pixiv.net/?lang=en',
  1543. 'https://www.pixiv.net/search.php?s_mode=s_tag&word=miku',
  1544. 'https://www.pixiv.net/search.php?word=VOCALOID&s_mode=s_tag_full',
  1545. 'https://www.pixiv.net/discovery',
  1546. 'https://www.pixiv.net/discovery?x=1',
  1547. 'https://www.pixiv.net/member.php?id=3207350',
  1548. 'https://www.pixiv.net/member_illust.php?id=3207350&type=illust',
  1549. 'https://www.pixiv.net/bookmark.php?id=3207350&rest=show',
  1550. 'https://www.pixiv.net/ranking.php?mode=daily&content=ugoira',
  1551. 'https://www.pixiv.net/ranking.php?mode=daily',
  1552. 'https://www.pixiv.net/new_illust.php',
  1553. 'https://www.pixiv.net/new_illust.php?x=1',
  1554. 'https://www.pixiv.net/cate_r18.php',
  1555. 'https://www.pixiv.net/cate_r18.php?x=1',
  1556. 'https://www.pixiv.net/bookmark.php',
  1557. 'https://www.pixiv.net/bookmark.php?x=1',
  1558. 'https://www.pixiv.net/stacc?mode=unify',
  1559. 'https://www.pixiv.net/artworks/77996773',
  1560. 'https://www.pixiv.net/artworks/77996773#preview',
  1561. 'https://www.pixiv.net/tags/miku/novels',
  1562. ];
  1563.  
  1564. for (let j = 0; j < urls.length; j++) {
  1565. for (let i = 0; i < PageType.PageTypeCount; i++) {
  1566. if (Pages[i].CheckUrl(urls[j])) {
  1567. console.log(urls[j]);
  1568. console.log('[' + j + '] is ' + Pages[i].PageTypeString);
  1569. }
  1570. }
  1571. }
  1572. }
  1573. /* ---------------------------------------- scroll_lock ---------------------------------------- */
  1574. function preventDefault(e) {
  1575. e.preventDefault();
  1576. }
  1577. // modern Chrome requires { passive: false } when adding event
  1578. var supportsPassive = false;
  1579. try {
  1580. window.addEventListener("test", null, Object.defineProperty({}, 'passive', {
  1581. get: function () { supportsPassive = true; }
  1582. }));
  1583. } catch (e) { }
  1584.  
  1585. var wheelOpt = supportsPassive ? { passive: false } : false;
  1586. var wheelEvent = 'onwheel' in document.createElement('div') ? 'wheel' : 'mousewheel';
  1587.  
  1588. function disableScroll() {
  1589. window.addEventListener(wheelEvent, preventDefault, wheelOpt);
  1590. }
  1591. function enableScroll() {
  1592. window.removeEventListener(wheelEvent, preventDefault, wheelOpt);
  1593. }
  1594. /* ---------------------------------------- 预览 ---------------------------------------- */
  1595. let autoLoadInterval = null;
  1596. function PixivPreview() {
  1597. // 最终需要显示的预览图ID,用于避免鼠标滑过多张图片时,最终显示的图片错误
  1598. let previewTargetIllustId = '';
  1599.  
  1600. // 开启预览功能
  1601. function ActivePreview() {
  1602. let returnMap = Pages[g_pageType].GetProcessedPageElements();
  1603. if (!returnMap.loadingComplete) {
  1604. DoLog(LogLevel.Error, 'Page not load, should not call Preview!');
  1605. return;
  1606. }
  1607.  
  1608. // 鼠标进入
  1609. $(returnMap.controlElements).mouseenter(function (e) {
  1610. // 按住 Ctrl键 不显示预览图
  1611. if (e.ctrlKey) {
  1612. return;
  1613. }
  1614.  
  1615. let startTime = new Date().getTime();
  1616. let delay = parseInt(g_settings.previewDelay == null ? g_defaultSettings.previewDelay : g_settings.previewDelay);
  1617.  
  1618. let _this = $(this);
  1619. let illustId = _this.attr('illustId');
  1620. let illustType = _this.attr('illustType');
  1621. let pageCount = _this.attr('pageCount');
  1622.  
  1623. if (illustId == null) {
  1624. DoLog(LogLevel.Error, 'Can not found illustId in this element\'s attrbutes.');
  1625. return;
  1626. }
  1627. if (illustType == null) {
  1628. DoLog(LogLevel.Error, 'Can not found illustType in this element\'s attrbutes.');
  1629. return;
  1630. }
  1631. if (pageCount == null) {
  1632. DoLog(LogLevel.Error, 'Can not found pageCount in this element\'s attrbutes.');
  1633. return;
  1634. }
  1635. previewTargetIllustId = illustId;
  1636.  
  1637. // 鼠标位置
  1638. g_mousePos = { x: e.pageX, y: e.pageY };
  1639. // 预览 Div
  1640. let previewDiv = $(document.createElement('div')).addClass('pp-main').attr('illustId', illustId)
  1641. .css({
  1642. 'position': 'absolute', 'z-index': '999999', 'left': g_mousePos.x + 'px', 'top': g_mousePos.y + 'px',
  1643. 'border-style': 'solid', 'border-color': '#6495ed', 'border-width': '2px', 'border-radius': '20px',
  1644. 'width': '48px', 'height': '48px',
  1645. 'background-image': 'url(https://pp-1252089172.cos.ap-chengdu.myqcloud.com/transparent.png)',
  1646. 'display': 'none'
  1647. });
  1648. // 添加到 body
  1649. $('.pp-main').remove();
  1650. $('body').append(previewDiv);
  1651.  
  1652. let waitTime = delay - (new Date().getTime() - startTime);
  1653. if (waitTime > 0) {
  1654. setTimeout(function () {
  1655. previewDiv.show();
  1656. }, waitTime);
  1657. } else {
  1658. previewDiv.show();
  1659. }
  1660.  
  1661. // 加载中图片
  1662. let loadingImg = $(new Image()).addClass('pp-loading').attr('src', g_loadingImage).css({
  1663. 'position': 'absolute', 'border-radius': '20px',
  1664. });
  1665. previewDiv.append(loadingImg);
  1666.  
  1667. // 要显示的预览图节点
  1668. let loadImg = $(new Image()).addClass('pp-image').css({ 'height': '0px', 'width': '0px', 'display': 'none', 'border-radius': '20px' });
  1669. previewDiv.append(loadImg);
  1670.  
  1671. // 原图(笑脸)图标
  1672. let originIcon = $(new Image()).addClass('pp-original').attr('src', 'https://source.pixiv.net/www/images/pixivcomic-favorite.png')
  1673. .css({ 'position': 'absolute', 'bottom': '5px', 'right': '5px', 'display': 'none' });
  1674. previewDiv.append(originIcon);
  1675.  
  1676. // 点击图标新网页打开原图
  1677. originIcon.click(function () {
  1678. window.open($(previewDiv).children('img')[1].src);
  1679. });
  1680.  
  1681. // 右上角张数标记
  1682. let pageCountHTML = '<div class="pp-pageCount" style="display: flex;-webkit-box-align: center;align-items: center;box-sizing: border-box;margin-left: auto;height: 20px;color: rgb(255, 255, 255);font-size: 10px;line-height: 12px;font-weight: bold;flex: 0 0 auto;padding: 4px 6px;background: rgba(0, 0, 0, 0.32);border-radius: 10px;margin-top:5px;margin-right:5px;">\<svg viewBox="0 0 9 10" width="9" height="10" style="stroke: none;line-height: 0;font-size: 0px;fill: currentcolor;"><path d="M8,3 C8.55228475,3 9,3.44771525 9,4 L9,9 C9,9.55228475 8.55228475,10 8,10 L3,10 C2.44771525,10 2,9.55228475 2,9 L6,9 C7.1045695,9 8,8.1045695 8,7 L8,3 Z M1,1 L6,1 C6.55228475,1 7,1.44771525 7,2 L7,7 C7,7.55228475 6.55228475,8 6,8 L1,8 C0.44771525,8 0,7.55228475 0,7 L0,2 C0,1.44771525 0.44771525,1 1,1 Z"></path></svg><span style="margin-left:2px;" class="pp-page">0/0</span></div>';
  1683. let pageCountDiv = $(pageCountHTML)
  1684. .css({ 'position': 'absolute', 'top': '0px', 'display': 'none', 'right': '0px', 'display': 'none' });
  1685. previewDiv.append(pageCountDiv);
  1686.  
  1687. $('.pp-main').mouseleave(function (e) {
  1688. $(this).remove();
  1689. });
  1690.  
  1691. let url = '';
  1692. if (illustType == 2) {
  1693. // 动图
  1694. let screenWidth = document.documentElement.clientWidth;
  1695. let screenHeight = document.documentElement.clientHeight;
  1696. previewDiv.get(0).innerHTML = '<iframe class="pp-iframe" style="width: 48px; height: 48px; display: none; border-radius: 20px;" src="https://www.pixiv.net/artworks/' + illustId + '#preview" />';
  1697. previewDiv.append(loadingImg);
  1698. } else {
  1699. url = g_getArtworkUrl.replace('#id#', illustId);
  1700.  
  1701. // 获取图片链接
  1702. $.ajax(url, {
  1703. method: 'GET',
  1704. success: function (json) {
  1705. DoLog(LogLevel.Info, 'Got artwork urls:');
  1706. DoLog(LogLevel.Elements, json);
  1707.  
  1708. if (json.error === true) {
  1709. DoLog(LogLevel.Error, 'Server responsed an error: ' + json.message);
  1710. return;
  1711. }
  1712.  
  1713. // 已经不需要显示这个预览图了,直接丢弃
  1714. if (illustId != previewTargetIllustId) {
  1715. DoLog(LogLevel.Info, 'Drop this preview request.');
  1716. return;
  1717. }
  1718.  
  1719. let regular = [];
  1720. let original = [];
  1721. for (let i = 0; i < json.body.length; i++) {
  1722. regular.push(json.body[i].urls.regular);
  1723. original.push(json.body[i].urls.original);
  1724. }
  1725.  
  1726. DoLog(LogLevel.Info, 'Process urls complete.');
  1727. DoLog(LogLevel.Elements, regular);
  1728. DoLog(LogLevel.Elements, original);
  1729.  
  1730. ViewImages(regular, 0, original, g_settings.original, illustId);
  1731. },
  1732. error: function (data) {
  1733. DoLog(LogLevel.Error, 'Request image urls failed!');
  1734. if (data) {
  1735. DoLog(LogLevel.Elements, data);
  1736. }
  1737. }
  1738. });
  1739. }
  1740. });
  1741.  
  1742. // 鼠标移出图片
  1743. $(returnMap.controlElements).mouseleave(function (e) {
  1744. let _this = $(this);
  1745. let illustId = _this.attr('illustId');
  1746. let illustType = _this.attr('illustType');
  1747. let pageCount = _this.attr('pageCount');
  1748.  
  1749. let moveToElement = $(e.relatedTarget);
  1750. let isMoveToPreviewElement = false;
  1751. // 鼠标移动到预览图上
  1752. while (true) {
  1753. if (moveToElement.hasClass('pp-main') && moveToElement.attr('illustId') == illustId) {
  1754. isMoveToPreviewElement = true;
  1755. }
  1756.  
  1757. if (moveToElement.parent().length < 1) {
  1758. break;
  1759. }
  1760.  
  1761. moveToElement = moveToElement.parent();
  1762. }
  1763. if (!isMoveToPreviewElement) {
  1764. // 非预览图上
  1765. $('.pp-main').remove();
  1766. }
  1767. });
  1768.  
  1769. // 鼠标移动,调整位置
  1770. $(returnMap.controlElements).mousemove(function (e) {
  1771. // Ctrl 和 中键 都可以禁止预览图移动,这样就可以单手操作了
  1772. if (e.ctrlKey || e.buttons & 4) {
  1773. return;
  1774. }
  1775. let screenWidth = document.documentElement.clientWidth;
  1776. let screenHeight = document.documentElement.clientHeight;
  1777. g_mousePos.x = e.pageX; g_mousePos.y = e.pageY;
  1778.  
  1779. AdjustDivPosition();
  1780. });
  1781.  
  1782. // 这个页面有自动加载
  1783. if (Pages[g_pageType].HasAutoLoad && autoLoadInterval == null) {
  1784. autoLoadInterval = setInterval(ProcessAutoLoad, 1000);
  1785. DoLog(LogLevel.Info, 'Auto load interval set.');
  1786. }
  1787.  
  1788. // 插一段回调函数
  1789. window.PreviewCallback = PreviewCallback;
  1790. DoLog(LogLevel.Info, 'Callback function was inserted.');
  1791. DoLog(LogLevel.Elements, window.PreviewCallback);
  1792.  
  1793. DoLog(LogLevel.Info, 'Preview enable succeed!');
  1794. }
  1795.  
  1796. // 关闭预览功能,不是给外部用的
  1797. function DeactivePreview() {
  1798. let returnMap = Pages[g_pageType].GetProcessedPageElements();
  1799. if (!returnMap.loadingComplete) {
  1800. DoLog(LogLevel.Error, 'Page not load, should not call Preview!');
  1801. return;
  1802. }
  1803.  
  1804. // 只需要取消绑定事件, attrs 以及回调都不需要删除
  1805. $(returnMap.controlElements).unbind('mouseenter').unbind('mouseleave').unbind('mousemove');
  1806.  
  1807. if (autoLoadInterval) {
  1808. clearInterval(autoLoadInterval);
  1809. autoLoadInterval = null;
  1810. }
  1811.  
  1812. DoLog(LogLevel.Info, 'Preview disable succeed!');
  1813. }
  1814.  
  1815. // iframe 的回调函数
  1816. function PreviewCallback(canvasWidth, canvasHeight) {
  1817. DoLog(LogLevel.Info, 'iframe callback, width: ' + canvasWidth + ', height: ' + canvasHeight);
  1818.  
  1819. let size = AdjustDivPosition();
  1820.  
  1821. $('.pp-loading').hide();
  1822. $('.pp-iframe').css({ 'width': size.width, 'height': size.height }).show();
  1823. }
  1824.  
  1825. // 调整预览 Div 的位置
  1826. function AdjustDivPosition() {
  1827. // 鼠标到预览图的距离
  1828. let fromMouseToDiv = 30;
  1829.  
  1830. let screenWidth = document.documentElement.clientWidth;
  1831. let screenHeight = document.documentElement.clientHeight;
  1832. let left = 0;
  1833. let top = document.body.scrollTop + document.documentElement.scrollTop;
  1834.  
  1835. let width = 0, height = 0;
  1836. if ($('.pp-main').find('iframe').length > 0) {
  1837. let iframe = $('.pp-main').find('iframe').get(0);
  1838. if (iframe.contentWindow.GetCanvasSize) {
  1839. let canvasSize = iframe.contentWindow.GetCanvasSize();
  1840. width = canvasSize.width;
  1841. height = canvasSize.height;
  1842. } else {
  1843. width = 0;
  1844. height = 0;
  1845. }
  1846. } else {
  1847. $('.pp-image').css({ 'width': '', 'height': '' });
  1848. width = $('.pp-image').get(0) == null ? 0 : $('.pp-image').get(0).width;
  1849. height = $('.pp-image').get(0) == null ? 0 : $('.pp-image').get(0).height;
  1850. }
  1851.  
  1852. let isShowOnLeft = g_mousePos.x > screenWidth / 2;
  1853.  
  1854. let newWidth = 48, newHeight = 48;
  1855. if (width > 0 && height > 0) {
  1856. newWidth = isShowOnLeft ? g_mousePos.x - fromMouseToDiv : screenWidth - g_mousePos.x - fromMouseToDiv;
  1857. newHeight = height / width * newWidth;
  1858. // 高度不足以完整显示,只能让两侧留空了
  1859. if (newHeight > screenHeight) {
  1860. newHeight = screenHeight;
  1861. newWidth = newHeight / height * width;
  1862. }
  1863. newWidth -= 5;
  1864. newHeight -= 5;
  1865.  
  1866. // 设置新的宽高
  1867. if ($('.pp-main').find('iframe').length > 0) {
  1868. let iframe = $('.pp-main').find('iframe');
  1869. iframe.get(0).contentWindow.ResizeCanvas(newWidth, newHeight);
  1870. iframe.css({ 'width': newWidth, 'height': newHeight });
  1871. }
  1872. else {
  1873. $('.pp-image').css({ 'height': newHeight + 'px', 'width': newWidth + 'px' });
  1874. }
  1875.  
  1876. // 调整下一次 loading 出现的位置
  1877. $('.pp-loading').css({ 'left': newWidth / 2 - 24 + 'px', 'top': newHeight / 2 - 24 + 'px' });
  1878. }
  1879.  
  1880. // 图片宽度大于高度很多时,会显示在页面顶部,鼠标碰不到,把它移动到下面
  1881. if (top + newHeight <= g_mousePos.y) {
  1882. top = (g_mousePos.y - newHeight - fromMouseToDiv);
  1883. }
  1884. // 调整DIV的位置
  1885. left = isShowOnLeft ? g_mousePos.x - newWidth - fromMouseToDiv : g_mousePos.x + fromMouseToDiv;
  1886.  
  1887. $('.pp-main').css({ 'left': left + 'px', 'top': top + 'px', 'width': newWidth, 'height': newHeight });
  1888.  
  1889. // 返回新的宽高
  1890. return {
  1891. width: newWidth,
  1892. height: newHeight,
  1893. };
  1894. }
  1895.  
  1896. // 请求显示的预览图ID
  1897. let displayTargetIllustId = '';
  1898. // 显示预览图
  1899. function ViewImages(regular, index, original, isShowOriginal, illustId) {
  1900. displayTargetIllustId = illustId;
  1901. if (!regular || regular.length === 0) {
  1902. DoLog(LogLevel.Error, 'Regular url array is null, can not view images!');
  1903. return;
  1904. }
  1905. if (index == null || index < 0 || index >= regular.length) {
  1906. DoLog(LogLevel.Error, 'Index(' + index + ') out of range, can not view images!');
  1907. return;
  1908. }
  1909. if (original == null || original.length === 0) {
  1910. DoLog(LogLevel.Warning, 'Original array is null, replace it with regular array.');
  1911. original = regular;
  1912. }
  1913. if (original.length < regular) {
  1914. DoLog(LogLevel.Warning, 'Original array\'s length is less than regular array, replace it with regular array.');
  1915. original = regular;
  1916. }
  1917. if (isShowOriginal == null) {
  1918. isShowOriginal = false;
  1919. }
  1920.  
  1921. if (original.length > 1) {
  1922. $('.pp-page').text((index + 1) + '/' + regular.length);
  1923. $('.pp-pageCount').show();
  1924. }
  1925. if (isShowOriginal) {
  1926. $('.pp-image').addClass('original');
  1927. } else {
  1928. $('.pp-image').removeClass('original');
  1929. }
  1930. g_settings.original = isShowOriginal ? 1 : 0;
  1931.  
  1932. // 隐藏页数和原图标签
  1933. $('.pp-original, .pp-pageCount').hide();
  1934.  
  1935. // 第一次需要绑定事件
  1936. if ($('.pp-image').attr('index') == null || $('.pp-image').attr('pageCount') != regular.length) {
  1937. $('.pp-image').attr('pageCount', regular.length);
  1938.  
  1939. // 绑定点击事件,Ctrl+左键 单击切换原图
  1940. $('.pp-image').on('click', function (ev) {
  1941. let _this = $(this);
  1942. let isOriginal = _this.hasClass('original');
  1943. let index = _this.attr('index');
  1944. if (index == null) {
  1945. index = 0;
  1946. } else {
  1947. index = parseInt(index);
  1948. }
  1949.  
  1950. if (ev.ctrlKey) {
  1951. // 按住 Ctrl 来回切换原图
  1952. isOriginal = !isOriginal;
  1953. ViewImages(regular, index, original, isOriginal, illustId);
  1954. }
  1955. else if (ev.shiftKey) {
  1956. // 按住 Shift 点击图片新标签页打开原图
  1957. window.open(original[index]);
  1958. } else {
  1959. if (regular.length == 1) {
  1960. return;
  1961. }
  1962. // 如果是多图,点击切换下一张
  1963. if (++index >= regular.length) {
  1964. index = 0;
  1965. }
  1966. ViewImages(regular, index, original, isOriginal, illustId);
  1967. // 预加载
  1968. for (let i = index + 1; i < regular.length && i <= index + 3; i++) {
  1969. let image = new Image();
  1970. image.src = isOriginal ? original[i] : regular[i];;
  1971. }
  1972. }
  1973. });
  1974.  
  1975. // mousewheel event,和上面一樣
  1976. $('.pp-image').bind('mousewheel', function (ev) {
  1977. let _this = $(this);
  1978. let isOriginal = _this.hasClass('original');
  1979. let index = _this.attr('index');
  1980. if (index == null) {
  1981. index = 0;
  1982. } else {
  1983. index = parseInt(index);
  1984. }
  1985.  
  1986. if (regular.length == 1) {
  1987. return;
  1988. }
  1989. // 如果是多图,点击切换下一张
  1990. if (ev.originalEvent.wheelDelta < 0) {
  1991. if (++index >= regular.length) {
  1992. index = 0;
  1993. }
  1994. } else {
  1995. if (--index < 0) {
  1996. index = regular.length - 1;
  1997. }
  1998. }
  1999. ViewImages(regular, index, original, isOriginal, illustId);
  2000. // 预加载
  2001. for (let i = index + 1; i < regular.length && i <= index + 3; i++) {
  2002. let image = new Image();
  2003. image.src = isOriginal ? original[i] : regular[i];;
  2004. }
  2005. });
  2006.  
  2007. // scrollLock
  2008. $(".pp-image").mouseenter(function () {
  2009. disableScroll()
  2010. });
  2011. $(".pp-image").mouseleave(function () {
  2012. enableScroll()
  2013. });
  2014.  
  2015. // 图片预加载完成
  2016. $('.pp-image').on('load', function () {
  2017. // 显示图片前也判断一下是不是目标图片
  2018. if (displayTargetIllustId != previewTargetIllustId) {
  2019. DoLog(LogLevel.Info, '(2)Drop this preview request.');
  2020. return;
  2021. }
  2022.  
  2023. // 调整图片位置和大小
  2024. let _this = $(this);
  2025. let size = AdjustDivPosition();
  2026. let isShowOriginal = _this.hasClass('original');
  2027.  
  2028. $('.pp-loading').css('display', 'none');
  2029. // 显示图像、页数、原图标签
  2030. $('.pp-image').css('display', '');
  2031. if (regular.length > 1) {
  2032. $('.pp-pageCount').show();
  2033. }
  2034. if (isShowOriginal) {
  2035. $('.pp-original').show();
  2036. }
  2037.  
  2038. // 预加载
  2039. for (let i = index + 1; i < regular.length && i <= index + 3; i++) {
  2040. let image = new Image();
  2041. image.src = isShowOriginal ? original[i] : regular[i];;
  2042. }
  2043. }).on('error', function () {
  2044. DoLog(LogLevel.Error, 'Load image failed!');
  2045. });
  2046. }
  2047.  
  2048. $('.pp-image').attr('src', isShowOriginal ? original[index] : regular[index]).attr('index', index);
  2049. }
  2050.  
  2051. // 处理自动加载
  2052. function ProcessAutoLoad() {
  2053. if (Pages[g_pageType].GetProcessedPageElements() == null) {
  2054. DoLog(LogLevel.Error, 'Call ProcessPageElements first!');
  2055. return;
  2056. }
  2057.  
  2058. let oldReturnMap = Pages[g_pageType].GetProcessedPageElements();
  2059. let newReturnMap = Pages[g_pageType].ProcessPageElements();
  2060.  
  2061. if (newReturnMap.loadingComplete) {
  2062. if (oldReturnMap.controlElements.length < newReturnMap.controlElements.length || newReturnMap.forceUpdate) {
  2063. DoLog(LogLevel.Info, 'Page loaded ' + (newReturnMap.controlElements.length - oldReturnMap.controlElements.length) + ' new work(s).');
  2064.  
  2065. if (g_settings.linkBlank) {
  2066. $(newReturnMap.controlElements).find('a').attr('target', '_blank');
  2067. }
  2068.  
  2069. SetTargetBlank(newReturnMap);
  2070. DeactivePreview();
  2071. ActivePreview();
  2072.  
  2073. return;
  2074. } else if (oldReturnMap.controlElements.length > newReturnMap.controlElements.length) {
  2075. DoLog(LogLevel.Warning, 'works become less?');
  2076.  
  2077. Pages[g_pageType].private.returnMap = oldReturnMap;
  2078.  
  2079. return;
  2080. }
  2081. }
  2082.  
  2083. DoLog(LogLevel.Info, 'Page not change.');
  2084. }
  2085.  
  2086. // 开启预览
  2087. ActivePreview();
  2088. }
  2089. /* ---------------------------------------- 排序 ---------------------------------------- */
  2090. let imageElementTemplate = null;
  2091. function PixivSK(callback) {
  2092. // 不合理的设定
  2093. if (g_settings.pageCount < 1 || g_settings.favFilter < 0) {
  2094. g_settings.pageCount = 1;
  2095. g_settings.favFilter = 0;
  2096. }
  2097. // 当前已经取得的页面数量
  2098. let currentGettingPageCount = 0;
  2099. // 当前加载的页面 URL
  2100. let currentUrl = 'https://www.pixiv.net/ajax/search/';
  2101. // 当前加载的是第几张页面
  2102. let currentPage = 0;
  2103. // 获取到的作品
  2104. let works = [];
  2105.  
  2106. // 仅搜索页启用
  2107. if (g_pageType != PageType.Search) {
  2108. return;
  2109. }
  2110.  
  2111. // 获取第 currentPage 页的作品
  2112. let getWorks = function (onloadCallback) {
  2113. $('#progress').text(Texts[g_language].sort_getWorks.replace('%1', currentGettingPageCount + 1).replace('%2', g_settings.pageCount));
  2114.  
  2115. let url = currentUrl.replace(/p=\d+/, 'p=' + currentPage);
  2116.  
  2117. if (location.href.indexOf('?') != -1) {
  2118. let param = location.href.split('?')[1];
  2119. param = param.replace(/^p=\d+/, '');
  2120. param = param.replace(/&p=\d+/, '');
  2121. url += '&' + param;
  2122. }
  2123.  
  2124. if (url.indexOf('order=') == -1) {
  2125. url += '&order=date_d';
  2126. }
  2127. if (url.indexOf('mode=') == -1) {
  2128. url += '&mode=all';
  2129. }
  2130. if (url.indexOf('s_mode=') == -1) {
  2131. url += '&s_mode=s_tag_full';
  2132. }
  2133.  
  2134. DoLog(LogLevel.Info, 'getWorks url: ' + url);
  2135.  
  2136. let req = new XMLHttpRequest();
  2137. req.open('GET', url, true);
  2138. req.onload = function (event) {
  2139. onloadCallback(req);
  2140. };
  2141. req.onerror = function (event) {
  2142. DoLog(LogLevel.Error, 'Request search page error!');
  2143. };
  2144.  
  2145. req.send(null);
  2146. };
  2147.  
  2148. function getFollowingOfType(user_id, type, offset) {
  2149. return new Promise(function (resolve, reject) {
  2150. if (offset == null) {
  2151. offset = 0;
  2152. }
  2153. let limit = 100;
  2154. let following_show = [];
  2155. $.ajax('https://www.pixiv.net/ajax/user/' + user_id + '/following?offset=' + offset + '&limit=' + limit + '&rest=' + type, {
  2156. async: true,
  2157. success: function (data) {
  2158. if (data == null || data.error) {
  2159. DoLog(LogLevel.Error, 'Following response contains an error.');
  2160. resolve([]);
  2161. return;
  2162. }
  2163. if (data.body.users.length == 0) {
  2164. resolve([]);
  2165. return;
  2166. }
  2167. $.each(data.body.users, function (i, user) {
  2168. following_show.push(user.userId);
  2169. });
  2170. getFollowingOfType(user_id, type, offset + limit).then(function (members) {
  2171. resolve(following_show.concat(members));
  2172. return;
  2173. });
  2174. },
  2175. error: function () {
  2176. DoLog(LogLevel.Error, 'Request following failed.');
  2177. resolve([]);
  2178. }
  2179. });
  2180. });
  2181. }
  2182.  
  2183. function getFollowingOfCurrentUser() {
  2184. return new Promise(function (resolve, reject) {
  2185. let user_id = '';
  2186.  
  2187. try {
  2188. user_id = dataLayer[0].user_id;
  2189. } catch (ex) {
  2190. DoLog(LogLevel.Error, 'Get user id failed.');
  2191. resolve([]);
  2192. return;
  2193. }
  2194.  
  2195. // show/hide
  2196. $('#progress').text(Texts[g_language].sort_getPublicFollowing);
  2197.  
  2198. // 首先从Cookie读取
  2199. let following = GetCookie('followingOfUid-' + user_id);
  2200. if (following != null) {
  2201. resolve(following);
  2202. return;
  2203. }
  2204.  
  2205. getFollowingOfType(user_id, 'show').then(function (members) {
  2206. $('#progress').text(Texts[g_language].sort_getPrivateFollowing);
  2207. getFollowingOfType(user_id, 'hide').then(function (members2) {
  2208. let following = members.concat(members2);
  2209. SetCookie('followingOfUid-' + user_id, following, 1);
  2210. resolve(following);
  2211. });
  2212. });
  2213. });
  2214. }
  2215.  
  2216. // 筛选已关注画师作品
  2217. let filterByUser = function () {
  2218. return new Promise(function (resolve, reject) {
  2219. if (!g_settings.hideFollowed) {
  2220. resolve();
  2221. }
  2222.  
  2223. getFollowingOfCurrentUser().then(function (members) {
  2224. let tempWorks = [];
  2225. let hideWorkCount = 0;
  2226. $(works).each(function (i, work) {
  2227. let found = false;
  2228. for (let i = 0; i < members.length; i++) {
  2229. if (members[i] == work.userId) {
  2230. found = true;
  2231. break;
  2232. }
  2233. }
  2234. if (!found) {
  2235. tempWorks.push(work);
  2236. } else {
  2237. hideWorkCount++;
  2238. }
  2239. });
  2240. works = tempWorks;
  2241.  
  2242. DoLog(LogLevel.Info, hideWorkCount + ' works were hide.');
  2243. DoLog(LogLevel.Elements, works);
  2244. resolve();
  2245. });
  2246. });
  2247. };
  2248.  
  2249. // 排序和筛选
  2250. let filterAndSort = function () {
  2251. return new Promise(function (resolve, reject) {
  2252. DoLog(LogLevel.Info, 'Start sort.');
  2253. DoLog(LogLevel.Elements, works);
  2254.  
  2255. // 收藏量低于 FAV_FILTER 的作品不显示
  2256. let text = Texts[g_language].sort_filtering.replace('%2', g_settings.favFilter);
  2257. text = text.replace('%1', (g_settings.hideFavorite ? Texts[g_language].sort_filteringHideFavorite : ''));
  2258. $('#progress').text(text); // 实际上这个太快完全看不到
  2259. let tmp = [];
  2260. $(works).each(function (i, work) {
  2261. let bookmarkCount = work.bookmarkCount ? work.bookmarkCount : 0;
  2262. if (bookmarkCount >= g_settings.favFilter && !(g_settings.hideFavorite && work.bookmarkData)) {
  2263. tmp.push(work);
  2264. }
  2265. });
  2266. works = tmp;
  2267.  
  2268. filterByUser().then(function () {
  2269. // 排序
  2270. works.sort(function (a, b) {
  2271. let favA = a.bookmarkCount;
  2272. let favB = b.bookmarkCount;
  2273. if (!favA) {
  2274. favA = 0;
  2275. }
  2276. if (!favB) {
  2277. favB = 0;
  2278. }
  2279. if (favA > favB) {
  2280. return -1;
  2281. }
  2282. if (favA < favB) {
  2283. return 1;
  2284. }
  2285. return 0;
  2286. });
  2287. DoLog(LogLevel.Info, 'Sort complete.');
  2288. DoLog(LogLevel.Elements, works);
  2289. resolve();
  2290. });
  2291. });
  2292. };
  2293.  
  2294. if (currentPage === 0) {
  2295. let url = location.href;
  2296.  
  2297. if (url.indexOf('&p=') == -1 && url.indexOf('?p=') == -1) {
  2298. DoLog(LogLevel.Warning, 'Can not found page in url.');
  2299. if (url.indexOf('?') == -1) {
  2300. url += '?p=1';
  2301. DoLog(LogLevel.Info, 'Add "?p=1": ' + url);
  2302. } else {
  2303. url += '&p=1';
  2304. DoLog(LogLevel.Info, 'Add "&p=1": ' + url);
  2305. }
  2306. }
  2307. let wordMatch = url.match(/\/tags\/([^/]*)\//);
  2308. let searchWord = '';
  2309. if (wordMatch) {
  2310. DoLog(LogLevel.Info, 'Search key word: ' + searchWord);
  2311. searchWord = wordMatch[1];
  2312. } else {
  2313. DoLog(LogLevel.Error, 'Can not found search key word!');
  2314. return;
  2315. }
  2316.  
  2317. // page
  2318. let page = url.match(/p=(\d*)/)[1];
  2319. currentPage = parseInt(page);
  2320. DoLog(LogLevel.Info, 'Current page: ' + currentPage);
  2321.  
  2322. let type = url.match(/tags\/.*\/(.*)[?$]/)[1];
  2323. currentUrl += type + '/';
  2324.  
  2325. currentUrl += searchWord + '?word=' + searchWord + '&p=' + currentPage;
  2326. DoLog(LogLevel.Info, 'Current url: ' + currentUrl);
  2327. } else {
  2328. DoLog(LogLevel.Error, '???');
  2329. }
  2330.  
  2331. let imageContainer = Pages[PageType.Search].GetImageListContainer();
  2332. // loading
  2333. $(imageContainer).hide().before('<div id="loading" style="width:100%;text-align:center;"><img src="' + g_loadingImage + '" /><p id="progress" style="text-align: center;font-size: large;font-weight: bold;padding-top: 10px;">0%</p></div>');
  2334.  
  2335. // page
  2336. if (true) {
  2337. let pageSelectorDiv = Pages[PageType.Search].GetPageSelector();
  2338. if (pageSelectorDiv == null) {
  2339. DoLog(LogLevel.Error, 'Can not found page selector!');
  2340. return;
  2341. }
  2342.  
  2343. if ($(pageSelectorDiv).find('a').length > 2) {
  2344. let pageButton = $(pageSelectorDiv).find('a').get(1);
  2345. let newPageButtons = [];
  2346. let pageButtonString = 'Previewer';
  2347. for (let i = 0; i < 9; i++) {
  2348. let newPageButton = pageButton.cloneNode(true);
  2349. $(newPageButton).find('span').text(pageButtonString[i]);
  2350. newPageButtons.push(newPageButton);
  2351. }
  2352.  
  2353. $(pageSelectorDiv).find('button').remove();
  2354. while ($(pageSelectorDiv).find('a').length > 2) {
  2355. $(pageSelectorDiv).find('a:first').next().remove();
  2356. }
  2357.  
  2358. for (let i = 0; i < 9; i++) {
  2359. $(pageSelectorDiv).find('a:last').before(newPageButtons[i]);
  2360. }
  2361.  
  2362. $(pageSelectorDiv).find('a').attr('href', 'javascript:;');
  2363.  
  2364. let pageUrl = location.href;
  2365. if (pageUrl.indexOf('&p=') == -1 && pageUrl.indexOf('?p=') == -1) {
  2366. if (pageUrl.indexOf('?') == -1) {
  2367. pageUrl += '?p=1';
  2368. } else {
  2369. pageUrl += '&p=1';
  2370. }
  2371. }
  2372. let prevPageUrl = pageUrl.replace(/p=\d+/, 'p=' + (currentPage - g_settings.pageCount > 1 ? currentPage - g_settings.pageCount : 1));
  2373. let nextPageUrl = pageUrl.replace(/p=\d+/, 'p=' + (currentPage + g_settings.pageCount));
  2374. DoLog(LogLevel.Info, 'Previous page url: ' + prevPageUrl);
  2375. DoLog(LogLevel.Info, 'Next page url: ' + nextPageUrl);
  2376. // 重新插入一遍清除事件绑定
  2377. let prevButton = $(pageSelectorDiv).find('a:first');
  2378. prevButton.before(prevButton.clone());
  2379. prevButton.remove();
  2380. let nextButton = $(pageSelectorDiv).find('a:last');
  2381. nextButton.before(nextButton.clone());
  2382. nextButton.remove();
  2383. $(pageSelectorDiv).find('a:first').attr('href', prevPageUrl).addClass('pp-prevPage');
  2384. $(pageSelectorDiv).find('a:last').attr('href', nextPageUrl).addClass('pp-nextPage');
  2385. }
  2386.  
  2387. let onloadCallback = function (req) {
  2388. let no_artworks_found = false;
  2389.  
  2390. try {
  2391. let json = JSON.parse(req.responseText);
  2392. if (json.hasOwnProperty('error')) {
  2393. if (json.error === false) {
  2394. let data;
  2395. if (json.body.illustManga) {
  2396. data = json.body.illustManga.data;
  2397. } else if (json.body.manga) {
  2398. data = json.body.manga.data;
  2399. } else if (json.body.illust) {
  2400. data = json.body.illust.data;
  2401. }
  2402. if (data.length > 0) {
  2403. works = works.concat(data);
  2404. } else {
  2405. no_artworks_found = true;
  2406. }
  2407. } else {
  2408. DoLog(LogLevel.Error, 'ajax error!');
  2409. return;
  2410. }
  2411. } else {
  2412. DoLog(LogLevel.Error, 'Key "error" not found!');
  2413. return;
  2414. }
  2415. } catch (e) {
  2416. DoLog(LogLevel.Error, 'A invalid json string!');
  2417. DoLog(LogLevel.Info, req.responseText);
  2418. }
  2419.  
  2420. currentPage++;
  2421. currentGettingPageCount++;
  2422.  
  2423. // 后面已经没有作品了
  2424. if (no_artworks_found) {
  2425. DoLog(LogLevel.Warning, 'No artworks found, ignore ' + (g_settings.pageCount - currentGettingPageCount) + ' pages.');
  2426. currentPage += g_settings.pageCount - currentGettingPageCount;
  2427. currentGettingPageCount = g_settings.pageCount;
  2428. }
  2429. // 设定数量的页面加载完成
  2430. if (currentGettingPageCount == g_settings.pageCount) {
  2431. DoLog(LogLevel.Info, 'Load complete, start to load bookmark count.');
  2432. DoLog(LogLevel.Elements, works);
  2433.  
  2434. // 获取到的作品里面可能有广告,先删掉,否则后面一些处理需要做判断
  2435. let tempWorks = [];
  2436. let workIdsSet = new Set();
  2437. for (let i = 0; i < works.length; i++) {
  2438. if (works[i].id && !workIdsSet.has(works[i].id)) {
  2439. tempWorks.push(works[i]);
  2440. workIdsSet.add(works[i].id);
  2441. }
  2442. }
  2443. works = tempWorks;
  2444. DoLog(LogLevel.Info, 'Clear ad container complete.');
  2445. DoLog(LogLevel.Elements, works);
  2446.  
  2447. GetBookmarkCount(0);
  2448. } else {
  2449. getWorks(onloadCallback);
  2450. }
  2451. };
  2452.  
  2453. getWorks(onloadCallback);
  2454. }
  2455.  
  2456. let xhrs = [];
  2457. let currentRequestGroupMinimumIndex = 0;
  2458. function FillXhrsArray() {
  2459. xhrs.length = 0;
  2460. let onloadFunc = function (event) {
  2461. let json = null;
  2462. try {
  2463. json = JSON.parse(event.currentTarget.responseText);
  2464. } catch (e) {
  2465. DoLog(LogLevel.Error, 'Parse json failed!');
  2466. DoLog(LogLevel.Element, e);
  2467. return;
  2468. }
  2469.  
  2470. if (json) {
  2471. let illustId = '';
  2472. let illustIdMatched = event.currentTarget.responseURL.match(/illust_id=(\d+)/);
  2473. if (illustIdMatched) {
  2474. illustId = illustIdMatched[1];
  2475. } else {
  2476. DoLog(LogLevel.Error, 'Can not get illust id from url!');
  2477. return;
  2478. }
  2479. let indexOfThisRequest = -1;
  2480. for (let j = 0; j < g_maxXhr; j++) {
  2481. if (xhrs[j].illustId == illustId) {
  2482. indexOfThisRequest = j;
  2483. break;
  2484. }
  2485. }
  2486. if (indexOfThisRequest == -1) {
  2487. DoLog(LogLevel.Error, 'This url not match any request!');
  2488. return;
  2489. }
  2490. xhrs[indexOfThisRequest].complete = true;
  2491.  
  2492. if (!json.error) {
  2493. let bookmarkCount = json.body.illust_details.bookmark_user_total;
  2494. works[currentRequestGroupMinimumIndex + indexOfThisRequest].bookmarkCount = parseInt(bookmarkCount);
  2495. DoLog(LogLevel.Info, 'IllustId: ' + illustId + ', bookmarkCount: ' + bookmarkCount);
  2496. } else {
  2497. DoLog(LogLevel.Error, 'Some error occured: ' + json.message);
  2498. }
  2499.  
  2500. let completeCount = 0;
  2501. // 真实完成数(不包含没有发起请求的XHR,最后一批请求时)
  2502. let completeReally = 0;
  2503. for (let j = 0; j < g_maxXhr; j++) {
  2504. if (xhrs[j].complete) {
  2505. completeCount++;
  2506. if (xhrs[j].illustId != '') {
  2507. completeReally++;
  2508. }
  2509. }
  2510. }
  2511. $('#loading').find('#progress').text(Texts[g_language].sort_getBookmarkCount.replace('%1', currentRequestGroupMinimumIndex + completeReally).replace('%2', works.length));
  2512. if (completeCount == g_maxXhr) {
  2513. currentRequestGroupMinimumIndex += g_maxXhr;
  2514. GetBookmarkCount(currentRequestGroupMinimumIndex);
  2515. }
  2516. }
  2517. };
  2518. let onerrorFunc = function (event) {
  2519. let illustId = '';
  2520. let illustIdMatched = event.currentTarget.__sentry_xhr__.url.match(/artworks\/(\d+)/);
  2521. if (illustIdMatched) {
  2522. illustId = illustIdMatched[1];
  2523. } else {
  2524. DoLog(LogLevel.Error, 'Can not get illust id from url!');
  2525. return;
  2526. }
  2527.  
  2528. DoLog(LogLevel.Error, 'Send request failed, set this illust(' + illustId + ')\'s bookmark count to 0!');
  2529.  
  2530. let indexOfThisRequest = -1;
  2531. for (let j = 0; j < g_maxXhr; j++) {
  2532. if (xhrs[j].illustId == illustId) {
  2533. indexOfThisRequest = j;
  2534. break;
  2535. }
  2536. }
  2537. if (indexOfThisRequest == -1) {
  2538. DoLog('This url not match any request!');
  2539. return;
  2540. }
  2541. xhrs[indexOfThisRequest].complete = true;
  2542.  
  2543. let completeCount = 0;
  2544. let completeReally = 0;
  2545. for (let j = 0; j < g_maxXhr; j++) {
  2546. if (xhrs[j].complete) {
  2547. completeCount++;
  2548. if (xhrs[j].illustId != '') {
  2549. completeReally++;
  2550. }
  2551. }
  2552. }
  2553. $('#loading').find('#progress').text(Texts[g_language].sort_getBookmarkCount.replace('%1', currentRequestGroupMinimumIndex + completeReally).replace('%2', works.length));
  2554. if (completeCount == g_maxXhr) {
  2555. GetBookmarkCount(currentRequestGroupMinimumIndex + g_maxXhr);
  2556. }
  2557. };
  2558. for (let i = 0; i < g_maxXhr; i++) {
  2559. xhrs.push({
  2560. xhr: new XMLHttpRequest(),
  2561. illustId: '',
  2562. complete: false,
  2563. });
  2564. xhrs[i].xhr.onload = onloadFunc;
  2565. xhrs[i].xhr.onerror = onerrorFunc;
  2566. }
  2567. }
  2568.  
  2569. let GetBookmarkCount = function (index) {
  2570. if (index >= works.length) {
  2571. clearAndUpdateWorks();
  2572. return;
  2573. }
  2574.  
  2575. if (xhrs.length === 0) {
  2576. FillXhrsArray();
  2577. }
  2578.  
  2579. for (let i = 0; i < g_maxXhr; i++) {
  2580. if (index + i >= works.length) {
  2581. xhrs[i].complete = true;
  2582. xhrs[i].illustId = '';
  2583. continue;
  2584. }
  2585.  
  2586. let illustId = works[index + i].id;
  2587. let url = 'https://www.pixiv.net/touch/ajax/illust/details?illust_id=' + illustId;
  2588. xhrs[i].illustId = illustId;
  2589. xhrs[i].complete = false;
  2590. xhrs[i].xhr.open('GET', url, true);
  2591. xhrs[i].xhr.send(null);
  2592. }
  2593. };
  2594.  
  2595. /*
  2596. li
  2597. -div
  2598. --div
  2599. ---div
  2600. ----div
  2601. -----div
  2602. ------a
  2603. -------div: 多图标签、R18标签
  2604. -------div: 里面是 img (以及 svg 动图标签)
  2605. ------div: 里面是 like 相关的元素
  2606. ---a: 作品标题,跳转链接
  2607. ---div: 作者头像和昵称
  2608. */
  2609. let clearAndUpdateWorks = function () {
  2610. filterAndSort().then(function () {
  2611.  
  2612. let container = Pages[PageType.Search].GetImageListContainer();
  2613. let firstImageElement = Pages[PageType.Search].GetFirstImageElement();
  2614. if (imageElementTemplate == null) {
  2615. imageElementTemplate = firstImageElement.cloneNode(true);
  2616.  
  2617. // 清理模板
  2618. // image
  2619. let img = $($(imageElementTemplate).find('img').get(0));
  2620. let imageDiv = img.parent();
  2621. let imageLink = imageDiv.parent();
  2622. let imageLinkDiv = imageLink.parent();
  2623. let titleLinkParent = imageLinkDiv.parent().next();
  2624. if (img == null || imageDiv == null || imageLink == null || imageLinkDiv == null || titleLinkParent == null) {
  2625. DoLog(LogLevel.Error, 'Can not found some elements!');
  2626. }
  2627. let titleLink = $('<a></a>');
  2628. if (titleLinkParent.children().length == 0) {
  2629. titleLinkParent.append(titleLink);
  2630. } else {
  2631. titleLink = titleLinkParent.children('a:first');
  2632. }
  2633.  
  2634. // author
  2635. let authorDiv = titleLinkParent.next();
  2636. let authorLinkProfileImage = authorDiv.find('a:first');
  2637. let authorLink = authorDiv.find('a:last');
  2638. let authorName = authorLink;
  2639. let authorImage = $(authorDiv.find('img').get(0));
  2640.  
  2641. // others
  2642. let bookmarkDiv = imageLink.next();
  2643. let bookmarkSvg = bookmarkDiv.find('svg');
  2644. let additionTagDiv = imageDiv.prev();
  2645. let animationTag = imageDiv.find('svg');
  2646.  
  2647. let bookmarkCountDiv = additionTagDiv.clone();
  2648. bookmarkCountDiv.css({ 'top': 'auto', 'bottom': '0px', 'width': '50%' });
  2649. additionTagDiv.parent().append(bookmarkCountDiv);
  2650.  
  2651. // 添加 class,方便后面修改内容
  2652. img.addClass('ppImg');
  2653. imageLink.addClass('ppImageLink');
  2654. //if (titleLink.get(0).tagName == 'A') {
  2655. titleLink.addClass('ppTitleLink');
  2656. //} else {
  2657. // titleLink.append('<a class="ppTitleLink"></a>');
  2658. //}
  2659. authorLinkProfileImage.addClass('ppAuthorLinkProfileImage');
  2660. authorLink.addClass('ppAuthorLink');
  2661. authorName.addClass('ppAuthorName');
  2662. authorImage.addClass('ppAuthorImage');
  2663. bookmarkSvg.attr('class', bookmarkSvg.attr('class') + ' ppBookmarkSvg');
  2664. additionTagDiv.addClass('ppAdditionTag');
  2665. bookmarkCountDiv.addClass('ppBookmarkCount');
  2666.  
  2667. img.attr('src', '');
  2668. additionTagDiv.empty();
  2669. bookmarkCountDiv.empty();
  2670. animationTag.remove();
  2671. bookmarkSvg.find('path:first').css('fill', 'rgb(31, 31, 31)');
  2672. bookmarkSvg.find('path:last').css('fill', 'rgb(255, 255, 255)');
  2673.  
  2674. if (g_settings.linkBlank) {
  2675. imageLink.attr('target', '_blank');
  2676. titleLink.attr('target', '_blank');
  2677. authorLinkProfileImage.attr('target', '_blank');
  2678. authorLink.attr('target', '_blank');
  2679. }
  2680. }
  2681.  
  2682. $(container).empty();
  2683. for (let i = 0; i < works.length; i++) {
  2684. let li = $(imageElementTemplate.cloneNode(true));
  2685.  
  2686. let regularUrl = works[i].url;
  2687. if (g_settings.fullSizeThumb) {
  2688. regularUrl = convertThumbUrlToSmall(works[i].url);
  2689. }
  2690. li.find('.ppImg').attr('src', regularUrl).css('object-fit', 'contain');
  2691. li.find('.ppImageLink').attr('href', '/artworks/' + works[i].id);
  2692. li.find('.ppTitleLink').attr('href', '/artworks/' + works[i].id).text(works[i].title);
  2693. li.find('.ppAuthorLink, .ppAuthorLinkProfileImage').attr('href', '/member.php?id=' + works[i].userId).attr({ 'userId': works[i].userId, 'profileImageUrl': works[i].profileImageUrl, 'userName': works[i].userName });
  2694. li.find('.ppAuthorName').text(works[i].userName);
  2695. li.find('.ppAuthorImage').attr('src', works[i].profileImageUrl);
  2696. li.find('.ppBookmarkSvg').attr('illustId', works[i].id);
  2697. if (works[i].bookmarkData) {
  2698. li.find('.ppBookmarkSvg').find('path').css('fill', 'rgb(255, 64, 96)');
  2699. li.find('.ppBookmarkSvg').attr('bookmarkId', works[i].bookmarkData.id);
  2700. }
  2701. if (works[i].xRestrict !== 0) {
  2702. let R18HTML = '<div style="margin-top: 2px; margin-left: 2px;"><div style="color: rgb(255, 255, 255);font-weight: bold;font-size: 10px;line-height: 1;padding: 3px 6px;border-radius: 3px;background: rgb(255, 64, 96);">R-18</div></div>';
  2703. li.find('.ppAdditionTag').append(R18HTML);
  2704. }
  2705. if (works[i].pageCount > 1) {
  2706. let pageCountHTML = '<div style="display: flex;-webkit-box-align: center;align-items: center;box-sizing: border-box;margin-left: auto;height: 20px;color: rgb(255, 255, 255);font-size: 10px;line-height: 12px;font-weight: bold;flex: 0 0 auto;padding: 4px 6px;background: rgba(0, 0, 0, 0.32);border-radius: 10px;">\<svg viewBox="0 0 9 10" width="9" height="10" style="stroke: none;line-height: 0;font-size: 0px;fill: currentcolor;"><path d="M8,3 C8.55228475,3 9,3.44771525 9,4 L9,9 C9,9.55228475 8.55228475,10 8,10 L3,10 C2.44771525,10 2,9.55228475 2,9 L6,9 C7.1045695,9 8,8.1045695 8,7 L8,3 Z M1,1 L6,1 C6.55228475,1 7,1.44771525 7,2 L7,7 C7,7.55228475 6.55228475,8 6,8 L1,8 C0.44771525,8 0,7.55228475 0,7 L0,2 C0,1.44771525 0.44771525,1 1,1 Z"></path></svg><span style="margin-left: 2px;">' + works[i].pageCount + '</span></div>';
  2707. li.find('.ppAdditionTag').append(pageCountHTML);
  2708. }
  2709. let bookmarkCountHTML = '<div style="margin-bottom: 6px; margin-left: 2px;"><div style="color: rgb(7, 95, 166);font-weight: bold;font-size: 13px;line-height: 1;padding: 3px 6px;border-radius: 3px;background: rgb(204, 236, 255);">' + works[i].bookmarkCount + ' likes</div></div>';
  2710. li.find('.ppBookmarkCount').append(bookmarkCountHTML);
  2711. if (works[i].illustType == 2) {
  2712. let animationHTML = '<svg viewBox="0 0 24 24" style="width: 48px; height: 48px;stroke: none;fill: rgb(255, 255, 255);line-height: 0;font-size: 0px;vertical-align: middle;position:absolute;"><circle cx="12" cy="12" r="10" style="fill: rgb(0, 0, 0);fill-opacity: 0.4;"></circle><path d="M9,8.74841664 L9,15.2515834 C9,15.8038681 9.44771525,16.2515834 10,16.2515834 C10.1782928,16.2515834 10.3533435,16.2039156 10.5070201,16.1135176 L16.0347118,12.8619342 C16.510745,12.5819147 16.6696454,11.969013 16.3896259,11.4929799 C16.3034179,11.3464262 16.1812655,11.2242738 16.0347118,11.1380658 L10.5070201,7.88648243 C10.030987,7.60646294 9.41808527,7.76536339 9.13806578,8.24139652 C9.04766776,8.39507316 9,8.57012386 9,8.74841664 Z"></path></svg>';
  2713. li.find('.ppImg').after(animationHTML);
  2714. }
  2715.  
  2716. $(container).append(li);
  2717. }
  2718.  
  2719. // 监听加入书签点击事件,监听父节点,但是按照 <svg> 节点处理
  2720. $('.ppBookmarkSvg').parent().on('click', function (ev) {
  2721. if (g_csrfToken == '') {
  2722. DoLog(LogLevel.Error, 'No g_csrfToken, failed to add bookmark!');
  2723. alert('获取 Token 失败,无法添加,请到详情页操作。');
  2724. return;
  2725. }
  2726. // 非公开收藏
  2727. let restrict = 0;
  2728. if (ev.ctrlKey) {
  2729. restrict = 1;
  2730. }
  2731.  
  2732. let _this = $(this).children('svg:first');
  2733. let illustId = _this.attr('illustId');
  2734. let bookmarkId = _this.attr('bookmarkId');
  2735. if (bookmarkId == null || bookmarkId == '') {
  2736. DoLog(LogLevel.Info, 'Add bookmark, illustId: ' + illustId);
  2737. $.ajax('/ajax/illusts/bookmarks/add', {
  2738. method: 'POST',
  2739. contentType: 'application/json;charset=utf-8',
  2740. headers: { 'x-csrf-token': g_csrfToken },
  2741. data: '{"illust_id":"' + illustId + '","restrict":' + restrict + ',"comment":"","tags":[]}',
  2742. success: function (data) {
  2743. DoLog(LogLevel.Info, 'addBookmark result: ');
  2744. DoLog(LogLevel.Elements, data);
  2745. if (data.error) {
  2746. DoLog(LogLevel.Error, 'Server returned an error: ' + data.message);
  2747. return;
  2748. }
  2749. let bookmarkId = data.body.last_bookmark_id;
  2750. DoLog(LogLevel.Info, 'Add bookmark success, bookmarkId is ' + bookmarkId);
  2751. _this.attr('bookmarkId', bookmarkId);
  2752. _this.find('path').css('fill', 'rgb(255, 64, 96)');
  2753. }
  2754. });
  2755. } else {
  2756. DoLog(LogLevel.Info, 'Delete bookmark, bookmarkId: ' + bookmarkId);
  2757. $.ajax('/rpc/index.php', {
  2758. method: 'POST',
  2759. headers: { 'x-csrf-token': g_csrfToken },
  2760. data: { "mode": "delete_illust_bookmark", "bookmark_id": bookmarkId },
  2761. success: function (data) {
  2762. DoLog(LogLevel.Info, 'delete bookmark result: ');
  2763. DoLog(LogLevel.Elements, data);
  2764. if (data.error) {
  2765. DoLog(LogLevel.Error, 'Server returned an error: ' + data.message);
  2766. return;
  2767. }
  2768. DoLog(LogLevel.Info, 'Delete bookmark success.');
  2769. _this.attr('bookmarkId', '');
  2770. _this.find('path:first').css('fill', 'rgb(31, 31, 31)');
  2771. _this.find('path:last').css('fill', 'rgb(255, 255, 255)');
  2772. }
  2773. });
  2774. }
  2775.  
  2776. _this.parent().focus();
  2777. });
  2778.  
  2779. $('.ppAuthorLink').on('mouseenter', function (e) {
  2780. let _this = $(this);
  2781.  
  2782. function getOffset(e) {
  2783. if (e.offsetParent) {
  2784. let offset = getOffset(e.offsetParent);
  2785. return {
  2786. offsetTop: e.offsetTop + offset.offsetTop,
  2787. offsetLeft: e.offsetLeft + offset.offsetLeft,
  2788. };
  2789. } else {
  2790. return {
  2791. offsetTop: e.offsetTop,
  2792. offsetLeft: e.offsetLeft,
  2793. };
  2794. }
  2795. }
  2796.  
  2797. let isFollowed = false;
  2798. $.ajax('https://www.pixiv.net/ajax/user/' + _this.attr('userId') + '?full=1', {
  2799. method: 'GET',
  2800. async: false,
  2801. success: function (data) {
  2802. if (data.error == false && data.body.isFollowed) {
  2803. isFollowed = true;
  2804. }
  2805. },
  2806. });
  2807.  
  2808. $('.pp-authorDiv').remove();
  2809. let pres = $('<div class="pp-authorDiv"><div class="ppa-main" style="position: absolute; top: 0px; left: 0px; border-width: 1px; border-style: solid; z-index: 1; border-color: rgba(0, 0, 0, 0.08); border-radius: 8px;"><div class=""style=" width: 336px; background-color: rgb(255, 255, 255); padding-top: 24px; flex-flow: column;"><div class=""style=" display: flex; align-items: center; flex-flow: column;"><a class="ppa-authorLink"><div role="img"size="64"class=""style=" display: inline-block; width: 64px; height: 64px; border-radius: 50%; overflow: hidden;"><img class="ppa-authorImage" width="64"height="64"style="object-fit: cover; object-position: center top;"></div></a><a class="ppa-authorLink"><div class="ppa-authorName" style=" line-height: 24px; font-size: 16px; font-weight: bold; margin: 4px 0px 0px;"></div></a><div class=""style=" margin: 12px 0px 24px;"><button type="button"class="ppa-follow"style=" padding: 9px 25px; line-height: 1; border: none; border-radius: 16px; font-weight: 700; background-color: #0096fa; color: #fff; cursor: pointer;"><span style="margin-right: 4px;"><svg viewBox="0 0 8 8"width="10"height="10"class=""style=" stroke: rgb(255, 255, 255); stroke-linecap: round; stroke-width: 2;"><line x1="1"y1="4"x2="7"y2="4"></line><line x1="4"y1="1"x2="4"y2="7"></line></svg></span>关注</button></div></div></div></div></div>');
  2810. $('body').append(pres);
  2811. let offset = getOffset(this);
  2812. pres.find('.ppa-main').css({ 'top': offset.offsetTop - 196 + 'px', 'left': offset.offsetLeft - 113 + 'px' });
  2813. pres.find('.ppa-authorLink').attr('href', '/member.php?id=' + _this.attr('userId'));
  2814. pres.find('.ppa-authorImage').attr('src', _this.attr('profileImageUrl'));
  2815. pres.find('.ppa-authorName').text(_this.attr('userName'));
  2816. if (isFollowed) {
  2817. pres.find('.ppa-follow').get(0).outerHTML = '<button type="button" class="ppa-follow followed" data-click-action="click" data-click-label="follow" style="padding: 9px 25px;line-height: 1;border: none;border-radius: 16px;font-size: 14px;font-weight: 700;cursor: pointer;">关注中</button>';
  2818. }
  2819. pres.find('.ppa-follow').attr('userId', _this.attr('userId'));
  2820. pres.on('mouseleave', function (e) {
  2821. $(this).remove();
  2822. }).on('mouseenter', function () {
  2823. $(this).addClass('mouseenter');
  2824. });
  2825.  
  2826. pres.find('.ppa-follow').on('click', function () {
  2827. let userId = $(this).attr('userId');
  2828. if ($(this).hasClass('followed')) {
  2829. // 取关
  2830. $.ajax('https://www.pixiv.net/rpc_group_setting.php', {
  2831. method: 'POST',
  2832. headers: { 'x-csrf-token': g_csrfToken },
  2833. data: 'mode=del&type=bookuser&id=' + userId,
  2834. success: function (data) {
  2835. DoLog(LogLevel.Info, 'delete bookmark result: ');
  2836. DoLog(LogLevel.Elements, data);
  2837.  
  2838. if (data.type == 'bookuser') {
  2839. $('.ppa-follow').get(0).outerHTML = '<button type="button"class="ppa-follow"style=" padding: 9px 25px; line-height: 1; border: none; border-radius: 16px; font-weight: 700; background-color: #0096fa; color: #fff; cursor: pointer;"><span style="margin-right: 4px;"><svg viewBox="0 0 8 8"width="10"height="10"class=""style=" stroke: rgb(255, 255, 255); stroke-linecap: round; stroke-width: 2;"><line x1="1"y1="4"x2="7"y2="4"></line><line x1="4"y1="1"x2="4"y2="7"></line></svg></span>关注</button>';
  2840. }
  2841. else {
  2842. DoLog(LogLevel.Error, 'Delete follow failed!');
  2843. }
  2844. }
  2845. });
  2846. } else {
  2847. // 关注
  2848. $.ajax('https://www.pixiv.net/bookmark_add.php', {
  2849. method: 'POST',
  2850. headers: { 'x-csrf-token': g_csrfToken },
  2851. data: 'mode=add&type=user&user_id=' + userId + '&tag=&restrict=0&format=json',
  2852. success: function (data) {
  2853. DoLog(LogLevel.Info, 'addBookmark result: ');
  2854. DoLog(LogLevel.Elements, data);
  2855. // success
  2856. if (data.length === 0) {
  2857. $('.ppa-follow').get(0).outerHTML = '<button type="button" class="ppa-follow followed" data-click-action="click" data-click-label="follow" style="padding: 9px 25px;line-height: 1;border: none;border-radius: 16px;font-size: 14px;font-weight: 700;cursor: pointer;">关注中</button>';
  2858. } else {
  2859. DoLog(LogLevel.Error, 'Follow failed!');
  2860. }
  2861. }
  2862. });
  2863. }
  2864. });
  2865. }).on('mouseleave', function (e) {
  2866. setTimeout(function () {
  2867. if (!$('.pp-authorDiv').hasClass('mouseenter')) {
  2868. $('.pp-authorDiv').remove();
  2869. }
  2870. }, 200);
  2871. });
  2872.  
  2873. if (works.length === 0) {
  2874. $(container).show().get(0).outerHTML = '<div class=""style="display: flex;align-items: center;justify-content: center; height: 408px;flex-flow: column;"><div class=""style="margin-bottom: 12px;color: rgba(0, 0, 0, 0.16);"><svg viewBox="0 0 16 16"size="72"style="fill: currentcolor;height: 72px;vertical-align: middle;"><path d="M8.25739 9.1716C7.46696 9.69512 6.51908 10 5.5 10C2.73858 10 0.5 7.76142 0.5 5C0.5 2.23858 2.73858 0 5.5 0C8.26142 0 10.5 2.23858 10.5 5C10.5 6.01908 10.1951 6.96696 9.67161 7.75739L11.7071 9.79288C12.0976 10.1834 12.0976 10.8166 11.7071 11.2071C11.3166 11.5976 10.6834 11.5976 10.2929 11.2071L8.25739 9.1716ZM8.5 5C8.5 6.65685 7.15685 8 5.5 8C3.84315 8 2.5 6.65685 2.5 5C2.5 3.34315 3.84315 2 5.5 2C7.15685 2 8.5 3.34315 8.5 5Z"transform="translate(2.25 2.25)"fill-rule="evenodd"clip-rule="evenodd"></path></svg></div><span class="sc-LzMCO fLDUzU">' + Texts[g_language].sort_noWork + '</span></div>';
  2875. }
  2876.  
  2877. // 恢复显示
  2878. $('#loading').remove();
  2879. $(container).show();
  2880.  
  2881. Pages[PageType.Search].ProcessPageElements();
  2882.  
  2883. // 监听键盘的左右键,用来翻页
  2884. $(document).keydown(function (e) {
  2885. if (g_settings.pageByKey != 1) {
  2886. return;
  2887. }
  2888. if (e.keyCode == 39) {
  2889. let btn = $('.pp-nextPage');
  2890. if (btn.length < 1 || btn.attr('hidden') == 'hidden') {
  2891. return;
  2892. }
  2893. // 很奇怪不能用 click()
  2894. location.href = btn.attr('href');
  2895. } else if (e.keyCode == 37) {
  2896. let btn = $('.pp-prevPage');
  2897. if (btn.length < 1 || btn.attr('hidden') == 'hidden') {
  2898. return;
  2899. }
  2900. location.href = btn.attr('href');
  2901. }
  2902. });
  2903.  
  2904. if (callback) {
  2905. callback();
  2906. }
  2907. });
  2908. }
  2909. };
  2910.  
  2911. /* ---------------------------------------- 小说 ---------------------------------------- */
  2912. function PixivNS(callback) {
  2913. function findNovelSection() {
  2914. let ul = $('section>div>ul');
  2915. if (ul.length == 0) {
  2916. DoLog(LogLevel.Error, 'Can not found novel list.');
  2917. return null;
  2918. }
  2919. return ul;
  2920. }
  2921.  
  2922. function getSearchParamsWithoutPage() {
  2923. return location.search.substr(1).replace(/&?p=\d+/, '');
  2924. }
  2925.  
  2926. function getNovelTemplate(ul) {
  2927. if (!ul) {
  2928. return null;
  2929. }
  2930. if (ul.length == 0) {
  2931. DoLog(LogLevel.Error, 'Empty list, can not create template.');
  2932. return null;
  2933. }
  2934. let template = ul.children().eq(0).clone(true)
  2935. // 左侧图片
  2936. let picDiv = template.children().eq(0).children().eq(0);
  2937. picDiv.find('a:first').addClass('pns-link');
  2938. picDiv.find('img:first').addClass('pns-img');
  2939. // 右侧详情
  2940. let detailDiv = template.children().eq(0).children().eq(1).children().eq(0);
  2941. let titleDiv = detailDiv.children().eq(0);
  2942. if (titleDiv.children().length > 1) {
  2943. titleDiv.children().eq(0).addClass('pns-series');
  2944. } else {
  2945. // 如果作为模板的DIV没有系列,就自己加一个
  2946. let series = $('<a class="pns-series" href="/novel/series/000000"></a>');
  2947. series.css({
  2948. 'display': 'inline-block',
  2949. 'white-space': 'nowrap',
  2950. 'text-overflow': 'ellipsis',
  2951. 'overflow': 'hidden',
  2952. 'max-width': '100%',
  2953. 'line-height': '22px',
  2954. 'font-size': '14px',
  2955. 'text-decoration': 'none'
  2956. });
  2957. $('head').append('<style>.pns-series:visited{color:rgb(173,173,173)}</style>');
  2958. titleDiv.prepend(series);
  2959. }
  2960. titleDiv.children().eq(1).addClass('pns-title').addClass('pns-link');
  2961. let authorDiv = detailDiv.children().eq(1);
  2962. authorDiv.children().eq(0).addClass('pns-author');
  2963. let tagDiv = detailDiv.children().eq(2);
  2964. let bookmarkDiv = tagDiv.children().eq(0);
  2965. bookmarkDiv.children().eq(0).addClass('pns-text-count');
  2966. if (bookmarkDiv.children().length < 2) {
  2967. bookmarkDiv.find('.pns-text-count').after('<div class="pns-bookmark-div"><span style="display: inline-flex; vertical-align: top; height: 20px;"><svg viewBox="0 0 12 12" size="12" class="sc-14heosd-1 dcwYur"><path fill-rule="evenodd" clip-rule="evenodd" d="M9 0.75C10.6569 0.75 12 2.09315 12 3.75C12 7.71703 7.33709 10.7126 6.23256 11.3666C6.08717 11.4526 5.91283 11.4526 5.76744 11.3666C4.6629 10.7126 0 7.71703 0 3.75C0 2.09315 1.34315 0.75 3 0.75C4.1265 0.75 5.33911 1.60202 6 2.66823C6.66089 1.60202 7.8735 0.75 9 0.75Z"></path></svg></span><span class="pns-bookmark-count" style="padding-left: 4px;">0</span></div>')
  2968. } else {
  2969. bookmarkDiv.find('span:last').addClass('pns-bookmark-count').parent().addClass('pns-bookmark-div');
  2970. }
  2971. detailDiv.children().eq(3).empty().addClass('pns-tag-list');
  2972. let descDiv = detailDiv.children().eq(4);
  2973. descDiv.children().eq(0).addClass('pns-desc');
  2974. // 右下角爱心
  2975. let likeDiv = template.children().eq(0).children().eq(1).children().eq(1);
  2976. let svg = likeDiv.find('svg');
  2977. svg.attr('class', svg.attr('class') + ' pns-like');
  2978. likeDiv.find('path:first').css('color', 'rgb(31, 31, 31)');
  2979. likeDiv.find('path:last').css('fill', 'rgb(255, 255, 255)');
  2980.  
  2981. return template;
  2982. }
  2983.  
  2984. function fillTemplate(template, novel) {
  2985. if (template == null || novel == null) {
  2986. return null;
  2987. }
  2988. let link = template.find('.pns-link:first').attr('href').replace(/id=\d+/g, 'id=' + novel.id);
  2989. template.find('.pns-link').attr('href', link);
  2990. template.find('.pns-img').attr('src', novel.url);
  2991. if (novel.seriesId) {
  2992. let seriesLink = template.find('.pns-series').attr('href').replace(/\d+$/, novel.seriesId);
  2993. template.find('.pns-series').text(novel.seriesTitle).attr('title', novel.seriesTitle).attr('href', seriesLink);
  2994. } else {
  2995. template.find('.pns-series').hide();
  2996. }
  2997. template.find('.pns-title').text(novel.title).attr('title', novel.title);
  2998. let authorLink = template.find('.pns-author').attr('href').replace(/\d+$/, novel.userId);
  2999. template.find('.pns-author').text(novel.userName).attr('href', authorLink);
  3000. template.find('.pns-text-count').text(novel.textCount + '文字');
  3001. if (novel.bookmarkCount == 0) {
  3002. template.find('.pns-bookmark-div').hide();
  3003. } else {
  3004. template.find('.pns-bookmark-count').text(novel.bookmarkCount);
  3005. }
  3006. let tagList = template.find('.pns-tag-list');
  3007. let search = getSearchParamsWithoutPage();
  3008. if (search.length > 0) {
  3009. search = '?' + search;
  3010. }
  3011. $.each(novel.tags, function (i, tag) {
  3012. let tagItem = $('<span"><a style="color: rgb(61, 118, 153);" href="/tags/' + tag + '/novels' + search + '">' + tag + '</a></span>');
  3013. if (tag == 'R-18' || tag == 'R-18G') {
  3014. tagItem.find('a').css({ 'color': 'rgb(255, 64, 96)', 'font-weight': 'bold' }).text(tag);
  3015. }
  3016. tagList.append(tagItem);
  3017. });
  3018. template.find('.pns-desc').html(novel.description).attr('title', template.find('.pns-desc').text());
  3019. let like = template.find('.pns-like');
  3020. like.attr('novel-id', novel.id);
  3021. if (novel.bookmarkData) {
  3022. like.attr('bookmark-id', novel.bookmarkData.id);
  3023. like.find('path:first').css('color', 'rgb(255, 64, 96)');
  3024. like.find('path:last').css('fill', 'rgb(255, 64, 96)');
  3025. }
  3026. like.click(function() {
  3027. if ($(this).attr('disable')) {
  3028. return;
  3029. }
  3030. let bid = $(this).attr('bookmark-id');
  3031. let nid = $(this).attr('novel-id');
  3032. if (bid) {
  3033. deleteBookmark($(this), bid);
  3034. } else {
  3035. addBookmark($(this), nid, 0);
  3036. }
  3037. $(this).blur();
  3038. });
  3039. if (g_settings.linkBlank) {
  3040. template.find('a').attr('target', '_blank');
  3041. }
  3042. return template;
  3043. }
  3044.  
  3045. function getNovelByPage(key, from, to, total) {
  3046. if (total == undefined) {
  3047. total = to - from;
  3048. }
  3049.  
  3050. let url = location.origin + g_getNovelUrl.replace(/#key#/g, key).replace(/#page#/g, from);
  3051. let search = getSearchParamsWithoutPage();
  3052. if (search.length > 0) {
  3053. url += '&' + search;
  3054. }
  3055.  
  3056. updateProgress(Texts[g_language].nsort_getWorks.replace('1%', total - to + from + 1).replace('2%', total));
  3057.  
  3058. let novelList = [];
  3059. function onLoadFinish(data, resolve) {
  3060. if (data && data.body && data.body.novel && data.body.novel.data) {
  3061. novelList = novelList.concat(data.body.novel.data);
  3062. }
  3063.  
  3064. if (from == to - 1) {
  3065. resolve(novelList);
  3066. } else {
  3067. getNovelByPage(key, from + 1, to, total).then(function (list) {
  3068. if (list && list.length > 0) {
  3069. novelList = novelList.concat(list);
  3070. }
  3071. resolve(novelList);
  3072. });
  3073. }
  3074. }
  3075.  
  3076. return new Promise(function (resolve, reject) {
  3077. $.ajax({
  3078. url: url,
  3079. success: function (data) {
  3080. onLoadFinish(data, resolve);
  3081. },
  3082. error: function () {
  3083. DoLog(LogLevel.Error, 'get novel page ' + from + ' failed!');
  3084. onLoadFinish(null, resolve);
  3085. },
  3086. });
  3087. });
  3088. }
  3089.  
  3090. function sortNovel(list) {
  3091. updateProgress(Texts[g_language].nsort_sorting);
  3092. // 排序
  3093. list.sort(function (a, b) {
  3094. let bookmarkA = a.bookmarkCount;
  3095. let bookmarkB = b.bookmarkCount;
  3096. if (!bookmarkA) {
  3097. bookmarkA = 0;
  3098. }
  3099. if (!bookmarkB) {
  3100. bookmarkB = 0;
  3101. }
  3102. if (bookmarkA > bookmarkB) {
  3103. return -1;
  3104. }
  3105. if (bookmarkA < bookmarkB) {
  3106. return 1;
  3107. }
  3108. return 0;
  3109. });
  3110. return list;
  3111. }
  3112.  
  3113. function rearrangeNovel(list) {
  3114. let ul = findNovelSection();
  3115. if (ul == null) {
  3116. return;
  3117. }
  3118. let template = getNovelTemplate(ul);
  3119. if (template == null) {
  3120. return;
  3121. }
  3122. let newList = [];
  3123. $.each(list, function (i, novel) {
  3124. let e = fillTemplate(template.clone(true), novel);
  3125. if (e != null) {
  3126. newList.push(e);
  3127. }
  3128. });
  3129. ul.empty();
  3130. $.each(newList, function (i, e) {
  3131. ul.append(e);
  3132. });
  3133. hideLoading();
  3134. }
  3135.  
  3136. function getKeyWord() {
  3137. let match = location.pathname.match(/\/tags\/(.+)\/novels/);
  3138. if (!match) {
  3139. return '';
  3140. }
  3141. return match[1];
  3142. }
  3143.  
  3144. function getCurrentPage() {
  3145. let match = location.search.match(/p=(\d+)/);
  3146. if (match) {
  3147. return parseInt(match[1]);
  3148. }
  3149. return 1;
  3150. }
  3151.  
  3152. function showLoading() {
  3153. let ul = findNovelSection();
  3154. if (ul == null) {
  3155. iLog.e('Can not found novel section!');
  3156. return;
  3157. }
  3158.  
  3159. ul.hide().before('<div id="loading" style="width:100%;text-align:center;"><img src="' + g_loadingImage + '" /><p id="progress" style="text-align: center;font-size: large;font-weight: bold;padding-top: 10px;">0%</p></div>');
  3160. }
  3161.  
  3162. function hideLoading() {
  3163. let ul = findNovelSection();
  3164. if (ul == null) {
  3165. iLog.e('Can not found novel section!');
  3166. return;
  3167. }
  3168.  
  3169. $('#loading').remove();
  3170. ul.show();
  3171. }
  3172.  
  3173. function updateProgress(msg) {
  3174. let p = $('#progress');
  3175. p.text(msg);
  3176. }
  3177.  
  3178. function addBookmark(element, novelId, restrict) {
  3179. if (g_csrfToken == '') {
  3180. iLog.e('No g_csrfToken, failed to add bookmark!');
  3181. alert('获取 Token 失败,无法添加,请到详情页操作。');
  3182. return;
  3183. }
  3184. element.attr('disable', 'disable');
  3185. iLog.i('add bookmark: ' + novelId);
  3186. $.ajax('/ajax/novels/bookmarks/add', {
  3187. method: 'POST',
  3188. contentType: 'application/json;charset=utf-8',
  3189. headers: { 'x-csrf-token': g_csrfToken },
  3190. data: '{"novel_id":"' + novelId + '","restrict":' + restrict + ',"comment":"","tags":[]}',
  3191. success: function (data) {
  3192. iLog.i('add novel bookmark result: ');
  3193. iLog.d(data);
  3194. if (data.error) {
  3195. iLog.e('Server returned an error: ' + data.message);
  3196. return;
  3197. }
  3198. let bookmarkId = data.body;
  3199. iLog.i('Add novel bookmark success, bookmarkId is ' + bookmarkId);
  3200. element.attr('bookmark-id', bookmarkId);
  3201. element.find('path:first').css('color', 'rgb(255, 64, 96)');
  3202. element.find('path:last').css('fill', 'rgb(255, 64, 96)');
  3203. element.removeAttr('disable');
  3204. },
  3205. error: function() {
  3206. element.removeAttr('disable');
  3207. }
  3208. });
  3209. }
  3210.  
  3211. function deleteBookmark(element, bookmarkId) {
  3212. if (g_csrfToken == '') {
  3213. iLog.e('No g_csrfToken, failed to add bookmark!');
  3214. alert('获取 Token 失败,无法添加,请到详情页操作。');
  3215. return;
  3216. }
  3217. element.attr('disable', 'disable');
  3218. iLog.i('delete bookmark: ' + bookmarkId);
  3219. $.ajax('/ajax/novels/bookmarks/delete', {
  3220. method: 'POST',
  3221. headers: { 'x-csrf-token': g_csrfToken },
  3222. data: { 'del': 1, 'book_id': bookmarkId },
  3223. success: function (data) {
  3224. iLog.i('delete novel bookmark result: ');
  3225. iLog.d(data);
  3226. if (data.error) {
  3227. iLog.e('Server returned an error: ' + data.message);
  3228. return;
  3229. }
  3230. iLog.i('delete novel bookmark success');
  3231. element.removeAttr('bookmark-id');
  3232. element.find('path:first').css('color', 'rgb(31, 31, 31)');
  3233. element.find('path:last').css('fill', 'rgb(255, 255, 255)');
  3234. element.removeAttr('disable');
  3235. },
  3236. error: function() {
  3237. element.removeAttr('disable');
  3238. }
  3239. });
  3240. }
  3241.  
  3242. function changePageSelector() {
  3243. let pager = Pages[PageType.NovelSearch].GetPageSelector();
  3244. if (pager.length == 0) {
  3245. iLog.e('can not found page selector!');
  3246. return;
  3247. }
  3248. let left = pager.find('a:first').clone().attr('aria-disabled', 'false').removeAttr('hidden').addClass('pp-prevPage');
  3249. let right = pager.find('a:last').clone().attr('aria-disabled', 'false').removeAttr('hidden').addClass('pp-nextPage');
  3250. let normal = pager.find('a').eq(1).clone().removeAttr('href');
  3251. let href = location.href;
  3252. let match = href.match(/[?&]p=(\d+)/);
  3253. let page = 1;
  3254. if (match) {
  3255. page = parseInt(match[1]);
  3256. } else {
  3257. if (location.search == '') {
  3258. href += '?p=1';
  3259. } else {
  3260. href += '&p=1';
  3261. }
  3262. }
  3263. if (page == 1) {
  3264. left.attr('hidden', 'hidden');
  3265. }
  3266. pager.empty();
  3267. let lp = page - g_settings.novelPageCount;
  3268. left.attr('href', href.replace('?p=' + page, '?p=' + lp).replace('&p=' + page, '&p=' + lp));
  3269. pager.append(left);
  3270. let s = 'Previewer';
  3271. for (let i = 0; i < s.length; ++i) {
  3272. let n = normal.clone().text(s[i]);
  3273. pager.append(n);
  3274. }
  3275. let rp = page + g_settings.novelPageCount;
  3276. right.attr('href', href.replace('?p=' + page, '?p=' + rp).replace('&p=' + page, '&p=' + rp));
  3277. pager.append(right);
  3278. }
  3279.  
  3280. function listnerToKeyBoard() {
  3281. $(document).keydown(function (e) {
  3282. if (g_settings.pageByKey != 1) {
  3283. return;
  3284. }
  3285. if (e.keyCode == 39) {
  3286. let btn = $('.pp-nextPage');
  3287. if (btn.length < 1 || btn.attr('hidden') == 'hidden') {
  3288. return;
  3289. }
  3290. location.href = btn.attr('href');
  3291. } else if (e.keyCode == 37) {
  3292. let btn = $('.pp-prevPage');
  3293. if (btn.length < 1 || btn.attr('hidden') == 'hidden') {
  3294. return;
  3295. }
  3296. location.href = btn.attr('href');
  3297. }
  3298. });
  3299. }
  3300.  
  3301. function main() {
  3302. let keyWord = getKeyWord();
  3303. if (keyWord.length == 0) {
  3304. DoLog(LogLevel.Error, 'Parse key word error.');
  3305. return;
  3306. }
  3307. let currentPage = getCurrentPage();
  3308.  
  3309. showLoading();
  3310. changePageSelector();
  3311. listnerToKeyBoard();
  3312. getNovelByPage(keyWord, currentPage, currentPage + g_settings.novelPageCount).then(function (novelList) {
  3313. rearrangeNovel(sortNovel(novelList));
  3314. });
  3315. }
  3316.  
  3317. main();
  3318. }
  3319. /* ---------------------------------------- 设置 ---------------------------------------- */
  3320. function SetCookie(name, value, days) {
  3321. let Days = 180;
  3322. if (days) {
  3323. Days = days;
  3324. }
  3325. let exp = new Date();
  3326. exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000);
  3327. let str = JSON.stringify(value);
  3328. document.cookie = name + "=" + str + ";expires=" + exp.toGMTString() + ';path=\/';
  3329. }
  3330. function GetCookie(name) {
  3331. let arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
  3332. if (arr = document.cookie.match(reg)) {
  3333. return unescape(arr[2]);
  3334. }
  3335. else {
  3336. return null;
  3337. }
  3338. }
  3339. function ShowInstallMessage() {
  3340. $('#pp-bg').remove();
  3341. let bg = $('<div id="pp-bg"></div>').css({
  3342. 'width': document.documentElement.clientWidth + 'px', 'height': document.documentElement.clientHeight + 'px', 'position': 'fixed',
  3343. 'z-index': 999999, 'background-color': 'rgba(0,0,0,0.8)',
  3344. 'left': '0px', 'top': '0px'
  3345. });
  3346. $('body').append(bg);
  3347.  
  3348. bg.get(0).innerHTML = '<img id="pps-close"src="https://pp-1252089172.cos.ap-chengdu.myqcloud.com/Close.png"style="position: absolute; right: 35px; top: 20px; width: 32px; height: 32px; cursor: pointer;"><div style="position: absolute;width: 40%;left: 30%;top: 25%;font-size: 25px; text-align: center; color: white;">' + Texts[g_language].install_title + g_version + '</div><br>' + Texts[g_language].install_body;
  3349. $('#pps-close').click(function () {
  3350. $('#pp-bg').remove();
  3351. });
  3352. }
  3353. function ShowUpgradeMessage() {
  3354. $('#pp-bg').remove();
  3355. let bg = $('<div id="pp-bg"></div>').css({
  3356. 'width': document.documentElement.clientWidth + 'px', 'height': document.documentElement.clientHeight + 'px', 'position': 'fixed',
  3357. 'z-index': 999999, 'background-color': 'rgba(0,0,0,0.8)',
  3358. 'left': '0px', 'top': '0px'
  3359. });
  3360. $('body').append(bg);
  3361.  
  3362. let body = '新功能:<br><dd>1.支持搜索页小说排序,可在设置中关闭。</dd>优化:<br><dd>1.减少jQuery引发的错误。<br>2.设置在小窗口下不会超出屏幕。</dd>修复:<br><dd>1.更新提示不会随窗口大小改变。</dd>';
  3363. bg.get(0).innerHTML = '<img id="pps-close"src="https://pp-1252089172.cos.ap-chengdu.myqcloud.com/Close.png"style="position: absolute; right: 35px; top: 20px; width: 32px; height: 32px; cursor: pointer;"><div style="position: absolute;width: 40%;left: 30%;top: 25%;font-size: 25px; text-align: center; color: white;">'
  3364. + Texts[g_language].install_title + g_version
  3365. + '</div><br><div style="position:absolute;left:50%;top:30%;font-size:20px;color:white;transform:translate(-50%,0);">'
  3366. + body + '</div>';
  3367. $('#pps-close').click(function () {
  3368. $('#pp-bg').remove();
  3369. });
  3370. }
  3371. function FillNewSetting(st) {
  3372. // 升级可能会有部分新加字段在cookie里读不到
  3373. let changed = false;
  3374. $.each(g_defaultSettings, function(k, v) {
  3375. if (st[k] == undefined) {
  3376. st[k] = g_defaultSettings[k];
  3377. changed = true;
  3378. }
  3379. });
  3380. return {
  3381. 'st': st,
  3382. 'change': changed
  3383. };
  3384. }
  3385. function GetSettings() {
  3386. let settings;
  3387.  
  3388. let cookie = GetCookie('PixivPreview');
  3389. if (cookie == null || cookie == 'null') {
  3390. // 新安装
  3391. settings = g_defaultSettings;
  3392. SetCookie('PixivPreview', settings);
  3393. ShowInstallMessage();
  3394. } else {
  3395. settings = JSON.parse(cookie);
  3396. let mp = FillNewSetting(settings);
  3397. if (mp.change) {
  3398. settings = mp.st;
  3399. SetCookie('PixivPreview', settings);
  3400. }
  3401. // 升级
  3402. if (settings.version != g_version) {
  3403. ShowUpgradeMessage();
  3404. settings.version = g_version;
  3405. SetCookie('PixivPreview', settings);
  3406. }
  3407. }
  3408.  
  3409. return settings;
  3410. }
  3411. function ShowSetting() {
  3412. let screenWidth = document.documentElement.clientWidth;
  3413. let screenHeight = document.documentElement.clientHeight;
  3414.  
  3415. $('#pp-bg').remove();
  3416. let bg = $('<div id="pp-bg"></div>').css({
  3417. 'width': screenWidth + 'px', 'height': screenHeight + 'px', 'position': 'fixed',
  3418. 'z-index': 999999, 'background-color': 'rgba(0,0,0,0.8)',
  3419. 'left': '0px', 'top': '0px'
  3420. });
  3421. $('body').append(bg);
  3422.  
  3423. let settings = GetSettings();
  3424.  
  3425. let settingHTML = '<div style="color: white; font-size: 1em;">' +
  3426. '<img id="pps-close" src="https://pp-1252089172.cos.ap-chengdu.myqcloud.com/Close.png" style="position: absolute; right: 35px; top: 20px; width: 32px; height: 32px; cursor: pointer;">' +
  3427. '<div style="position: absolute; height: 60%; left: 50%; top: 10%; overflow-y: auto; transform: translate(-50%, 0%);">' +
  3428. '<ul id="pps-ul" style="list-style: none; padding: 0; margin: 0;"></ul></div>' +
  3429. '<div style="margin-top: 10px;position: absolute;bottom: 10%;width: 100%;text-align: center;">' +
  3430. '<button id="pps-save" style="font-size: 25px; border-radius: 12px; height: 48px; min-width: 138px; max-width: 150px; background-color: green; color: white; margin: 0 32px 0 32px; cursor: pointer; border: none;">' + Texts[g_language].setting_save + '</button>' +
  3431. '<button id="pps-reset" style="font-size: 25px; border-radius: 12px; height: 48px; min-width: 138px; max-width: 150px; background-color: darkred; color: white; margin: 0 32px 0 32px; cursor: pointer; border: none;">' + Texts[g_language].setting_reset + '</button>' +
  3432. '</div></div>';
  3433.  
  3434. bg.get(0).innerHTML = settingHTML;
  3435. let ul = $('#pps-ul');
  3436. function getImageAction(id) {
  3437. return '<img id="' + id + '" src="https://pp-1252089172.cos.ap-chengdu.myqcloud.com/On.png" style="height: 32px; cursor: pointer; margin-right: 20px; vertical-align: middle;"/>';
  3438. }
  3439. function getInputAction(id) {
  3440. return '<input id="' + id + '" style="font-size: 24px; padding: 0; margin-right: 16px; border-width: 0px; width: 64px; text-align: center;"/>'
  3441. }
  3442. function getSelectAction(id) {
  3443. return '<select id="' + id + '" style="font-size: 20px; margin-right: 10px;"></select>';
  3444. }
  3445. function addItem(action, text) {
  3446. ul.append('<li style="font-size: 25px; padding-bottom: 5px;">' + action + text + '</li>');
  3447. }
  3448. ul.empty();
  3449. addItem(getSelectAction('pps-lang'), Texts[g_language].setting_language);
  3450. addItem(getImageAction('pps-preview'), Texts[g_language].setting_preview);
  3451. addItem(getImageAction('pps-sort'), Texts[g_language].setting_sort);
  3452. addItem(getImageAction('pps-anime'), Texts[g_language].setting_anime);
  3453. addItem(getImageAction('pps-original'), Texts[g_language].setting_origin);
  3454. addItem(getInputAction('pps-previewDelay'), Texts[g_language].setting_previewDelay);
  3455. addItem('', '&nbsp');
  3456. addItem(getInputAction('pps-maxPage'), Texts[g_language].setting_maxPage);
  3457. addItem(getInputAction('pps-hideLess'), Texts[g_language].setting_hideWork);
  3458. addItem(getImageAction('pps-hideBookmarked'), Texts[g_language].setting_hideFav);
  3459. addItem(getImageAction('pps-hideFollowed'), Texts[g_language].setting_hideFollowed + '&nbsp<button id="pps-clearFollowingCache" style="cursor:pointer;background-color:gold;border-radius:12px;border:none;font-size:20px;padding:3px 10px;" title="' + Texts[g_language].setting_clearFollowingCacheHelp + '">' + Texts[g_language].setting_clearFollowingCache + '</button>');
  3460. addItem(getImageAction('pps-newTab'), Texts[g_language].setting_blank);
  3461. addItem(getImageAction('pps-pageKey'), Texts[g_language].setting_turnPage);
  3462. addItem(getImageAction('pps-fullSizeThumb'), Texts[g_language].sort_fullSizeThumb);
  3463. addItem('', '&nbsp');
  3464. addItem(getImageAction('pps-novelSort'), Texts[g_language].setting_novelSort);
  3465. addItem(getInputAction('pps-novelMaxPage'), Texts[g_language].setting_novelMaxPage);
  3466.  
  3467. let imgOn = 'https://pp-1252089172.cos.ap-chengdu.myqcloud.com/On.png';
  3468. let imgOff = 'https://pp-1252089172.cos.ap-chengdu.myqcloud.com/Off.png'
  3469. $('#pps-preview').attr('src', settings.enablePreview ? imgOn : imgOff).addClass(settings.enablePreview ? 'on' : 'off').css('cursor: pointer');
  3470. $('#pps-sort').attr('src', settings.enableSort ? imgOn : imgOff).addClass(settings.enableSort ? 'on' : 'off').css('cursor: pointer');
  3471. $('#pps-anime').attr('src', settings.enableAnimeDownload ? imgOn : imgOff).addClass(settings.enableAnimeDownload ? 'on' : 'off').css('cursor: pointer');
  3472. $('#pps-original').attr('src', settings.original ? imgOn : imgOff).addClass(settings.original ? 'on' : 'off').css('cursor: pointer');
  3473. $('#pps-previewDelay').val(settings.previewDelay);
  3474. $('#pps-maxPage').val(settings.pageCount);
  3475. $('#pps-hideLess').val(settings.favFilter);
  3476. $('#pps-hideBookmarked').attr('src', settings.hideFavorite ? imgOn : imgOff).addClass(settings.hideFavorite ? 'on' : 'off').css('cursor: pointer');
  3477. $('#pps-hideFollowed').attr('src', settings.hideFollowed ? imgOn : imgOff).addClass(settings.hideFollowed ? 'on' : 'off').css('cursor: pointer');
  3478. $('#pps-newTab').attr('src', settings.linkBlank ? imgOn : imgOff).addClass(settings.linkBlank ? 'on' : 'off').css('cursor: pointer');
  3479. $('#pps-pageKey').attr('src', settings.pageByKey ? imgOn : imgOff).addClass(settings.pageByKey ? 'on' : 'off').css('cursor: pointer');
  3480. $('#pps-fullSizeThumb').attr('src', settings.fullSizeThumb ? imgOn : imgOff).addClass(settings.fullSizeThumb ? 'on' : 'off').css('cursor: pointer');
  3481. $('#pps-novelSort').attr('src', settings.enableNovelSort ? imgOn : imgOff).addClass(settings.enableNovelSort ? 'on' : 'off').css('cursor: pointer');
  3482. $('#pps-novelMaxPage').val(settings.novelPageCount);
  3483.  
  3484. $('#pps-lang')
  3485. .append('<option value="-1">Auto</option>')
  3486. .append('<option value="' + Lang.zh_CN + '">简体中文</option>')
  3487. .append('<option value="' + Lang.en_US + '">English</option>')
  3488. .append('<option value="' + Lang.ru_RU + '">Русский язык</option>')
  3489. .val(g_settings.lang == undefined ? Lang.auto : g_settings.lang);
  3490.  
  3491. $('#pps-ul').find('img').click(function () {
  3492. let _this = $(this);
  3493.  
  3494. if (_this.hasClass('on')) {
  3495. _this.attr('src', imgOff).removeClass('on').addClass('off');
  3496. } else {
  3497. _this.attr('src', imgOn).removeClass('off').addClass('on');
  3498. }
  3499. });
  3500. $('#pps-clearFollowingCache').click(function () {
  3501. let user_id = dataLayer[0].user_id;
  3502. SetCookie('followingOfUid-' + user_id, null, -1);
  3503. alert(Texts[g_language].setting_followingCacheCleared);
  3504. });
  3505.  
  3506. $('#pps-save').click(function () {
  3507. if ($('#pps-maxPage').val() === '') {
  3508. $('#pps-maxPage').val(g_defaultSettings.pageCount);
  3509. }
  3510. if ($('#pps-hideLess').val() == '') {
  3511. $('#pps-hideLess').val(g_defaultSettings.favFilter);
  3512. }
  3513.  
  3514. let settings = {
  3515. 'lang': $('#pps-lang').val(),
  3516. 'enablePreview': $('#pps-preview').hasClass('on') ? 1 : 0,
  3517. 'enableSort': $('#pps-sort').hasClass('on') ? 1 : 0,
  3518. 'enableAnimeDownload': $('#pps-anime').hasClass('on') ? 1 : 0,
  3519. 'original': $('#pps-original').hasClass('on') ? 1 : 0,
  3520. 'previewDelay': parseInt($('#pps-previewDelay').val()),
  3521. 'pageCount': parseInt($('#pps-maxPage').val()),
  3522. 'favFilter': parseInt($('#pps-hideLess').val()),
  3523. 'hideFavorite': $('#pps-hideBookmarked').hasClass('on') ? 1 : 0,
  3524. 'hideFollowed': $('#pps-hideFollowed').hasClass('on') ? 1 : 0,
  3525. 'linkBlank': $('#pps-newTab').hasClass('on') ? 1 : 0,
  3526. 'pageByKey': $('#pps-pageKey').hasClass('on') ? 1 : 0,
  3527. 'fullSizeThumb': $('#pps-fullSizeThumb').hasClass('on') ? 1 : 0,
  3528. 'enableNovelSort': $('#pps-novelSort').hasClass('on') ? 1 : 0,
  3529. 'novelPageCount': parseInt($('#pps-novelMaxPage').val()),
  3530. 'version': g_version,
  3531. }
  3532.  
  3533. SetCookie('PixivPreview', settings);
  3534.  
  3535. location.href = location.href;
  3536. });
  3537.  
  3538. $('#pps-reset').click(function () {
  3539. let comfirmText = Texts[g_language].setting_resetHint;
  3540. if (confirm(comfirmText)) {
  3541. SetCookie('PixivPreview', null);
  3542. location.href = location.href;
  3543. }
  3544. });
  3545.  
  3546. $('#pps-close').click(function () {
  3547. $('#pp-bg').remove();
  3548. });
  3549. }
  3550. function SetTargetBlank(returnMap) {
  3551. if (g_settings.linkBlank) {
  3552. let target = [];
  3553. $.each(returnMap.controlElements, function (i, e) {
  3554. if (e.tagName == 'A') {
  3555. target.push(e);
  3556. }
  3557. });
  3558.  
  3559. $.each($(returnMap.controlElements).find('a'), function (i, e) {
  3560. target.push(e);
  3561. });
  3562.  
  3563. $.each(target, function (i, e) {
  3564. $(e).attr({ 'target': '_blank', 'rel': 'external' });
  3565. // 主页这里用的是js监听跳转,特殊处理
  3566. if (g_pageType == PageType.Home || g_pageType == PageType.Member || g_pageType == PageType.Artwork) {
  3567. e.addEventListener("click", function (ev) {
  3568. ev.stopPropagation();
  3569. })
  3570. }
  3571. });
  3572. }
  3573. }
  3574. /* --------------------------------------- 主函数 --------------------------------------- */
  3575. let loadInterval = null;
  3576. let itv = null;
  3577. function Load() {
  3578. // 匹配当前页面
  3579. for (let i = 0; i < PageType.PageTypeCount; i++) {
  3580. if (Pages[i].CheckUrl(location.href)) {
  3581. g_pageType = i;
  3582. break;
  3583. }
  3584. }
  3585. if (g_pageType >= 0) {
  3586. DoLog(LogLevel.Info, 'Current page is ' + Pages[g_pageType].PageTypeString);
  3587. } else {
  3588. DoLog(LogLevel.Info, 'Unsupported page.');
  3589. clearInterval(loadInterval);
  3590. return;
  3591. }
  3592.  
  3593. // 设置按钮
  3594. let toolBar = Pages[g_pageType].GetToolBar();
  3595. if (toolBar) {
  3596. DoLog(LogLevel.Elements, toolBar);
  3597. clearInterval(loadInterval);
  3598. } else {
  3599. DoLog(LogLevel.Warning, 'Get toolbar failed.');
  3600. return;
  3601. }
  3602.  
  3603. window.onresize = function () {
  3604. if ($('#pp-bg').length > 0) {
  3605. let screenWidth = document.documentElement.clientWidth;
  3606. let screenHeight = document.documentElement.clientHeight;
  3607. $('#pp-bg').css({ 'width': screenWidth + 'px', 'height': screenHeight + 'px' });
  3608. }
  3609. };
  3610.  
  3611. if ($('#pp-settings').length == 0) {
  3612. toolBar.appendChild(toolBar.firstChild.cloneNode(true));
  3613. toolBar.lastChild.outerHTML = '<button id="pp-settings" style="background-color: rgb(0, 0, 0);margin-top: 5px;opacity: 0.8;cursor: pointer;border: none;padding: 12px;border-radius: 24px;width: 48px;height: 48px;"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve" style="fill: white;"><metadata> Svg Vector Icons : http://www.sfont.cn </metadata><g><path d="M377.5,500c0,67.7,54.8,122.5,122.5,122.5S622.5,567.7,622.5,500S567.7,377.5,500,377.5S377.5,432.3,377.5,500z"></path><path d="M990,546v-94.8L856.2,411c-8.9-35.8-23-69.4-41.6-100.2L879,186L812,119L689,185.2c-30.8-18.5-64.4-32.6-100.2-41.5L545.9,10h-94.8L411,143.8c-35.8,8.9-69.5,23-100.2,41.5L186.1,121l-67,66.9L185.2,311c-18.6,30.8-32.6,64.4-41.5,100.3L10,454v94.8L143.8,589c8.9,35.8,23,69.4,41.6,100.2L121,814l67,67l123-66.2c30.8,18.6,64.5,32.6,100.3,41.5L454,990h94.8L589,856.2c35.8-8.9,69.4-23,100.2-41.6L814,879l67-67l-66.2-123.1c18.6-30.7,32.6-64.4,41.5-100.2L990,546z M500,745c-135.3,0-245-109.7-245-245c0-135.3,109.7-245,245-245s245,109.7,245,245C745,635.3,635.3,745,500,745z"></path></g></svg></button>';
  3614. $(toolBar.lastChild).css('margin-top', '10px');
  3615. $(toolBar.lastChild).css('opacity', '0.8');
  3616. $(toolBar.lastChild).click(function () {
  3617. ShowSetting();
  3618. });
  3619. }
  3620.  
  3621. // 读取设置
  3622. g_settings = GetSettings();
  3623.  
  3624. // 自动检测语言
  3625. g_language = g_settings.lang == undefined ? Lang.auto : g_settings.lang;
  3626. if (g_language == Lang.auto) {
  3627. let lang = $('html').attr('lang');
  3628. if (lang && lang.indexOf('zh') != -1) {
  3629. // 简体中文和繁体中文都用简体中文
  3630. g_language = Lang.zh_CN;
  3631. } else {
  3632. // 其他的统一用英语,其他语言也不知道谷歌翻译得对不对
  3633. g_language = Lang.en_US;
  3634. }
  3635. }
  3636.  
  3637. // g_csrfToken
  3638. if (g_pageType == PageType.Search || g_pageType == PageType.NovelSearch) {
  3639. $.get(location.href, function (data) {
  3640. let matched = data.match(/token":"([a-z0-9]{32})/);
  3641. if (matched.length > 0) {
  3642. g_csrfToken = matched[1];
  3643. DoLog(LogLevel.Info, 'Got g_csrfToken: ' + g_csrfToken);
  3644. } else {
  3645. DoLog(LogLevel.Error, 'Can not get g_csrfToken, so you can not add works to bookmark when sorting has enabled.');
  3646. }
  3647. });
  3648. }
  3649.  
  3650. // 排序、预览
  3651. itv = setInterval(function () {
  3652. let returnMap = Pages[g_pageType].ProcessPageElements();
  3653. if (!returnMap.loadingComplete) {
  3654. return;
  3655. }
  3656.  
  3657. DoLog(LogLevel.Info, 'Process page comlete, sorting and prevewing begin.');
  3658. DoLog(LogLevel.Elements, returnMap);
  3659.  
  3660. clearInterval(itv);
  3661.  
  3662. SetTargetBlank(returnMap);
  3663.  
  3664. try {
  3665. if (g_pageType == PageType.Artwork) {
  3666. Pages[g_pageType].Work();
  3667. if (g_settings.enablePreview) {
  3668. PixivPreview();
  3669. }
  3670. }
  3671. else if (g_pageType == PageType.Search) {
  3672. if (g_settings.enableSort) {
  3673. g_sortComplete = false;
  3674. PixivSK(function () {
  3675. g_sortComplete = true;
  3676. if (g_settings.enablePreview) {
  3677. PixivPreview();
  3678. }
  3679. });
  3680. } else if (g_settings.enablePreview) {
  3681. PixivPreview();
  3682. }
  3683. } else if (g_pageType == PageType.NovelSearch) {
  3684. if (g_settings.enableNovelSort) {
  3685. PixivNS();
  3686. }
  3687. } else if (g_settings.enablePreview) {
  3688. PixivPreview();
  3689. }
  3690. }
  3691. catch (e) {
  3692. DoLog(LogLevel.Error, 'Unknown error: ' + e);
  3693. }
  3694. }, 500);
  3695. }
  3696. function startLoad() {
  3697. loadInterval = setInterval(Load, 1000);
  3698. setInterval(function () {
  3699. if (location.href != initialUrl) {
  3700. // 排序中点击搜索tag,可能导致进行中的排序出现混乱,加取消太麻烦,直接走刷新
  3701. if (!g_sortComplete) {
  3702. location.href = location.href;
  3703. return;
  3704. }
  3705. // fix 主页预览图出现后点击图片,进到详情页,预览图不消失的问题
  3706. if ($('.pp-main').length > 0) {
  3707. $('.pp-main').remove();
  3708. }
  3709. initialUrl = location.href;
  3710. clearInterval(loadInterval);
  3711. clearInterval(itv);
  3712. clearInterval(autoLoadInterval);
  3713. autoLoadInterval = null;
  3714. g_pageType = -1;
  3715. loadInterval = setInterval(Load, 300);
  3716. }
  3717. }, 1000);
  3718. }
  3719. let inChecking = false;
  3720. let jqItv = setInterval(function () {
  3721. if (inChecking) {
  3722. return;
  3723. }
  3724. inChecking = true;
  3725. checkJQuery().then(function (isLoad) {
  3726. if (isLoad) {
  3727. clearInterval(jqItv);
  3728. startLoad();
  3729. }
  3730. inChecking = false;
  3731. });
  3732. }, 1000);