Pixiv Previewer

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

当前为 2021-09-05 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Pixiv Previewer
  3. // @namespace https://github.com/Ocrosoft/PixivPreviewer
  4. // @version 3.5.4
  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 none
  12. // @compatible Chrome
  13. // ==/UserScript==
  14.  
  15. // 测试 JQuery,如果不支持就插入
  16. //let $ = function () { };
  17. try {
  18. $();
  19. } catch (e) {
  20. let script = document.createElement('script');
  21. script.src = 'https://cdn.bootcdn.net/ajax/libs/jquery/2.1.4/jquery.min.js';
  22. document.head.appendChild(script);
  23. }
  24.  
  25. let Lang = {
  26. // 自动选择
  27. auto: -1,
  28. // 中文-中国大陆
  29. zh_CN: 0,
  30. // 英语-美国
  31. en_US: 1,
  32. // 俄语-俄罗斯
  33. ru_RU: 2,
  34. };
  35. let Texts = {};
  36. Texts[Lang.zh_CN] = {
  37. // 安装或更新后弹出的提示
  38. install_title: '欢迎使用 PixivPreviewer v',
  39. install_body: '<div style="position: absolute;width: 40%;left: 30%;top: 30%;font-size: 20px; color: white;"><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>',
  40. // 设置项
  41. setting_language: '语言',
  42. setting_preview: '预览',
  43. setting_sort: '排序(仅搜索页生效)',
  44. setting_anime: '动图下载(动图预览及详情页生效)',
  45. setting_origin: '预览时优先显示原图(慢)',
  46. setting_previewDelay: '延迟显示预览图(毫秒)',
  47. setting_maxPage: '每次排序时统计的最大页数',
  48. setting_hideWork: '隐藏收藏数少于设定值的作品',
  49. setting_hideFav: '排序时隐藏已收藏的作品',
  50. setting_hideFollowed: '排序时隐藏已关注画师作品',
  51. setting_clearFollowingCache: '清除缓存',
  52. setting_clearFollowingCacheHelp: '关注画师信息会在本地保存一天,如果希望立即更新,请点击清除缓存',
  53. setting_followingCacheCleared: '已清除缓存,请刷新页面。',
  54. setting_blank: '使用新标签页打开作品详情页',
  55. setting_turnPage: '使用键盘←→进行翻页(排序后的搜索页)',
  56. setting_save: '保存设置',
  57. setting_reset: '重置脚本',
  58. setting_resetHint: '这会删除所有设置,相当于重新安装脚本,确定要重置吗?',
  59. // 搜索时过滤值太高
  60. sort_noWork: '没有可以显示的作品',
  61. sort_getWorks: '正在获取第%1/%2页作品',
  62. sort_getBookmarkCount: '获取收藏数:%1/%2',
  63. sort_getPublicFollowing: '获取公开关注画师',
  64. sort_getPrivateFollowing: '获取私有关注画师',
  65. sort_filtering: '过滤%1收藏量低于%2的作品',
  66. sort_filteringHideFavorite: '已收藏和',
  67. sort_fullSizeThumb: '排序后展示全尺寸图片',
  68. };
  69. // translate by google
  70. Texts[Lang.en_US] = {
  71. install_title: 'Welcome to PixivPreviewer v',
  72. install_body: '<div style="position: absolute;width: 40%;left: 30%;top: 30%;font-size: 20px; color: white;"><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>',
  73. setting_language: 'Language',
  74. setting_preview: 'Preview',
  75. setting_sort: 'Sorting (Search page)',
  76. setting_anime: 'Animation download (Preview and Artwork page)',
  77. setting_origin: 'Display original image when preview (slow)',
  78. setting_previewDelay: 'Delay of display preview image(Million seconds)',
  79. setting_maxPage: 'Maximum number of pages counted per sort',
  80. setting_hideWork: 'Hide works with bookmark count less than set value',
  81. setting_hideFav: 'Hide favorites when sorting',
  82. setting_hideFollowed: 'Hide artworks of followed artists when sorting',
  83. setting_clearFollowingCache: 'Cache',
  84. 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',
  85. setting_followingCacheCleared: 'Success, please refresh the page.',
  86. setting_blank: 'Open works\' details page in new tab',
  87. setting_turnPage: 'Use ← → to turn pages (Search page)',
  88. setting_save: 'Save',
  89. setting_reset: 'Reset',
  90. setting_resetHint: 'This will delete all settings and set it to default. Are you sure?',
  91. sort_noWork: 'No works to display',
  92. sort_getWorks: 'Getting artworks of page: %1 of %2',
  93. sort_getBookmarkCount: 'Getting bookmark count of artworks:%1 of %2',
  94. sort_getPublicFollowing: 'Getting public following list',
  95. sort_getPrivateFollowing: 'Getting private following list',
  96. sort_filtering: 'Filtering%1works with bookmark count less than %2',
  97. sort_filteringHideFavorite: ' favorited works and ',
  98. sort_fullSizeThumb: 'Display not cropped images.',
  99. };
  100. // RU: перевод от vanja-san
  101. Texts[Lang.ru_RU] = {
  102. install_title: 'Добро пожаловать в PixivPreviewer v',
  103. install_body: '<div style="position: absolute;width: 40%;left: 30%;top: 30%;font-size: 20px; color: white;"><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>',
  104. setting_language: 'Язык',
  105. setting_preview: 'Предпросмотр',
  106. setting_sort: 'Сортировка (Страница поиска)',
  107. setting_anime: 'Анимация скачивания (Страницы предпросмотра и Artwork)',
  108. setting_origin: 'При предпросмотре, показывать изображения с оригинальным качеством (медленно)',
  109. setting_previewDelay: 'Задержка отображения предпросмотра изображения (Миллион секунд)',
  110. setting_maxPage: 'Максимальное количество страниц, подсчитанных за сортировку',
  111. setting_hideWork: 'Скрыть работы с количеством закладок меньше установленного значения',
  112. setting_hideFav: 'При сортировке, скрыть избранное',
  113. setting_hideFollowed: 'При сортировке, скрыть работы художников на которых подписаны',
  114. setting_clearFollowingCache: 'Кэш',
  115. setting_clearFollowingCacheHelp: 'Следующая информация о художниках будет сохранена локально в течение одного дня, если вы хотите обновить её немедленно, нажмите на эту кнопку, чтобы очистить кэш',
  116. setting_followingCacheCleared: 'Готово, обновите страницу.',
  117. setting_blank: 'Открывать страницу с описанием работы на новой вкладке',
  118. setting_turnPage: 'Использовать ← → для перелистывания страниц (Страница поиска)',
  119. setting_save: 'Сохранить',
  120. setting_reset: 'Сбросить',
  121. setting_resetHint: 'Это удалит все настройки и установит их по умолчанию. Продолжить?',
  122. sort_noWork: 'Нет работ для отображения',
  123. sort_getWorks: 'Получение иллюстраций страницы: %1 из %2',
  124. sort_getBookmarkCount: 'Получение количества закладок artworks:%1 из %2',
  125. sort_getPublicFollowing: 'Получение публичного списка подписок',
  126. sort_getPrivateFollowing: 'Получение приватного списка подписок',
  127. sort_filtering: 'Фильтрация %1 работ с количеством закладок меньше чем %2',
  128. sort_filteringHideFavorite: ' избранные работы и ',
  129. sort_fullSizeThumb: 'Показать неотредактированное изображение',
  130. };
  131.  
  132. let LogLevel = {
  133. None: 0,
  134. Error: 1,
  135. Warning: 2,
  136. Info: 3,
  137. Elements: 4,
  138. };
  139. function DoLog(level, msgOrElement) {
  140. if (level <= g_logLevel) {
  141. let prefix = '%c';
  142. let param = '';
  143.  
  144. if (level == LogLevel.Error) {
  145. prefix += '[Error]';
  146. param = 'color:#ff0000';
  147. } else if (level == LogLevel.Warning) {
  148. prefix += '[Warning]';
  149. param = 'color:#ffa500';
  150. } else if (level == LogLevel.Info) {
  151. prefix += '[Info]';
  152. param = 'color:#000000';
  153. } else if (level == LogLevel.Elements) {
  154. prefix += 'Elements';
  155. param = 'color:#000000';
  156. }
  157.  
  158. if (level != LogLevel.Elements) {
  159. console.log(prefix + msgOrElement, param);
  160. } else {
  161. console.log(msgOrElement);
  162. }
  163.  
  164. if (++g_logCount > 512) {
  165. //console.clear();
  166. g_logCount = 0;
  167. }
  168. }
  169. }
  170.  
  171. // 语言
  172. let g_language = Lang.zh_CN;
  173. // 版本号,第三位不需要跟脚本的版本号对上,第三位更新只有需要弹更新提示的时候才需要更新这里
  174. let g_version = '3.2.0';
  175. // 添加收藏需要这个
  176. let g_csrfToken = '';
  177. // 打的日志数量,超过一定数值清空控制台
  178. let g_logCount = 0;
  179. // 当前页面类型
  180. let g_pageType = -1;
  181. // 图片详情页的链接,使用时替换 #id#
  182. let g_artworkUrl = '/artworks/#id#';
  183. // 获取图片链接的链接
  184. let g_getArtworkUrl = '/ajax/illust/#id#/pages';
  185. // 获取动图下载链接的链接
  186. let g_getUgoiraUrl = '/ajax/illust/#id#/ugoira_meta';
  187. // 鼠标位置
  188. let g_mousePos = { x: 0, y: 0 };
  189. // 加载中图片
  190. let g_loadingImage = 'https://pp-1252089172.cos.ap-chengdu.myqcloud.com/loading.gif';
  191. // 页面打开时的 url
  192. let initialUrl = location.href;
  193. // 默认设置,仅用于首次脚本初始化
  194. let g_defaultSettings = {
  195. 'lang': -1,
  196. 'enablePreview': 1,
  197. 'enableSort': 1,
  198. 'enableAnimeDownload': 1,
  199. 'original': 0,
  200. 'previewDelay': 200,
  201. 'pageCount': 2,
  202. 'favFilter': 0,
  203. 'hideFavorite': 0,
  204. 'hideFollowed': 0,
  205. 'linkBlank': 1,
  206. 'pageByKey': 0,
  207. 'fullSizeThumb': 0,
  208. 'logLevel': 1,
  209. 'version': g_version,
  210. };
  211. // 设置
  212. let g_settings;
  213. // 日志等级
  214. let g_logLevel = LogLevel.Error;
  215. // 排序时同时请求收藏量的 Request 数量,没必要太多,并不会加快速度
  216. let g_maxXhr = 64;
  217. // 排序是否完成(如果排序时页面出现了非刷新切换,强制刷新)
  218. let g_sortComplete = true;
  219.  
  220. // 页面相关的一些预定义,包括处理页面元素等
  221. let PageType = {
  222. // 搜索
  223. Search: 0,
  224. // 关注的新作品
  225. BookMarkNew: 1,
  226. // 发现
  227. Discovery: 2,
  228. // 用户主页
  229. Member: 3,
  230. // 首页
  231. Home: 4,
  232. // 排行榜
  233. Ranking: 5,
  234. // 大家的新作品
  235. NewIllust: 6,
  236. // R18
  237. R18: 7,
  238. // 自己的收藏页
  239. BookMark: 8,
  240. // 动态
  241. Stacc: 9,
  242. // 作品详情页(处理动图预览及下载)
  243. Artwork: 10,
  244.  
  245. // 总数
  246. PageTypeCount: 11,
  247. };
  248. let Pages = {};
  249. /* Pages 必须实现的函数
  250. * PageTypeString: string,字符串形式的 PageType
  251. * bool CheckUrl: function(string url),用于检查一个 url 是否是当前页面的目标 url
  252. * ReturnMap ProcessPageElements: function(),处理页面(寻找图片元素、添加属性等),返回 ReturnMap
  253. * ReturnMap GetProcessedPageElements: function(), 返回上一次 ProcessPageElements 的返回值(如果没有上次调用则调用一次)
  254. * Object GetToolBar: function(), 返回工具栏元素(右下角那个,用来放设置按钮)
  255. * HasAutoLoad: bool,表示这个页面是否有自动加载功能
  256. */
  257. let ReturnMapSample = {
  258. // 页面是否加载完成,false 意味着后面的成员无效
  259. loadingComplete: false,
  260. // 控制元素,每个图片的鼠标响应元素
  261. controlElements: [],
  262. // 可有可无,如果为 true,强制重新刷新预览功能
  263. forceUpdate: false,
  264. };
  265. let ControlElementsAttributesSample = {
  266. // 图片信息,内容如下:
  267. // [必需] 图片 id
  268. illustId: 0,
  269. // [必需] 图片类型(0:普通图片,2:动图)
  270. illustType: 0,
  271. // [必需] 页数
  272. pageCount: 1,
  273. // [可选] 标题
  274. title: '',
  275. // [可选] 作者 id
  276. userId: 0,
  277. // [可选] 作者昵称
  278. userName: '',
  279. // [可选] 收藏数
  280. bookmarkCount: 0,
  281. };
  282.  
  283. function findToolbarCommon() {
  284. // 目前第三级div,除了目标div外,子元素都是div
  285. return $('#root>div>div>ul').get(0);
  286. }
  287. function findToolbarOld() {
  288. return $('._toolmenu').get(0);
  289. }
  290. function convertThumbUrlToSmall(thumbUrl) {
  291. // 目前发现有以下两种格式的缩略图
  292. // https://i.pximg.net/c/128x128/custom-thumb/img/2021/01/31/20/35/53/87426718_p0_custom1200.jpg
  293. // https://i.pximg.net/c/128x128/img-master/img/2021/01/31/10/57/06/87425082_p0_square1200.jpg
  294. let replace1 = 'c/540x540_70/img-master';
  295. //let replace1 = 'img-master'; // 这个是转到regular的,比small的大多了,会很慢
  296. let replace2 = '_master';
  297. return thumbUrl.replace(/c\/.*\/custom-thumb/, replace1).replace('_custom', replace2)
  298. .replace(/c\/.*\/img-master/, replace1).replace('_square', replace2);
  299. }
  300.  
  301. Pages[PageType.Search] = {
  302. PageTypeString: 'SearchPage',
  303. CheckUrl: function (url) {
  304. // 没有 /artworks 的页面不支持
  305. return /^https?:\/\/www.pixiv.net\/tags\/.*\/(artworks|illustrations|manga)/.test(url) ||
  306. /^https?:\/\/www.pixiv.net\/en\/tags\/.*\/(artworks|illustrations|manga)/.test(url);
  307. },
  308. ProcessPageElements: function () {
  309. let returnMap = {
  310. loadingComplete: false,
  311. controlElements: [],
  312. };
  313.  
  314. let sections = $('section');
  315. DoLog(LogLevel.Info, 'Page has ' + sections.length + ' <section>.');
  316. DoLog(LogLevel.Elements, sections);
  317. // 先对 section 进行评分
  318. let sectionIndex = -1;
  319. let bestScore = -99;
  320. sections.each(function (i, e) {
  321. let section = $(e);
  322. let score = 0;
  323. if (section.find('ul').length > 0) {
  324. let childrenCount = section.children().length;
  325. if (childrenCount != 2) {
  326. DoLog(LogLevel.Warning, '<ul> was found in this <section>, but it has ' + childrenCount + ' children!');
  327. score--;
  328. }
  329. let ul = section.find('ul');
  330. if (ul.length > 1) {
  331. DoLog(LogLevel.Warning, 'This section has more than one <ul>?');
  332. score--;
  333. }
  334. if ($(ul.parent().get(0)).css('display') == 'none' || $(ul.get(0)).css('display') == 'none') {
  335. DoLog(LogLevel.Info, '<ul> or it\'s parentNode is not visible now, continue waiting.');
  336. sectionIndex = -1;
  337. bestScore = 999;
  338. return false;
  339. }
  340. if ($(ul.get(0)).next().length === 0) {
  341. DoLog(LogLevel.Info, 'Page selector not exists, continue waiting.');
  342. sectionIndex = -1;
  343. bestScore = 999;
  344. return false;
  345. }
  346. let lis = ul.find('li');
  347. if (lis.length === 0) {
  348. DoLog(LogLevel.Info, 'This <ul> has 0 children, will be skipped.');
  349. return false;
  350. }
  351. if ($(lis.get(0)).find('figure').length > 0) {
  352. DoLog(LogLevel.Warning, '<figure> was found in the first <li>, continue waiting.');
  353. sectionIndex = -1;
  354. bestScore = 999;
  355. return false;
  356. }
  357. if (lis.length > 4) {
  358. score += 5;
  359. }
  360. // 正确的会在后面
  361. if (score >= bestScore) {
  362. bestScore = score;
  363. sectionIndex = i;
  364. }
  365. } else {
  366. DoLog(LogLevel.Info, 'This section(' + i + ' is not has <ul>, will be skipped.');
  367. }
  368. });
  369.  
  370. if (sectionIndex == -1) {
  371. if (bestScore < 100) {
  372. DoLog(LogLevel.Error, 'No suitable <section>!');
  373. }
  374. return returnMap;
  375. }
  376.  
  377. let lis = $(sections[sectionIndex]).find('ul').find('li');
  378. lis.each(function (i, e) {
  379. let li = $(e);
  380.  
  381. // 只填充必须的几个,其他的目前用不着
  382. let ctlAttrs = {
  383. illustId: 0,
  384. illustType: 0,
  385. pageCount: 1,
  386. };
  387.  
  388. let img = $(li.find('img').get(0));
  389. let imageLink = img.parent().parent();
  390. let additionDiv = img.parent().prev();
  391. let animationSvg = img.parent().find('svg');
  392. let pageCountSpan = additionDiv.find('span');
  393.  
  394. if (img == null || imageLink == null) {
  395. DoLog(LogLevel.Warning, 'Can not found img or imageLink, skip this.');
  396. return;
  397. }
  398.  
  399. let link = imageLink.attr('href');
  400. if (link == null) {
  401. DoLog(LogLevel.Warning, 'Invalid href, skip this.');
  402. return;
  403. }
  404. let linkMatched = link.match(/artworks\/(\d+)/);
  405. let illustId = '';
  406. if (linkMatched) {
  407. ctlAttrs.illustId = linkMatched[1];
  408. } else {
  409. DoLog(LogLevel.Error, 'Get illustId failed, skip this list item!');
  410. return;
  411. }
  412. if (animationSvg.length > 0) {
  413. ctlAttrs.illustType = 2;
  414. }
  415. if (pageCountSpan.length > 0) {
  416. ctlAttrs.pageCount = parseInt(pageCountSpan.text());
  417. }
  418.  
  419. // 添加 attr
  420. let control = li.children('div:first').children('div:first');
  421. control.attr({
  422. 'illustId': ctlAttrs.illustId,
  423. 'illustType': ctlAttrs.illustType,
  424. 'pageCount': ctlAttrs.pageCount
  425. });
  426.  
  427. control.addClass('pp-control');
  428. });
  429. returnMap.controlElements = $('.pp-control');
  430. this.private.pageSelector = $($(sections[sectionIndex]).find('ul').get(0)).next().get(0);
  431. returnMap.loadingComplete = true;
  432. this.private.imageListConrainer = $(sections[sectionIndex]).find('ul').get(0);
  433.  
  434. DoLog(LogLevel.Info, 'Process page elements complete.');
  435. DoLog(LogLevel.Elements, returnMap);
  436.  
  437. this.private.returnMap = returnMap;
  438. return returnMap;
  439. },
  440. GetProcessedPageElements: function () {
  441. if (this.private.returnMap == null) {
  442. return this.ProcessPageElements();
  443. }
  444. return this.private.returnMap;
  445. },
  446. GetToolBar: function () {
  447. return findToolbarCommon();
  448. },
  449. // 搜索页有 lazyload,不开排序的情况下,最后几张图片可能会无法预览。这里把它当做自动加载处理
  450. HasAutoLoad: true,
  451. GetImageListContainer: function () {
  452. return this.private.imageListConrainer;
  453. },
  454. GetFirstImageElement: function () {
  455. return $(this.private.imageListConrainer).find('li').get(0);
  456. },
  457. GetPageSelector: function () {
  458. return this.private.pageSelector;
  459. },
  460. private: {
  461. imageListContainer: null,
  462. pageSelector: null,
  463. returnMap: null,
  464. },
  465. };
  466. Pages[PageType.BookMarkNew] = {
  467. PageTypeString: 'BookMarkNewPage',
  468. CheckUrl: function (url) {
  469. return /^https:\/\/www.pixiv.net\/bookmark_new_illust.php.*/.test(url) ||
  470. /^https:\/\/www.pixiv.net\/bookmark_new_illust_r18.php.*/.test(url);
  471. },
  472. ProcessPageElements: function () {
  473. let returnMap = {
  474. loadingComplete: false,
  475. controlElements: [],
  476. };
  477.  
  478. let containerDiv = $('#js-mount-point-latest-following').children('div:first');
  479. if (containerDiv.length > 0) {
  480. DoLog(LogLevel.Info, 'Found container div.');
  481. DoLog(LogLevel.Elements, containerDiv);
  482. } else {
  483. DoLog(LogLevel.Error, 'Can not found container div.');
  484. return returnMap;
  485. }
  486.  
  487. containerDiv.children().each(function (i, e) {
  488. let _this = $(e);
  489.  
  490. let figure = _this.find('figure');
  491. if (figure.length === 0) {
  492. DoLog(LogLevel.Warning, 'Can not found <fingure>, skip this element.');
  493. return;
  494. }
  495.  
  496. let link = figure.children('div:first').children('a:first');
  497. if (link.length === 0) {
  498. DoLog(LogLevel.Warning, 'Can not found <a>, skip this element.');
  499. return;
  500. }
  501.  
  502. let ctlAttrs = {
  503. illustId: 0,
  504. illustType: 0,
  505. pageCount: 1,
  506. };
  507.  
  508. let href = link.attr('href');
  509. if (href == null || href === '') {
  510. DoLog(LogLevel.Warning, 'No href found, skip.');
  511. return;
  512. } else {
  513. let matched = href.match(/artworks\/(\d+)/);
  514. if (matched) {
  515. ctlAttrs.illustId = matched[1];
  516. } else {
  517. DoLog(LogLevel.Warning, 'Can not found illust id, skip.');
  518. return;
  519. }
  520. }
  521.  
  522. if (link.children().length > 1) {
  523. if (link.children('div:first').find('span').length > 0) {
  524. let span = link.children('div:first').children('span:first');
  525. if (span.length === 0) {
  526. DoLog(LogLevel.Warning, 'Can not found <span>, skip this element.');
  527. return;
  528. }
  529. ctlAttrs.pageCount = span.text();
  530. } else {
  531. ctlAttrs.illustType = 2;
  532. }
  533. }
  534.  
  535. let control = figure.children('div:first');
  536. control.attr({
  537. 'illustId': ctlAttrs.illustId,
  538. 'illustType': ctlAttrs.illustType,
  539. 'pageCount': ctlAttrs.pageCount
  540. });
  541.  
  542. returnMap.controlElements.push(control.get(0));
  543. });
  544.  
  545. DoLog(LogLevel.Info, 'Process page elements complete.');
  546. DoLog(LogLevel.Elements, returnMap);
  547.  
  548. returnMap.loadingComplete = true;
  549. this.private.returnMap = returnMap;
  550. return returnMap;
  551. },
  552. GetProcessedPageElements: function () {
  553. if (this.private.returnMap == null) {
  554. return this.ProcessPageElements();
  555. }
  556. return this.private.returnMap;
  557. },
  558. GetToolBar: function () {
  559. return findToolbarOld();
  560. },
  561. HasAutoLoad: false,
  562. private: {
  563. returnMap: null,
  564. },
  565. };
  566. Pages[PageType.Discovery] = {
  567. PageTypeString: 'DiscoveryPage',
  568. CheckUrl: function (url) {
  569. return /^https?:\/\/www.pixiv.net\/discovery.*/.test(url);
  570. },
  571. ProcessPageElements: function () {
  572. let returnMap = {
  573. loadingComplete: false,
  574. controlElements: [],
  575. };
  576.  
  577. let containerDiv = $('.gtm-illust-recommend-zone');
  578. if (containerDiv.length > 0) {
  579. DoLog(LogLevel.Info, 'Found container div.');
  580. DoLog(LogLevel.Elements, containerDiv);
  581. } else {
  582. DoLog(LogLevel.Error, 'Can not found container div.');
  583. return returnMap;
  584. }
  585.  
  586. containerDiv.children().each(function (i, e) {
  587. let _this = $(e);
  588.  
  589. let figure = _this.find('figure');
  590. if (figure.length === 0) {
  591. DoLog(LogLevel.Warning, 'Can not found <fingure>, skip this element.');
  592. return;
  593. }
  594.  
  595. let link = figure.children('div:first').children('a:first');
  596. if (link.length === 0) {
  597. DoLog(LogLevel.Warning, 'Can not found <a>, skip this element.');
  598. return;
  599. }
  600.  
  601. let ctlAttrs = {
  602. illustId: 0,
  603. illustType: 0,
  604. pageCount: 1,
  605. };
  606.  
  607. let href = link.attr('href');
  608. if (href == null || href === '') {
  609. DoLog(LogLevel.Warning, 'No href found, skip.');
  610. return;
  611. } else {
  612. let matched = href.match(/artworks\/(\d+)/);
  613. if (matched) {
  614. ctlAttrs.illustId = matched[1];
  615. } else {
  616. DoLog(LogLevel.Warning, 'Can not found illust id, skip.');
  617. return;
  618. }
  619. }
  620.  
  621. if (link.children().length > 1) {
  622. if (link.children('div:first').find('span').length > 0) {
  623. let span = link.children('div:first').children('span:first');
  624. if (span.length === 0) {
  625. DoLog(LogLevel.Warning, 'Can not found <span>, skip this element.');
  626. return;
  627. }
  628. ctlAttrs.pageCount = span.text();
  629. } else if (link.children('div:last').css('background-image').indexOf('.svg') != -1) {
  630. ctlAttrs.illustType = 2;
  631. }
  632. }
  633.  
  634. let control = figure.children('div:first');
  635. control.attr({
  636. 'illustId': ctlAttrs.illustId,
  637. 'illustType': ctlAttrs.illustType,
  638. 'pageCount': ctlAttrs.pageCount
  639. });
  640.  
  641. returnMap.controlElements.push(control.get(0));
  642. });
  643.  
  644. DoLog(LogLevel.Info, 'Process page elements complete.');
  645. DoLog(LogLevel.Elements, returnMap);
  646.  
  647. returnMap.loadingComplete = true;
  648. this.private.returnMap = returnMap;
  649. return returnMap;
  650. },
  651. GetProcessedPageElements: function () {
  652. if (this.private.returnMap == null) {
  653. return this.ProcessPageElements();
  654. }
  655. return this.private.returnMap;
  656. },
  657. GetToolBar: function () {
  658. return findToolbarOld();
  659. },
  660. HasAutoLoad: true,
  661. private: {
  662. returnMap: null,
  663. },
  664. };
  665. Pages[PageType.Member] = {
  666. PageTypeString: 'MemberPage/MemberIllustPage/MemberBookMark',
  667. CheckUrl: function (url) {
  668. return /^https?:\/\/www.pixiv.net\/users\/\d+/.test(url);
  669. },
  670. ProcessPageElements: function () {
  671. let returnMap = {
  672. loadingComplete: false,
  673. controlElements: [],
  674. };
  675.  
  676. let sections = $('section');
  677. DoLog(LogLevel.Info, 'Page has ' + sections.length + ' <section>.');
  678. DoLog(LogLevel.Elements, sections);
  679.  
  680. let lis = sections.find('ul').find('li');
  681. lis.each(function (i, e) {
  682. let li = $(e);
  683.  
  684. // 只填充必须的几个,其他的目前用不着
  685. let ctlAttrs = {
  686. illustId: 0,
  687. illustType: 0,
  688. pageCount: 1,
  689. };
  690.  
  691. let img = $(li.find('img').get(0));
  692. let imageLink = img.parent().parent();
  693. let additionDiv = img.parent().prev();
  694. let animationSvg = img.parent().find('svg');
  695. let pageCountSpan = additionDiv.find('span');
  696.  
  697. if (!img || !imageLink) {
  698. DoLog(LogLevel.Warning, 'Can not found img or imageLink, skip this.');
  699. return;
  700. }
  701.  
  702. let link = imageLink.attr('href');
  703. if (link == null) {
  704. DoLog(LogLevel.Warning, 'Can not found illust id, skip this.');
  705. return;
  706. }
  707.  
  708. let linkMatched = link.match(/artworks\/(\d+)/);
  709. let illustId = '';
  710. if (linkMatched) {
  711. ctlAttrs.illustId = linkMatched[1];
  712. } else {
  713. DoLog(LogLevel.Error, 'Get illustId failed, skip this list item!');
  714. return;
  715. }
  716. if (animationSvg.length > 0) {
  717. ctlAttrs.illustType = 2;
  718. }
  719. if (pageCountSpan.length > 0) {
  720. ctlAttrs.pageCount = parseInt(pageCountSpan.text());
  721. }
  722.  
  723. // 添加 attr
  724. let control = li.children('div:first').children('div:first');
  725. if (control.length === 0) {
  726. control = li.children('div:last').children('div:first');
  727. }
  728. control.attr({
  729. 'illustId': ctlAttrs.illustId,
  730. 'illustType': ctlAttrs.illustType,
  731. 'pageCount': ctlAttrs.pageCount
  732. });
  733.  
  734. control.addClass('pp-control');
  735. });
  736. returnMap.controlElements = $('.pp-control');
  737. returnMap.loadingComplete = true;
  738.  
  739. DoLog(LogLevel.Info, 'Process page elements complete.');
  740. DoLog(LogLevel.Elements, returnMap);
  741.  
  742. this.private.returnMap = returnMap;
  743. return returnMap;
  744. },
  745. GetProcessedPageElements: function () {
  746. if (this.private.returnMap == null) {
  747. return this.ProcessPageElements();
  748. }
  749. return this.private.returnMap;
  750. },
  751. GetToolBar: function () {
  752. return findToolbarCommon();
  753. },
  754. // 跟搜索页一样的情况
  755. HasAutoLoad: true,
  756. private: {
  757. returnMap: null,
  758. },
  759. };
  760. Pages[PageType.Home] = {
  761. PageTypeString: 'HomePage',
  762. CheckUrl: function (url) {
  763. return /https?:\/\/www.pixiv.net\/?$/.test(url) ||
  764. /https?:\/\/www.pixiv.net\/en\/?$/.test(url);
  765. },
  766. ProcessPageElements: function () {
  767. let returnMap = {
  768. loadingComplete: false,
  769. controlElements: [],
  770. forceUpdate: false,
  771. };
  772.  
  773. let illust_div = $('div[type="illust"]');
  774.  
  775. DoLog(LogLevel.Info, 'This page has ' + illust_div.length + ' illust <div>.');
  776. if (illust_div.length < 1) {
  777. DoLog(LogLevel.Warning, 'Less than one <div>, continue waiting.');
  778. return returnMap;
  779. }
  780.  
  781. // 实际里面还套了一个 div,处理一下,方便一点
  782. let illust_div_c = [];
  783. illust_div.each(function(i, e) {
  784. illust_div_c.push($(e).children('div:first'));
  785. });
  786. illust_div = illust_div_c;
  787. $.each(illust_div, function (i, e) {
  788. let _this = $(e);
  789.  
  790. let a = _this.children('a:first');
  791. if (a.length == 0 || a.attr('href').indexOf('artworks') == -1) {
  792. DoLog(LogLevel.Warning, 'No href or an invalid href was found, skip this.');
  793. return;
  794. }
  795.  
  796. let ctlAttrs = {
  797. illustId: 0,
  798. illustType: 0,
  799. pageCount: 1,
  800. };
  801.  
  802. let illustId = a.attr('href').match(/\d+/);
  803. if (illustId == null) {
  804. DoLog(LogLevel.Warning, 'Can not found illust id of this image, skip.');
  805. return;
  806. } else {
  807. ctlAttrs.illustId = illustId[0];
  808. }
  809. let pageCount = a.children('div:first').find('span');
  810. if (pageCount.length > 0) {
  811. ctlAttrs.pageCount = parseInt($(pageCount.get(pageCount.length - 1)).text());
  812. }
  813. if ($(a.children('div').get(1)).find('svg').length > 0) {
  814. ctlAttrs.illustType = 2;
  815. }
  816.  
  817. let control = a;
  818. if (control.attr('illustId') != ctlAttrs.illustId) {
  819. returnMap.forceUpdate = true;
  820. }
  821. control.attr({
  822. 'illustId': ctlAttrs.illustId,
  823. 'illustType': ctlAttrs.illustType,
  824. 'pageCount': ctlAttrs.pageCount
  825. });
  826.  
  827. returnMap.controlElements.push(control.get(0));
  828. });
  829.  
  830. DoLog(LogLevel.Info, 'Process page elements complete.');
  831. DoLog(LogLevel.Elements, returnMap);
  832.  
  833. returnMap.loadingComplete = true;
  834. this.private.returnMap = returnMap;
  835. return returnMap;
  836. },
  837. GetProcessedPageElements: function () {
  838. if (this.private.returnMap == null) {
  839. return this.ProcessPageElements();
  840. }
  841. return this.private.returnMap;
  842. },
  843. GetToolBar: function () {
  844. return findToolbarCommon();
  845. },
  846. HasAutoLoad: true,
  847. private: {
  848. returnMap: null,
  849. },
  850. };
  851. Pages[PageType.Ranking] = {
  852. PageTypeString: 'RankingPage',
  853. CheckUrl: function (url) {
  854. return /^https?:\/\/www.pixiv.net\/ranking.php.*/.test(url);
  855. },
  856. ProcessPageElements: function () {
  857. let returnMap = {
  858. loadingComplete: false,
  859. controlElements: [],
  860. };
  861.  
  862. let works = $('._work');
  863.  
  864. DoLog(LogLevel.Info, 'Found .work, length: ' + works.length);
  865. DoLog(LogLevel.Elements, works);
  866.  
  867. works.each(function (i, e) {
  868. let _this = $(e);
  869.  
  870. let ctlAttrs = {
  871. illustId: 0,
  872. illustType: 0,
  873. pageCount: 1,
  874. };
  875.  
  876. let href = _this.attr('href');
  877.  
  878. if (href == null || href === '') {
  879. DoLog('Can not found illust id, skip this.');
  880. return;
  881. }
  882.  
  883. let matched = href.match(/artworks\/(\d+)/);
  884. if (matched) {
  885. ctlAttrs.illustId = matched[1];
  886. } else {
  887. DoLog('Can not found illust id, skip this.');
  888. return;
  889. }
  890.  
  891. if (_this.hasClass('multiple')) {
  892. ctlAttrs.pageCount = _this.find('.page-count').find('span').text();
  893. }
  894.  
  895. if (_this.hasClass('ugoku-illust')) {
  896. ctlAttrs.illustType = 2;
  897. }
  898.  
  899. // 添加 attr
  900. _this.attr({
  901. 'illustId': ctlAttrs.illustId,
  902. 'illustType': ctlAttrs.illustType,
  903. 'pageCount': ctlAttrs.pageCount
  904. });
  905.  
  906. returnMap.controlElements.push(e);
  907. });
  908.  
  909. returnMap.loadingComplete = true;
  910.  
  911. DoLog(LogLevel.Info, 'Process page elements complete.');
  912. DoLog(LogLevel.Elements, returnMap);
  913.  
  914. this.private.returnMap = returnMap;
  915. return returnMap;
  916. },
  917. GetProcessedPageElements: function () {
  918. if (this.private.returnMap == null) {
  919. return this.ProcessPageElements();
  920. }
  921. return this.private.returnMap;
  922. },
  923. GetToolBar: function () {
  924. return findToolbarOld();
  925. },
  926. HasAutoLoad: false,
  927. private: {
  928. returnMap: null,
  929. },
  930. };
  931. Pages[PageType.NewIllust] = {
  932. PageTypeString: 'NewIllustPage',
  933. CheckUrl: function (url) {
  934. return /^https?:\/\/www.pixiv.net\/new_illust.php.*/.test(url);
  935. },
  936. ProcessPageElements: function () {
  937. let returnMap = {
  938. loadingComplete: false,
  939. controlElements: [],
  940. };
  941.  
  942. let ul = $('#root').find('ul:first');
  943. if (ul.length === 0) {
  944. DoLog(LogLevel.Error, 'Can not found <ul>!');
  945. return returnMap;
  946. }
  947.  
  948. ul.find('li').each(function (i, e) {
  949. let _this = $(e);
  950.  
  951. let link = _this.find('a:first');
  952. let href = link.attr('href');
  953. if (href == null || href === '') {
  954. DoLog(LogLevel.Error, 'Can not found illust id, skip this.');
  955. return;
  956. }
  957.  
  958. let ctlAttrs = {
  959. illustId: 0,
  960. illustType: 0,
  961. pageCount: 1,
  962. };
  963.  
  964. let matched = href.match(/artworks\/(\d+)/);
  965. if (matched) {
  966. ctlAttrs.illustId = matched[1];
  967. } else {
  968. DoLog(LogLevel.Warning, 'Can not found illust id, skip this.');
  969. return;
  970. }
  971.  
  972. if (link.children().length > 1) {
  973. let span = link.find('svg').parent().parent().next();
  974. if (span.length > 0 && span.get(0).tagName == 'SPAN') {
  975. ctlAttrs.pageCount = span.text();
  976. } else if (link.find('svg').length > 0) {
  977. ctlAttrs.illustType = 2;
  978. }
  979. }
  980.  
  981. let control = _this.children('div:first').children('div:first');
  982. control.attr({
  983. 'illustId': ctlAttrs.illustId,
  984. 'illustType': ctlAttrs.illustType,
  985. 'pageCount': ctlAttrs.pageCount
  986. });
  987.  
  988. returnMap.controlElements.push(control.get(0));
  989. });
  990.  
  991. returnMap.loadingComplete = true;
  992.  
  993. DoLog(LogLevel.Info, 'Process page elements complete.');
  994. DoLog(LogLevel.Elements, returnMap);
  995.  
  996. this.private.returnMap = returnMap;
  997. return returnMap;
  998. },
  999. GetProcessedPageElements: function () {
  1000. if (this.private.returnMap == null) {
  1001. return this.ProcessPageElements();
  1002. }
  1003. return this.private.returnMap;
  1004. },
  1005. GetToolBar: function () {
  1006. return findToolbarCommon();
  1007. },
  1008. HasAutoLoad: true,
  1009. private: {
  1010. returnMap: null,
  1011. },
  1012. };
  1013. Pages[PageType.R18] = {
  1014. PageTypeString: 'R18Page',
  1015. CheckUrl: function (url) {
  1016. return /^https?:\/\/www.pixiv.net\/cate_r18.php.*/.test(url);
  1017. },
  1018. ProcessPageElements: function () {
  1019. //
  1020. },
  1021. GetToolBar: function () {
  1022. //
  1023. },
  1024. HasAutoLoad: false,
  1025. };
  1026. Pages[PageType.BookMark] = {
  1027. PageTypeString: 'BookMarkPage',
  1028. CheckUrl: function (url) {
  1029. return /^https:\/\/www.pixiv.net\/bookmark.php\/?$/.test(url);
  1030. },
  1031. ProcessPageElements: function () {
  1032. let returnMap = {
  1033. loadingComplete: false,
  1034. controlElements: [],
  1035. };
  1036.  
  1037. let images = $('.image-item');
  1038. DoLog(LogLevel.Info, 'Found images, length: ' + images.length);
  1039. DoLog(LogLevel.Elements, images);
  1040.  
  1041. images.each(function (i, e) {
  1042. let _this = $(e);
  1043.  
  1044. let work = _this.find('._work');
  1045. if (work.length === 0) {
  1046. DoLog(LogLevel.Warning, 'Can not found ._work, skip this.');
  1047. return;
  1048. }
  1049.  
  1050. let ctlAttrs = {
  1051. illustId: 0,
  1052. illustType: 0,
  1053. pageCount: 1,
  1054. };
  1055.  
  1056. let href = work.attr('href');
  1057. if (href == null || href === '') {
  1058. DoLog(LogLevel.Warning, 'Can not found illust id, skip this.');
  1059. return;
  1060. }
  1061.  
  1062. let matched = href.match(/artworks\/(\d+)/);
  1063. if (matched) {
  1064. ctlAttrs.illustId = matched[1];
  1065. } else {
  1066. DoLog(LogLevel.Warning, 'Can not found illust id, skip this.');
  1067. return;
  1068. }
  1069.  
  1070. if (work.hasClass('multiple')) {
  1071. ctlAttrs.pageCount = _this.find('.page-count').find('span').text();
  1072. }
  1073.  
  1074. if (work.hasClass('ugoku-illust')) {
  1075. ctlAttrs.illustType = 2;
  1076. }
  1077.  
  1078. // 添加 attr
  1079. let control = _this.children('a:first');
  1080. control.attr({
  1081. 'illustId': ctlAttrs.illustId,
  1082. 'illustType': ctlAttrs.illustType,
  1083. 'pageCount': ctlAttrs.pageCount
  1084. });
  1085.  
  1086. returnMap.controlElements.push(control.get(0));
  1087. });
  1088.  
  1089. returnMap.loadingComplete = true;
  1090.  
  1091. DoLog(LogLevel.Info, 'Process page elements complete.');
  1092. DoLog(LogLevel.Elements, returnMap);
  1093.  
  1094. this.private.returnMap = returnMap;
  1095. return returnMap;
  1096. },
  1097. GetProcessedPageElements: function () {
  1098. if (this.private.returnMap == null) {
  1099. return this.ProcessPageElements();
  1100. }
  1101. return this.private.returnMap;
  1102. },
  1103. GetToolBar: function () {
  1104. return findToolbarOld();
  1105. },
  1106. HasAutoLoad: false,
  1107. private: {
  1108. returnMap: null,
  1109. },
  1110. };
  1111. Pages[PageType.Stacc] = {
  1112. PageTypeString: 'StaccPage',
  1113. CheckUrl: function (url) {
  1114. return /^https:\/\/www.pixiv.net\/stacc.*/.test(url);
  1115. },
  1116. ProcessPageElements: function () {
  1117. let returnMap = {
  1118. loadingComplete: false,
  1119. controlElements: [],
  1120. };
  1121.  
  1122. let works = $('._work');
  1123.  
  1124. DoLog(LogLevel.Info, 'Found .work, length: ' + works.length);
  1125. DoLog(LogLevel.Elements, works);
  1126.  
  1127. works.each(function (i, e) {
  1128. let _this = $(e);
  1129.  
  1130. let ctlAttrs = {
  1131. illustId: 0,
  1132. illustType: 0,
  1133. pageCount: 1,
  1134. };
  1135.  
  1136. let href = _this.attr('href');
  1137.  
  1138. if (href == null || href === '') {
  1139. DoLog('Can not found illust id, skip this.');
  1140. return;
  1141. }
  1142.  
  1143. let matched = href.match(/illust_id=(\d+)/);
  1144. if (matched) {
  1145. ctlAttrs.illustId = matched[1];
  1146. } else {
  1147. DoLog('Can not found illust id, skip this.');
  1148. return;
  1149. }
  1150.  
  1151. if (_this.hasClass('multiple')) {
  1152. ctlAttrs.pageCount = _this.find('.page-count').find('span').text();
  1153. }
  1154.  
  1155. if (_this.hasClass('ugoku-illust')) {
  1156. ctlAttrs.illustType = 2;
  1157. }
  1158.  
  1159. // 添加 attr
  1160. _this.attr({
  1161. 'illustId': ctlAttrs.illustId,
  1162. 'illustType': ctlAttrs.illustType,
  1163. 'pageCount': ctlAttrs.pageCount
  1164. });
  1165.  
  1166. returnMap.controlElements.push(e);
  1167. });
  1168.  
  1169. returnMap.loadingComplete = true;
  1170.  
  1171. DoLog(LogLevel.Info, 'Process page elements complete.');
  1172. DoLog(LogLevel.Elements, returnMap);
  1173.  
  1174. this.private.returnMap = returnMap;
  1175. return returnMap;
  1176. },
  1177. GetProcessedPageElements: function () {
  1178. if (this.private.returnMap == null) {
  1179. return this.ProcessPageElements();
  1180. }
  1181. return this.private.returnMap;
  1182. },
  1183. GetToolBar: function () {
  1184. return findToolbarOld();
  1185. },
  1186. HasAutoLoad: true,
  1187. private: {
  1188. returnMap: null,
  1189. },
  1190. };
  1191. Pages[PageType.Artwork] = {
  1192. PageTypeString: 'ArtworkPage',
  1193. CheckUrl: function (url) {
  1194. return /^https:\/\/www.pixiv.net\/artworks\/.*/.test(url) ||
  1195. /^https:\/\/www.pixiv.net\/en\/artworks\/.*/.test(url);
  1196. },
  1197. ProcessPageElements: function () {
  1198. let returnMap = {
  1199. loadingComplete: false,
  1200. controlElements: [],
  1201. };
  1202.  
  1203. // 是动图
  1204. let canvas = $('main').find('figure').find('canvas');
  1205. if ($('main').find('figure').find('canvas').length > 0) {
  1206. this.private.needProcess = true;
  1207. canvas.addClass('pp-canvas');
  1208. }
  1209.  
  1210. if (location.href.indexOf('#preview') == -1) {
  1211. // 相关作品,container找不到说明还没加载
  1212. let containerDiv = $('.gtm-illust-recommend-zone');
  1213. if (containerDiv.length == 0) {
  1214. let asides = $('#root').find('aside');
  1215. $.each(asides, function(i, e) {
  1216. if ($(e).children('section').length == 1) {
  1217. containerDiv = $(e);
  1218. return false;
  1219. }
  1220. });
  1221. }
  1222. if (containerDiv.length > 0) {
  1223. DoLog(LogLevel.Info, 'Found container div.');
  1224. DoLog(LogLevel.Elements, containerDiv);
  1225.  
  1226. containerDiv.find('ul:first').children().each(function (i, e) {
  1227. let _this = $(e);
  1228.  
  1229. let img = _this.find('img');
  1230. if (img.length === 0) {
  1231. DoLog(LogLevel.Warning, 'Can not found <img>, skip this element.');
  1232. return;
  1233. }
  1234.  
  1235. let link = _this.find('a:first');
  1236. if (link.length === 0) {
  1237. DoLog(LogLevel.Warning, 'Can not found <a>, skip this element.');
  1238. return;
  1239. }
  1240.  
  1241. let ctlAttrs = {
  1242. illustId: 0,
  1243. illustType: 0,
  1244. pageCount: 1,
  1245. };
  1246.  
  1247. let href = link.attr('href');
  1248. if (href == null || href === '') {
  1249. DoLog(LogLevel.Warning, 'No href found, skip.');
  1250. return;
  1251. } else {
  1252. let matched = href.match(/artworks\/(\d+)/);
  1253. if (matched) {
  1254. ctlAttrs.illustId = matched[1];
  1255. } else {
  1256. DoLog(LogLevel.Warning, 'Can not found illust id, skip.');
  1257. return;
  1258. }
  1259. }
  1260.  
  1261. if (link.children().length > 1) {
  1262. if (link.children('div:first').find('span').length > 0) {
  1263. let span = link.children('div:first').find('span:first');
  1264. if (span.length === 0) {
  1265. DoLog(LogLevel.Warning, 'Can not found <span>, skip this element.');
  1266. return;
  1267. }
  1268. ctlAttrs.pageCount = span.next().text();
  1269. } else if (link.children('div:last').find('svg').length > 0) {
  1270. ctlAttrs.illustType = 2;
  1271. }
  1272. }
  1273.  
  1274. let control = link.parent();
  1275. control.attr({
  1276. 'illustId': ctlAttrs.illustId,
  1277. 'illustType': ctlAttrs.illustType,
  1278. 'pageCount': ctlAttrs.pageCount
  1279. });
  1280.  
  1281. returnMap.controlElements.push(control.get(0));
  1282. });
  1283. }
  1284.  
  1285. DoLog(LogLevel.Info, 'Process page elements complete.');
  1286. DoLog(LogLevel.Elements, returnMap);
  1287. }
  1288.  
  1289. returnMap.loadingComplete = true;
  1290. this.private.returnMap = returnMap;
  1291. return returnMap;
  1292. },
  1293. GetProcessedPageElements: function () {
  1294. if (this.private.returnMap == null) {
  1295. return this.ProcessPageElements();
  1296. }
  1297. return this.private.returnMap;
  1298. },
  1299. GetToolBar: function () {
  1300. return findToolbarCommon();
  1301. },
  1302. HasAutoLoad: true,
  1303. Work: function () {
  1304. function AddDownloadButton(button, offsetToOffsetTop) {
  1305. if (!g_settings.enableAnimeDownload) {
  1306. return;
  1307. }
  1308.  
  1309. let cloneButton = button.clone().css({ 'bottom': '50px', 'padding': 0, 'width': '48px', 'height': '48px', 'opacity': '0.4', 'cursor': 'pointer' });
  1310. 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>';
  1311.  
  1312. function MoveButton() {
  1313. function getOffset(e) {
  1314. if (e.offsetParent) {
  1315. let offset = getOffset(e.offsetParent);
  1316. return {
  1317. offsetTop: e.offsetTop + offset.offsetTop,
  1318. offsetLeft: e.offsetLeft + offset.offsetLeft,
  1319. };
  1320. } else {
  1321. return {
  1322. offsetTop: e.offsetTop,
  1323. offsetLeft: e.offsetLeft,
  1324. };
  1325. }
  1326. }
  1327.  
  1328. /*let offset = getOffset(button.get(0));
  1329. DoLog(LogLevel.Info, 'offset of download button: ' + offset.offsetTop + ', ' + offset.offsetLeft);
  1330. DoLog(LogLevel.Elements, offset);
  1331. cloneButton.css({ 'position': 'absolute' }).show();*/
  1332. }
  1333.  
  1334. MoveButton();
  1335. $(window).on('resize', MoveButton);
  1336. button.after(cloneButton);
  1337.  
  1338. cloneButton.mouseover(function () {
  1339. $(this).css('opacity', '0.2');
  1340. }).mouseleave(function () {
  1341. $(this).css('opacity', '0.4');
  1342. }).click(function () {
  1343. let illustId = '';
  1344.  
  1345. let matched = location.href.match(/artworks\/(\d+)/);
  1346. if (matched) {
  1347. illustId = matched[1];
  1348. DoLog(LogLevel.Info, 'IllustId=' + illustId);
  1349. } else {
  1350. DoLog(LogLevel.Error, 'Can not found illust id!');
  1351. return;
  1352. }
  1353.  
  1354. $.ajax(g_getUgoiraUrl.replace('#id#', illustId), {
  1355. method: 'GET',
  1356. success: function (json) {
  1357. DoLog(LogLevel.Elements, json);
  1358.  
  1359. if (json.error == true) {
  1360. DoLog(LogLevel.Error, 'Server response an error: ' + json.message);
  1361. return;
  1362. }
  1363.  
  1364. // 因为浏览器会拦截不同域的 open 操作,绕一下
  1365. let newWindow = window.open('_blank');
  1366. newWindow.location = json.body.originalSrc;
  1367. },
  1368. error: function () {
  1369. DoLog(LogLevel.Error, 'Request zip file failed!');
  1370. }
  1371. });
  1372. });
  1373. }
  1374.  
  1375. if (this.private.needProcess) {
  1376. let canvas = $('.pp-canvas');
  1377.  
  1378. // 预览模式,需要调成全屏,并且添加下载按钮到全屏播放的 div 里
  1379. if (location.href.indexOf('#preview') != -1) {
  1380. canvas.click();
  1381.  
  1382. $('#root').remove();
  1383.  
  1384. let callbackInterval = setInterval(function () {
  1385. let div = $('div[role="presentation"]');
  1386. if (div.length < 1) {
  1387. return;
  1388. }
  1389.  
  1390. DoLog(LogLevel.Info, 'found <div>, continue to next step.');
  1391.  
  1392. clearInterval(callbackInterval);
  1393.  
  1394. let presentationCanvas = div.find('canvas');
  1395. if (presentationCanvas.length < 1) {
  1396. DoLog(LogLevel.Error, 'Can not found canvas in the presentation div.');
  1397. return;
  1398. }
  1399.  
  1400. let width = 0, height = 0;
  1401. let tWidth = presentationCanvas.attr('width');
  1402. let tHeight = presentationCanvas.attr('height');
  1403. if (tWidth && tHeight) {
  1404. width = parseInt(tWidth);
  1405. height = parseInt(tHeight);
  1406. } else {
  1407. tWidth = presentationCanvas.css('width');
  1408. tHeight = presentationCanvas.css('height');
  1409. width = parseInt(tWidth);
  1410. height = parseInt(this);
  1411. }
  1412.  
  1413. let parent = presentationCanvas.parent();
  1414. for (let i = 0; i < 3; i++) {
  1415. parent.get(0).className = '';
  1416. parent = parent.parent();
  1417. }
  1418. presentationCanvas.css({ 'width': width + 'px', 'height': height + 'px', 'cursor': 'default' }).addClass('pp-presentationCanvas');
  1419. let divForStopClick = $('<div class="pp-disableClick"></div>').css({
  1420. 'width': width + 'px', 'height': height + 'px',
  1421. 'opacity': 0,
  1422. 'position': 'absolute', 'top': '0px', 'left': '0px', 'z-index': 99999,
  1423. });
  1424. div.append(divForStopClick);
  1425. div.append(presentationCanvas.next().css('z-index', 99999));
  1426. presentationCanvas.next().remove();
  1427. // 防止预览图消失
  1428. $('html').addClass('pp-main');
  1429.  
  1430. // 调整 canvas 大小的函数
  1431. window.ResizeCanvas = function (newWidth, newHeight) {
  1432. DoLog(LogLevel.Info, 'Resize canvas: ' + newWidth + 'x' + newHeight);
  1433. $('.pp-disableClick').css({ 'width': newWidth, 'height': newHeight });
  1434. $('.pp-presentationCanvas').css({ 'width': newWidth, 'height': newHeight });
  1435. };
  1436. window.GetCanvasSize = function () {
  1437. return {
  1438. width: width,
  1439. height: height,
  1440. };
  1441. }
  1442.  
  1443. // 添加下载按钮
  1444. AddDownloadButton(divForStopClick.next(), 0);
  1445.  
  1446. window.parent.PreviewCallback(width, height);
  1447. }, 500);
  1448. }
  1449. // 普通模式,只需要添加下载按钮到内嵌模式的 div 里
  1450. else {
  1451. let div = $('div[role="presentation"]:last');
  1452. let button = div.find('button');
  1453.  
  1454. let headerRealHeight = parseInt($('header').css('height')) +
  1455. parseInt($('header').css('padding-top')) + parseInt($('header').css('padding-bottom')) +
  1456. parseInt($('header').css('margin-top')) + parseInt($('header').css('margin-bottom')) +
  1457. parseInt($('header').css('border-bottom-width')) + parseInt($('header').css('border-top-width'));
  1458.  
  1459. AddDownloadButton(button, headerRealHeight);
  1460. }
  1461. }
  1462. },
  1463. private: {
  1464. needProcess: false,
  1465. returnMap: null,
  1466. },
  1467. };
  1468.  
  1469. function CheckUrlTest() {
  1470. let urls = [
  1471. 'http://www.pixiv.net',
  1472. 'http://www.pixiv.net',
  1473. 'https://www.pixiv.net',
  1474. 'https://www.pixiv.net/',
  1475. 'https://www.pixiv.net/?lang=en',
  1476. 'https://www.pixiv.net/search.php?s_mode=s_tag&word=miku',
  1477. 'https://www.pixiv.net/search.php?word=VOCALOID&s_mode=s_tag_full',
  1478. 'https://www.pixiv.net/discovery',
  1479. 'https://www.pixiv.net/discovery?x=1',
  1480. 'https://www.pixiv.net/member.php?id=3207350',
  1481. 'https://www.pixiv.net/member_illust.php?id=3207350&type=illust',
  1482. 'https://www.pixiv.net/bookmark.php?id=3207350&rest=show',
  1483. 'https://www.pixiv.net/ranking.php?mode=daily&content=ugoira',
  1484. 'https://www.pixiv.net/ranking.php?mode=daily',
  1485. 'https://www.pixiv.net/new_illust.php',
  1486. 'https://www.pixiv.net/new_illust.php?x=1',
  1487. 'https://www.pixiv.net/cate_r18.php',
  1488. 'https://www.pixiv.net/cate_r18.php?x=1',
  1489. 'https://www.pixiv.net/bookmark.php',
  1490. 'https://www.pixiv.net/bookmark.php?x=1',
  1491. 'https://www.pixiv.net/stacc?mode=unify',
  1492. 'https://www.pixiv.net/artworks/77996773',
  1493. 'https://www.pixiv.net/artworks/77996773#preview',
  1494. ];
  1495.  
  1496. for (let j = 0; j < urls.length; j++) {
  1497. for (let i = 0; i < PageType.PageTypeCount; i++) {
  1498. if (Pages[i].CheckUrl(urls[j])) {
  1499. console.log(urls[j]);
  1500. console.log('[' + j + '] is ' + Pages[i].PageTypeString);
  1501. }
  1502. }
  1503. }
  1504. }
  1505.  
  1506. /* ---------------------------------------- 预览 ---------------------------------------- */
  1507. let autoLoadInterval = null;
  1508. function PixivPreview() {
  1509. // 最终需要显示的预览图ID,用于避免鼠标滑过多张图片时,最终显示的图片错误
  1510. let previewTargetIllustId = '';
  1511.  
  1512. // 开启预览功能
  1513. function ActivePreview() {
  1514. let returnMap = Pages[g_pageType].GetProcessedPageElements();
  1515. if (!returnMap.loadingComplete) {
  1516. DoLog(LogLevel.Error, 'Page not load, should not call Preview!');
  1517. return;
  1518. }
  1519.  
  1520. // 鼠标进入
  1521. $(returnMap.controlElements).mouseenter(function (e) {
  1522. // 按住 Ctrl键 不显示预览图
  1523. if (e.ctrlKey) {
  1524. return;
  1525. }
  1526.  
  1527. let startTime = new Date().getTime();
  1528. let delay = parseInt(g_settings.previewDelay == null ? g_defaultSettings.previewDelay : g_settings.previewDelay);
  1529.  
  1530. let _this = $(this);
  1531. let illustId = _this.attr('illustId');
  1532. let illustType = _this.attr('illustType');
  1533. let pageCount = _this.attr('pageCount');
  1534.  
  1535. if (illustId == null) {
  1536. DoLog(LogLevel.Error, 'Can not found illustId in this element\'s attrbutes.');
  1537. return;
  1538. }
  1539. if (illustType == null) {
  1540. DoLog(LogLevel.Error, 'Can not found illustType in this element\'s attrbutes.');
  1541. return;
  1542. }
  1543. if (pageCount == null) {
  1544. DoLog(LogLevel.Error, 'Can not found pageCount in this element\'s attrbutes.');
  1545. return;
  1546. }
  1547. previewTargetIllustId = illustId;
  1548.  
  1549. // 鼠标位置
  1550. g_mousePos = { x: e.pageX, y: e.pageY };
  1551. // 预览 Div
  1552. let previewDiv = $(document.createElement('div')).addClass('pp-main').attr('illustId', illustId)
  1553. .css({
  1554. 'position': 'absolute', 'z-index': '999999', 'left': g_mousePos.x + 'px', 'top': g_mousePos.y + 'px',
  1555. 'border-style': 'solid', 'border-color': '#6495ed', 'border-width': '2px', 'border-radius': '20px',
  1556. 'width': '48px', 'height': '48px',
  1557. 'background-image': 'url(https://pp-1252089172.cos.ap-chengdu.myqcloud.com/transparent.png)',
  1558. 'display': 'none'
  1559. });
  1560. // 添加到 body
  1561. $('.pp-main').remove();
  1562. $('body').append(previewDiv);
  1563.  
  1564. let waitTime = delay - (new Date().getTime() - startTime);
  1565. if (waitTime > 0) {
  1566. setTimeout(function() {
  1567. previewDiv.show();
  1568. }, waitTime);
  1569. } else {
  1570. previewDiv.show();
  1571. }
  1572.  
  1573. // 加载中图片
  1574. let loadingImg = $(new Image()).addClass('pp-loading').attr('src', g_loadingImage).css({
  1575. 'position': 'absolute', 'border-radius': '20px',
  1576. });
  1577. previewDiv.append(loadingImg);
  1578.  
  1579. // 要显示的预览图节点
  1580. let loadImg = $(new Image()).addClass('pp-image').css({ 'height': '0px', 'width': '0px', 'display': 'none', 'border-radius': '20px' });
  1581. previewDiv.append(loadImg);
  1582.  
  1583. // 原图(笑脸)图标
  1584. let originIcon = $(new Image()).addClass('pp-original').attr('src', 'https://source.pixiv.net/www/images/pixivcomic-favorite.png')
  1585. .css({ 'position': 'absolute', 'bottom': '5px', 'right': '5px', 'display': 'none' });
  1586. previewDiv.append(originIcon);
  1587.  
  1588. // 点击图标新网页打开原图
  1589. originIcon.click(function () {
  1590. window.open($(previewDiv).children('img')[1].src);
  1591. });
  1592.  
  1593. // 右上角张数标记
  1594. 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>';
  1595. let pageCountDiv = $(pageCountHTML)
  1596. .css({ 'position': 'absolute', 'top': '0px', 'display': 'none', 'right': '0px', 'display': 'none' });
  1597. previewDiv.append(pageCountDiv);
  1598.  
  1599. $('.pp-main').mouseleave(function (e) {
  1600. $(this).remove();
  1601. });
  1602.  
  1603. let url = '';
  1604. if (illustType == 2) {
  1605. // 动图
  1606. let screenWidth = document.documentElement.clientWidth;
  1607. let screenHeight = document.documentElement.clientHeight;
  1608. 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" />';
  1609. previewDiv.append(loadingImg);
  1610. } else {
  1611. url = g_getArtworkUrl.replace('#id#', illustId);
  1612.  
  1613. // 获取图片链接
  1614. $.ajax(url, {
  1615. method: 'GET',
  1616. success: function (json) {
  1617. DoLog(LogLevel.Info, 'Got artwork urls:');
  1618. DoLog(LogLevel.Elements, json);
  1619.  
  1620. if (json.error === true) {
  1621. DoLog(LogLevel.Error, 'Server responsed an error: ' + json.message);
  1622. return;
  1623. }
  1624.  
  1625. // 已经不需要显示这个预览图了,直接丢弃
  1626. if (illustId != previewTargetIllustId) {
  1627. DoLog(LogLevel.Info, 'Drop this preview request.');
  1628. return;
  1629. }
  1630.  
  1631. let regular = [];
  1632. let original = [];
  1633. for (let i = 0; i < json.body.length; i++) {
  1634. regular.push(json.body[i].urls.regular);
  1635. original.push(json.body[i].urls.original);
  1636. }
  1637.  
  1638. DoLog(LogLevel.Info, 'Process urls complete.');
  1639. DoLog(LogLevel.Elements, regular);
  1640. DoLog(LogLevel.Elements, original);
  1641.  
  1642. ViewImages(regular, 0, original, g_settings.original, illustId);
  1643. },
  1644. error: function (data) {
  1645. DoLog(LogLevel.Error, 'Request image urls failed!');
  1646. if (data) {
  1647. DoLog(LogLevel.Elements, data);
  1648. }
  1649. }
  1650. });
  1651. }
  1652. });
  1653.  
  1654. // 鼠标移出图片
  1655. $(returnMap.controlElements).mouseleave(function (e) {
  1656. let _this = $(this);
  1657. let illustId = _this.attr('illustId');
  1658. let illustType = _this.attr('illustType');
  1659. let pageCount = _this.attr('pageCount');
  1660.  
  1661. let moveToElement = $(e.relatedTarget);
  1662. let isMoveToPreviewElement = false;
  1663. // 鼠标移动到预览图上
  1664. while (true) {
  1665. if (moveToElement.hasClass('pp-main') && moveToElement.attr('illustId') == illustId) {
  1666. isMoveToPreviewElement = true;
  1667. }
  1668.  
  1669. if (moveToElement.parent().length < 1) {
  1670. break;
  1671. }
  1672.  
  1673. moveToElement = moveToElement.parent();
  1674. }
  1675. if (!isMoveToPreviewElement) {
  1676. // 非预览图上
  1677. $('.pp-main').remove();
  1678. }
  1679. });
  1680.  
  1681. // 鼠标移动,调整位置
  1682. $(returnMap.controlElements).mousemove(function (e) {
  1683. // Ctrl 和 中键 都可以禁止预览图移动,这样就可以单手操作了
  1684. if (e.ctrlKey || e.buttons & 4) {
  1685. return;
  1686. }
  1687. let screenWidth = document.documentElement.clientWidth;
  1688. let screenHeight = document.documentElement.clientHeight;
  1689. g_mousePos.x = e.pageX; g_mousePos.y = e.pageY;
  1690.  
  1691. AdjustDivPosition();
  1692. });
  1693.  
  1694. // 这个页面有自动加载
  1695. if (Pages[g_pageType].HasAutoLoad && autoLoadInterval == null) {
  1696. autoLoadInterval = setInterval(ProcessAutoLoad, 1000);
  1697. DoLog(LogLevel.Info, 'Auto load interval set.');
  1698. }
  1699.  
  1700. // 插一段回调函数
  1701. window.PreviewCallback = PreviewCallback;
  1702. DoLog(LogLevel.Info, 'Callback function was inserted.');
  1703. DoLog(LogLevel.Elements, window.PreviewCallback);
  1704.  
  1705. DoLog(LogLevel.Info, 'Preview enable succeed!');
  1706. }
  1707.  
  1708. // 关闭预览功能,不是给外部用的
  1709. function DeactivePreview() {
  1710. let returnMap = Pages[g_pageType].GetProcessedPageElements();
  1711. if (!returnMap.loadingComplete) {
  1712. DoLog(LogLevel.Error, 'Page not load, should not call Preview!');
  1713. return;
  1714. }
  1715.  
  1716. // 只需要取消绑定事件, attrs 以及回调都不需要删除
  1717. $(returnMap.controlElements).unbind('mouseenter').unbind('mouseleave').unbind('mousemove');
  1718.  
  1719. if (autoLoadInterval) {
  1720. clearInterval(autoLoadInterval);
  1721. autoLoadInterval = null;
  1722. }
  1723.  
  1724. DoLog(LogLevel.Info, 'Preview disable succeed!');
  1725. }
  1726.  
  1727. // iframe 的回调函数
  1728. function PreviewCallback(canvasWidth, canvasHeight) {
  1729. DoLog(LogLevel.Info, 'iframe callback, width: ' + canvasWidth + ', height: ' + canvasHeight);
  1730.  
  1731. let size = AdjustDivPosition();
  1732.  
  1733. $('.pp-loading').hide();
  1734. $('.pp-iframe').css({ 'width': size.width, 'height': size.height }).show();
  1735. }
  1736.  
  1737. // 调整预览 Div 的位置
  1738. function AdjustDivPosition() {
  1739. // 鼠标到预览图的距离
  1740. let fromMouseToDiv = 30;
  1741.  
  1742. let screenWidth = document.documentElement.clientWidth;
  1743. let screenHeight = document.documentElement.clientHeight;
  1744. let left = 0;
  1745. let top = document.body.scrollTop + document.documentElement.scrollTop;
  1746.  
  1747. let width = 0, height = 0;
  1748. if ($('.pp-main').find('iframe').length > 0) {
  1749. let iframe = $('.pp-main').find('iframe').get(0);
  1750. if (iframe.contentWindow.GetCanvasSize) {
  1751. let canvasSize = iframe.contentWindow.GetCanvasSize();
  1752. width = canvasSize.width;
  1753. height = canvasSize.height;
  1754. } else {
  1755. width = 0;
  1756. height = 0;
  1757. }
  1758. } else {
  1759. $('.pp-image').css({ 'width': '', 'height': '' });
  1760. width = $('.pp-image').get(0) == null ? 0 : $('.pp-image').get(0).width;
  1761. height = $('.pp-image').get(0) == null ? 0 : $('.pp-image').get(0).height;
  1762. }
  1763.  
  1764. let isShowOnLeft = g_mousePos.x > screenWidth / 2;
  1765.  
  1766. let newWidth = 48, newHeight = 48;
  1767. if (width > 0 && height > 0) {
  1768. newWidth = isShowOnLeft ? g_mousePos.x - fromMouseToDiv : screenWidth - g_mousePos.x - fromMouseToDiv;
  1769. newHeight = height / width * newWidth;
  1770. // 高度不足以完整显示,只能让两侧留空了
  1771. if (newHeight > screenHeight) {
  1772. newHeight = screenHeight;
  1773. newWidth = newHeight / height * width;
  1774. }
  1775. newWidth -= 5;
  1776. newHeight -= 5;
  1777.  
  1778. // 设置新的宽高
  1779. if ($('.pp-main').find('iframe').length > 0) {
  1780. let iframe = $('.pp-main').find('iframe');
  1781. iframe.get(0).contentWindow.ResizeCanvas(newWidth, newHeight);
  1782. iframe.css({ 'width': newWidth, 'height': newHeight });
  1783. }
  1784. else {
  1785. $('.pp-image').css({ 'height': newHeight + 'px', 'width': newWidth + 'px' });
  1786. }
  1787.  
  1788. // 调整下一次 loading 出现的位置
  1789. $('.pp-loading').css({ 'left': newWidth / 2 - 24 + 'px', 'top': newHeight / 2 - 24 + 'px' });
  1790. }
  1791.  
  1792. // 图片宽度大于高度很多时,会显示在页面顶部,鼠标碰不到,把它移动到下面
  1793. if (top + newHeight <= g_mousePos.y) {
  1794. top = (g_mousePos.y - newHeight - fromMouseToDiv);
  1795. }
  1796. // 调整DIV的位置
  1797. left = isShowOnLeft ? g_mousePos.x - newWidth - fromMouseToDiv : g_mousePos.x + fromMouseToDiv;
  1798.  
  1799. $('.pp-main').css({ 'left': left + 'px', 'top': top + 'px', 'width': newWidth, 'height': newHeight });
  1800.  
  1801. // 返回新的宽高
  1802. return {
  1803. width: newWidth,
  1804. height: newHeight,
  1805. };
  1806. }
  1807.  
  1808. // 请求显示的预览图ID
  1809. let displayTargetIllustId = '';
  1810. // 显示预览图
  1811. function ViewImages(regular, index, original, isShowOriginal, illustId) {
  1812. displayTargetIllustId = illustId;
  1813. if (!regular || regular.length === 0) {
  1814. DoLog(LogLevel.Error, 'Regular url array is null, can not view images!');
  1815. return;
  1816. }
  1817. if (index == null || index < 0 || index >= regular.length) {
  1818. DoLog(LogLevel.Error, 'Index(' + index + ') out of range, can not view images!');
  1819. return;
  1820. }
  1821. if (original == null || original.length === 0) {
  1822. DoLog(LogLevel.Warning, 'Original array is null, replace it with regular array.');
  1823. original = regular;
  1824. }
  1825. if (original.length < regular) {
  1826. DoLog(LogLevel.Warning, 'Original array\'s length is less than regular array, replace it with regular array.');
  1827. original = regular;
  1828. }
  1829. if (isShowOriginal == null) {
  1830. isShowOriginal = false;
  1831. }
  1832.  
  1833. if (original.length > 1) {
  1834. $('.pp-page').text((index + 1) + '/' + regular.length);
  1835. $('.pp-pageCount').show();
  1836. }
  1837. if (isShowOriginal) {
  1838. $('.pp-image').addClass('original');
  1839. } else {
  1840. $('.pp-image').removeClass('original');
  1841. }
  1842. g_settings.original = isShowOriginal ? 1 : 0;
  1843.  
  1844. // 隐藏页数和原图标签
  1845. $('.pp-original, .pp-pageCount').hide();
  1846.  
  1847. // 第一次需要绑定事件
  1848. if ($('.pp-image').attr('index') == null || $('.pp-image').attr('pageCount') != regular.length) {
  1849. $('.pp-image').attr('pageCount', regular.length);
  1850.  
  1851. // 绑定点击事件,Ctrl+左键 单击切换原图
  1852. $('.pp-image').on('click', function (ev) {
  1853. let _this = $(this);
  1854. let isOriginal = _this.hasClass('original');
  1855. let index = _this.attr('index');
  1856. if (index == null) {
  1857. index = 0;
  1858. } else {
  1859. index = parseInt(index);
  1860. }
  1861.  
  1862. if (ev.ctrlKey) {
  1863. // 按住 Ctrl 来回切换原图
  1864. isOriginal = !isOriginal;
  1865. ViewImages(regular, index, original, isOriginal, illustId);
  1866. }
  1867. else if (ev.shiftKey) {
  1868. // 按住 Shift 点击图片新标签页打开原图
  1869. window.open(original[index]);
  1870. } else {
  1871. if (regular.length == 1) {
  1872. return;
  1873. }
  1874. // 如果是多图,点击切换下一张
  1875. if (++index >= regular.length) {
  1876. index = 0;
  1877. }
  1878. ViewImages(regular, index, original, isOriginal, illustId);
  1879. // 预加载
  1880. for (let i = index + 1; i < regular.length && i <= index + 3; i++) {
  1881. let image = new Image();
  1882. image.src = isOriginal ? original[i] : regular[i];;
  1883. }
  1884. }
  1885. });
  1886.  
  1887. // 图片预加载完成
  1888. $('.pp-image').on('load', function () {
  1889. // 显示图片前也判断一下是不是目标图片
  1890. if (displayTargetIllustId != previewTargetIllustId) {
  1891. DoLog(LogLevel.Info, '(2)Drop this preview request.');
  1892. return;
  1893. }
  1894.  
  1895. // 调整图片位置和大小
  1896. let _this = $(this);
  1897. let size = AdjustDivPosition();
  1898. let isShowOriginal = _this.hasClass('original');
  1899.  
  1900. $('.pp-loading').css('display', 'none');
  1901. // 显示图像、页数、原图标签
  1902. $('.pp-image').css('display', '');
  1903. if (regular.length > 1) {
  1904. $('.pp-pageCount').show();
  1905. }
  1906. if (isShowOriginal) {
  1907. $('.pp-original').show();
  1908. }
  1909.  
  1910. // 预加载
  1911. for (let i = index + 1; i < regular.length && i <= index + 3; i++) {
  1912. let image = new Image();
  1913. image.src = isShowOriginal ? original[i] : regular[i];;
  1914. }
  1915. }).on('error', function () {
  1916. DoLog(LogLevel.Error, 'Load image failed!');
  1917. });
  1918. }
  1919.  
  1920. $('.pp-image').attr('src', isShowOriginal ? original[index] : regular[index]).attr('index', index);
  1921. }
  1922.  
  1923. // 处理自动加载
  1924. function ProcessAutoLoad() {
  1925. if (Pages[g_pageType].GetProcessedPageElements() == null) {
  1926. DoLog(LogLevel.Error, 'Call ProcessPageElements first!');
  1927. return;
  1928. }
  1929.  
  1930. let oldReturnMap = Pages[g_pageType].GetProcessedPageElements();
  1931. let newReturnMap = Pages[g_pageType].ProcessPageElements();
  1932.  
  1933. if (newReturnMap.loadingComplete) {
  1934. if (oldReturnMap.controlElements.length < newReturnMap.controlElements.length || newReturnMap.forceUpdate) {
  1935. DoLog(LogLevel.Info, 'Page loaded ' + (newReturnMap.controlElements.length - oldReturnMap.controlElements.length) + ' new work(s).');
  1936.  
  1937. if (g_settings.linkBlank) {
  1938. $(newReturnMap.controlElements).find('a').attr('target', '_blank');
  1939. }
  1940.  
  1941. SetTargetBlank(newReturnMap);
  1942. DeactivePreview();
  1943. ActivePreview();
  1944.  
  1945. return;
  1946. } else if (oldReturnMap.controlElements.length > newReturnMap.controlElements.length) {
  1947. DoLog(LogLevel.Warning, 'works become less?');
  1948.  
  1949. Pages[g_pageType].private.returnMap = oldReturnMap;
  1950.  
  1951. return;
  1952. }
  1953. }
  1954.  
  1955. DoLog(LogLevel.Info, 'Page not change.');
  1956. }
  1957.  
  1958. // 开启预览
  1959. ActivePreview();
  1960. }
  1961. /* ---------------------------------------- 排序 ---------------------------------------- */
  1962. let imageElementTemplate = null;
  1963. function PixivSK(callback) {
  1964. // 不合理的设定
  1965. if (g_settings.pageCount < 1 || g_settings.favFilter < 0) {
  1966. g_settings.pageCount = 1;
  1967. g_settings.favFilter = 0;
  1968. }
  1969. // 当前已经取得的页面数量
  1970. let currentGettingPageCount = 0;
  1971. // 当前加载的页面 URL
  1972. let currentUrl = 'https://www.pixiv.net/ajax/search/';
  1973. // 当前加载的是第几张页面
  1974. let currentPage = 0;
  1975. // 获取到的作品
  1976. let works = [];
  1977.  
  1978. // 仅搜索页启用
  1979. if (g_pageType != PageType.Search) {
  1980. return;
  1981. }
  1982.  
  1983. // 获取第 currentPage 页的作品
  1984. let getWorks = function (onloadCallback) {
  1985. $('#progress').text(Texts[g_language].sort_getWorks.replace('%1', currentGettingPageCount + 1).replace('%2', g_settings.pageCount));
  1986.  
  1987. let url = currentUrl.replace(/p=\d+/, 'p=' + currentPage);
  1988.  
  1989. if (location.href.indexOf('?') != -1) {
  1990. let param = location.href.split('?')[1];
  1991. param = param.replace(/^p=\d+/, '');
  1992. param = param.replace(/&p=\d+/, '');
  1993. url += '&' + param;
  1994. }
  1995.  
  1996. if (url.indexOf('order=') == -1) {
  1997. url += '&order=date_d';
  1998. }
  1999. if (url.indexOf('mode=') == -1) {
  2000. url += '&mode=all';
  2001. }
  2002. if (url.indexOf('s_mode=') == -1) {
  2003. url += '&s_mode=s_tag_full';
  2004. }
  2005.  
  2006. DoLog(LogLevel.Info, 'getWorks url: ' + url);
  2007.  
  2008. let req = new XMLHttpRequest();
  2009. req.open('GET', url, true);
  2010. req.onload = function (event) {
  2011. onloadCallback(req);
  2012. };
  2013. req.onerror = function (event) {
  2014. DoLog(LogLevel.Error, 'Request search page error!');
  2015. };
  2016.  
  2017. req.send(null);
  2018. };
  2019.  
  2020. function getFollowingOfType(user_id, type, offset) {
  2021. return new Promise(function(resolve, reject) {
  2022. if (offset == null) {
  2023. offset = 0;
  2024. }
  2025. let limit = 100;
  2026. let following_show = [];
  2027. $.ajax('https://www.pixiv.net/ajax/user/' + user_id + '/following?offset=' + offset + '&limit=' + limit + '&rest=' + type, {
  2028. async: true,
  2029. success: function(data) {
  2030. if (data == null || data.error) {
  2031. DoLog(LogLevel.Error, 'Following response contains an error.');
  2032. resolve([]);
  2033. return;
  2034. }
  2035. if (data.body.users.length == 0) {
  2036. resolve([]);
  2037. return;
  2038. }
  2039. $.each(data.body.users, function(i, user) {
  2040. following_show.push(user.userId);
  2041. });
  2042. getFollowingOfType(user_id, type, offset + limit).then(function(members) {
  2043. resolve(following_show.concat(members));
  2044. return;
  2045. });
  2046. },
  2047. error: function() {
  2048. DoLog(LogLevel.Error, 'Request following failed.');
  2049. resolve([]);
  2050. }
  2051. });
  2052. });
  2053. }
  2054.  
  2055. function getFollowingOfCurrentUser() {
  2056. return new Promise(function(resolve, reject) {
  2057. let user_id = '';
  2058.  
  2059. try {
  2060. user_id = dataLayer[0].user_id;
  2061. } catch(ex) {
  2062. DoLog(LogLevel.Error, 'Get user id failed.');
  2063. resolve([]);
  2064. return;
  2065. }
  2066.  
  2067. // show/hide
  2068. $('#progress').text(Texts[g_language].sort_getPublicFollowing);
  2069.  
  2070. // 首先从Cookie读取
  2071. let following = GetCookie('followingOfUid-' + user_id);
  2072. if (following != null) {
  2073. resolve(following);
  2074. return;
  2075. }
  2076.  
  2077. getFollowingOfType(user_id, 'show').then(function(members) {
  2078. $('#progress').text(Texts[g_language].sort_getPrivateFollowing);
  2079. getFollowingOfType(user_id, 'hide').then(function(members2) {
  2080. let following = members.concat(members2);
  2081. SetCookie('followingOfUid-' + user_id, following, 1);
  2082. resolve(following);
  2083. });
  2084. });
  2085. });
  2086. }
  2087.  
  2088. // 筛选已关注画师作品
  2089. let filterByUser = function() {
  2090. return new Promise(function(resolve, reject) {
  2091. if (!g_settings.hideFollowed) {
  2092. resolve();
  2093. }
  2094.  
  2095. getFollowingOfCurrentUser().then(function(members) {
  2096. let tempWorks = [];
  2097. let hideWorkCount = 0;
  2098. $(works).each(function (i, work) {
  2099. let found = false;
  2100. for (let i = 0; i < members.length; i++) {
  2101. if (members[i] == work.userId) {
  2102. found = true;
  2103. break;
  2104. }
  2105. }
  2106. if (!found) {
  2107. tempWorks.push(work);
  2108. } else {
  2109. hideWorkCount++;
  2110. }
  2111. });
  2112. works = tempWorks;
  2113.  
  2114. DoLog(LogLevel.Info, hideWorkCount + ' works were hide.');
  2115. DoLog(LogLevel.Elements, works);
  2116. resolve();
  2117. });
  2118. });
  2119. };
  2120.  
  2121. // 排序和筛选
  2122. let filterAndSort = function () {
  2123. return new Promise(function(resolve, reject) {
  2124. DoLog(LogLevel.Info, 'Start sort.');
  2125. DoLog(LogLevel.Elements, works);
  2126.  
  2127. // 收藏量低于 FAV_FILTER 的作品不显示
  2128. let text = Texts[g_language].sort_filtering.replace('%2', g_settings.favFilter);
  2129. text = text.replace('%1', (g_settings.hideFavorite ? Texts[g_language].sort_filteringHideFavorite : ''));
  2130. $('#progress').text(text); // 实际上这个太快完全看不到
  2131. let tmp = [];
  2132. $(works).each(function (i, work) {
  2133. let bookmarkCount = work.bookmarkCount ? work.bookmarkCount : 0;
  2134. if (bookmarkCount >= g_settings.favFilter && !(g_settings.hideFavorite && work.bookmarkData)) {
  2135. tmp.push(work);
  2136. }
  2137. });
  2138. works = tmp;
  2139.  
  2140. filterByUser().then(function() {
  2141. // 排序
  2142. works.sort(function (a, b) {
  2143. let favA = a.bookmarkCount;
  2144. let favB = b.bookmarkCount;
  2145. if (!favA) {
  2146. favA = 0;
  2147. }
  2148. if (!favB) {
  2149. favB = 0;
  2150. }
  2151. if (favA > favB) {
  2152. return -1;
  2153. }
  2154. if (favA < favB) {
  2155. return 1;
  2156. }
  2157. return 0;
  2158. });
  2159. DoLog(LogLevel.Info, 'Sort complete.');
  2160. DoLog(LogLevel.Elements, works);
  2161. resolve();
  2162. });
  2163. });
  2164. };
  2165.  
  2166. if (currentPage === 0) {
  2167. let url = location.href;
  2168.  
  2169. if (url.indexOf('&p=') == -1 && url.indexOf('?p=') == -1) {
  2170. DoLog(LogLevel.Warning, 'Can not found page in url.');
  2171. if (url.indexOf('?') == -1) {
  2172. url += '?p=1';
  2173. DoLog(LogLevel.Info, 'Add "?p=1": ' + url);
  2174. } else {
  2175. url += '&p=1';
  2176. DoLog(LogLevel.Info, 'Add "&p=1": ' + url);
  2177. }
  2178. }
  2179. let wordMatch = url.match(/\/tags\/([^/]*)\//);
  2180. let searchWord = '';
  2181. if (wordMatch) {
  2182. DoLog(LogLevel.Info, 'Search key word: ' + searchWord);
  2183. searchWord = wordMatch[1];
  2184. } else {
  2185. DoLog(LogLevel.Error, 'Can not found search key word!');
  2186. return;
  2187. }
  2188.  
  2189. // page
  2190. let page = url.match(/p=(\d*)/)[1];
  2191. currentPage = parseInt(page);
  2192. DoLog(LogLevel.Info, 'Current page: ' + currentPage);
  2193.  
  2194. let type = url.match(/tags\/.*\/(.*)[?$]/)[1];
  2195. currentUrl += type + '/';
  2196.  
  2197. currentUrl += searchWord + '?word=' + searchWord + '&p=' + currentPage;
  2198. DoLog(LogLevel.Info, 'Current url: ' + currentUrl);
  2199. } else {
  2200. DoLog(LogLevel.Error, '???');
  2201. }
  2202.  
  2203. let imageContainer = Pages[PageType.Search].GetImageListContainer();
  2204. // loading
  2205. $(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>');
  2206.  
  2207. // page
  2208. if (true) {
  2209. let pageSelectorDiv = Pages[PageType.Search].GetPageSelector();
  2210. if (pageSelectorDiv == null) {
  2211. DoLog(LogLevel.Error, 'Can not found page selector!');
  2212. return;
  2213. }
  2214.  
  2215. if ($(pageSelectorDiv).find('a').length > 2) {
  2216. let pageButton = $(pageSelectorDiv).find('a').get(1);
  2217. let newPageButtons = [];
  2218. let pageButtonString = 'Previewer';
  2219. for (let i = 0; i < 9; i++) {
  2220. let newPageButton = pageButton.cloneNode(true);
  2221. $(newPageButton).find('span').text(pageButtonString[i]);
  2222. newPageButtons.push(newPageButton);
  2223. }
  2224.  
  2225. $(pageSelectorDiv).find('button').remove();
  2226. while ($(pageSelectorDiv).find('a').length > 2) {
  2227. $(pageSelectorDiv).find('a:first').next().remove();
  2228. }
  2229.  
  2230. for (let i = 0; i < 9; i++) {
  2231. $(pageSelectorDiv).find('a:last').before(newPageButtons[i]);
  2232. }
  2233.  
  2234. $(pageSelectorDiv).find('a').attr('href', 'javascript:;');
  2235.  
  2236. let pageUrl = location.href;
  2237. if (pageUrl.indexOf('&p=') == -1 && pageUrl.indexOf('?p=') == -1) {
  2238. if (pageUrl.indexOf('?') == -1) {
  2239. pageUrl += '?p=1';
  2240. } else {
  2241. pageUrl += '&p=1';
  2242. }
  2243. }
  2244. let prevPageUrl = pageUrl.replace(/p=\d+/, 'p=' + (currentPage - g_settings.pageCount > 1 ? currentPage - g_settings.pageCount : 1));
  2245. let nextPageUrl = pageUrl.replace(/p=\d+/, 'p=' + (currentPage + g_settings.pageCount));
  2246. DoLog(LogLevel.Info, 'Previous page url: ' + prevPageUrl);
  2247. DoLog(LogLevel.Info, 'Next page url: ' + nextPageUrl);
  2248. // 重新插入一遍清除事件绑定
  2249. let prevButton = $(pageSelectorDiv).find('a:first');
  2250. prevButton.before(prevButton.clone());
  2251. prevButton.remove();
  2252. let nextButton = $(pageSelectorDiv).find('a:last');
  2253. nextButton.before(nextButton.clone());
  2254. nextButton.remove();
  2255. $(pageSelectorDiv).find('a:first').attr('href', prevPageUrl).addClass('pp-prevPage');
  2256. $(pageSelectorDiv).find('a:last').attr('href', nextPageUrl).addClass('pp-nextPage');
  2257. }
  2258.  
  2259. let onloadCallback = function (req) {
  2260. let no_artworks_found = false;
  2261.  
  2262. try {
  2263. let json = JSON.parse(req.responseText);
  2264. if (json.hasOwnProperty('error')) {
  2265. if (json.error === false) {
  2266. let data;
  2267. if (json.body.illustManga) {
  2268. data = json.body.illustManga.data;
  2269. } else if (json.body.manga) {
  2270. data = json.body.manga.data;
  2271. } else if (json.body.illust) {
  2272. data = json.body.illust.data;
  2273. }
  2274. if (data.length > 0) {
  2275. works = works.concat(data);
  2276. } else {
  2277. no_artworks_found = true;
  2278. }
  2279. } else {
  2280. DoLog(LogLevel.Error, 'ajax error!');
  2281. return;
  2282. }
  2283. } else {
  2284. DoLog(LogLevel.Error, 'Key "error" not found!');
  2285. return;
  2286. }
  2287. } catch (e) {
  2288. DoLog(LogLevel.Error, 'A invalid json string!');
  2289. DoLog(LogLevel.Info, req.responseText);
  2290. }
  2291.  
  2292. currentPage++;
  2293. currentGettingPageCount++;
  2294.  
  2295. // 后面已经没有作品了
  2296. if (no_artworks_found) {
  2297. DoLog(LogLevel.Warning, 'No artworks found, ignore ' + (g_settings.pageCount - currentGettingPageCount) + ' pages.');
  2298. currentPage += g_settings.pageCount - currentGettingPageCount;
  2299. currentGettingPageCount = g_settings.pageCount;
  2300. }
  2301. // 设定数量的页面加载完成
  2302. if (currentGettingPageCount == g_settings.pageCount) {
  2303. DoLog(LogLevel.Info, 'Load complete, start to load bookmark count.');
  2304. DoLog(LogLevel.Elements, works);
  2305.  
  2306. // 获取到的作品里面可能有广告,先删掉,否则后面一些处理需要做判断
  2307. let tempWorks = [];
  2308. let workIdsSet = new Set();
  2309. for (let i = 0; i < works.length; i++) {
  2310. if (works[i].id && !workIdsSet.has(works[i].id)) {
  2311. tempWorks.push(works[i]);
  2312. workIdsSet.add(works[i].id);
  2313. }
  2314. }
  2315. works = tempWorks;
  2316. DoLog(LogLevel.Info, 'Clear ad container complete.');
  2317. DoLog(LogLevel.Elements, works);
  2318.  
  2319. GetBookmarkCount(0);
  2320. } else {
  2321. getWorks(onloadCallback);
  2322. }
  2323. };
  2324.  
  2325. getWorks(onloadCallback);
  2326. }
  2327.  
  2328. let xhrs = [];
  2329. let currentRequestGroupMinimumIndex = 0;
  2330. function FillXhrsArray() {
  2331. xhrs.length = 0;
  2332. let onloadFunc = function (event) {
  2333. let json = null;
  2334. try {
  2335. json = JSON.parse(event.currentTarget.responseText);
  2336. } catch(e) {
  2337. DoLog(LogLevel.Error, 'Parse json failed!');
  2338. DoLog(LogLevel.Element, e);
  2339. return;
  2340. }
  2341.  
  2342. if (json) {
  2343. let illustId = '';
  2344. let illustIdMatched = event.currentTarget.responseURL.match(/illust_id=(\d+)/);
  2345. if (illustIdMatched) {
  2346. illustId = illustIdMatched[1];
  2347. } else {
  2348. DoLog(LogLevel.Error, 'Can not get illust id from url!');
  2349. return;
  2350. }
  2351. let indexOfThisRequest = -1;
  2352. for (let j = 0; j < g_maxXhr; j++) {
  2353. if (xhrs[j].illustId == illustId) {
  2354. indexOfThisRequest = j;
  2355. break;
  2356. }
  2357. }
  2358. if (indexOfThisRequest == -1) {
  2359. DoLog(LogLevel.Error, 'This url not match any request!');
  2360. return;
  2361. }
  2362. xhrs[indexOfThisRequest].complete = true;
  2363.  
  2364. if (!json.error) {
  2365. let bookmarkCount = json.body.illust_details.bookmark_user_total;
  2366. works[currentRequestGroupMinimumIndex + indexOfThisRequest].bookmarkCount = parseInt(bookmarkCount);
  2367. DoLog(LogLevel.Info, 'IllustId: ' + illustId + ', bookmarkCount: ' + bookmarkCount);
  2368. } else {
  2369. DoLog(LogLevel.Error, 'Some error occured: ' + json.message);
  2370. }
  2371.  
  2372. let completeCount = 0;
  2373. // 真实完成数(不包含没有发起请求的XHR,最后一批请求时)
  2374. let completeReally = 0;
  2375. for (let j = 0; j < g_maxXhr; j++) {
  2376. if (xhrs[j].complete) {
  2377. completeCount++;
  2378. if (xhrs[j].illustId != '') {
  2379. completeReally++;
  2380. }
  2381. }
  2382. }
  2383. $('#loading').find('#progress').text(Texts[g_language].sort_getBookmarkCount.replace('%1', currentRequestGroupMinimumIndex + completeReally).replace('%2', works.length));
  2384. if (completeCount == g_maxXhr) {
  2385. currentRequestGroupMinimumIndex += g_maxXhr;
  2386. GetBookmarkCount(currentRequestGroupMinimumIndex);
  2387. }
  2388. }
  2389. };
  2390. let onerrorFunc = function (event) {
  2391. let illustId = '';
  2392. let illustIdMatched = event.currentTarget.__sentry_xhr__.url.match(/artworks\/(\d+)/);
  2393. if (illustIdMatched) {
  2394. illustId = illustIdMatched[1];
  2395. } else {
  2396. DoLog(LogLevel.Error, 'Can not get illust id from url!');
  2397. return;
  2398. }
  2399.  
  2400. DoLog(LogLevel.Error, 'Send request failed, set this illust(' + illustId + ')\'s bookmark count to 0!');
  2401.  
  2402. let indexOfThisRequest = -1;
  2403. for (let j = 0; j < g_maxXhr; j++) {
  2404. if (xhrs[j].illustId == illustId) {
  2405. indexOfThisRequest = j;
  2406. break;
  2407. }
  2408. }
  2409. if (indexOfThisRequest == -1) {
  2410. DoLog('This url not match any request!');
  2411. return;
  2412. }
  2413. xhrs[indexOfThisRequest].complete = true;
  2414.  
  2415. let completeCount = 0;
  2416. let completeReally = 0;
  2417. for (let j = 0; j < g_maxXhr; j++) {
  2418. if (xhrs[j].complete) {
  2419. completeCount++;
  2420. if (xhrs[j].illustId != '') {
  2421. completeReally++;
  2422. }
  2423. }
  2424. }
  2425. $('#loading').find('#progress').text(Texts[g_language].sort_getBookmarkCount.replace('%1', currentRequestGroupMinimumIndex + completeReally).replace('%2', works.length));
  2426. if (completeCount == g_maxXhr) {
  2427. GetBookmarkCount(currentRequestGroupMinimumIndex + g_maxXhr);
  2428. }
  2429. };
  2430. for (let i = 0; i < g_maxXhr; i++) {
  2431. xhrs.push({
  2432. xhr: new XMLHttpRequest(),
  2433. illustId: '',
  2434. complete: false,
  2435. });
  2436. xhrs[i].xhr.onload = onloadFunc;
  2437. xhrs[i].xhr.onerror = onerrorFunc;
  2438. }
  2439. }
  2440.  
  2441. let GetBookmarkCount = function (index) {
  2442. if (index >= works.length) {
  2443. clearAndUpdateWorks();
  2444. return;
  2445. }
  2446.  
  2447. if (xhrs.length === 0) {
  2448. FillXhrsArray();
  2449. }
  2450.  
  2451. for (let i = 0; i < g_maxXhr; i++) {
  2452. if (index + i >= works.length) {
  2453. xhrs[i].complete = true;
  2454. xhrs[i].illustId = '';
  2455. continue;
  2456. }
  2457.  
  2458. let illustId = works[index + i].id;
  2459. let url = 'https://www.pixiv.net/touch/ajax/illust/details?illust_id=' + illustId;
  2460. xhrs[i].illustId = illustId;
  2461. xhrs[i].complete = false;
  2462. xhrs[i].xhr.open('GET', url, true);
  2463. xhrs[i].xhr.send(null);
  2464. }
  2465. };
  2466.  
  2467. /*
  2468. li
  2469. -div
  2470. --div
  2471. ---div
  2472. ----div
  2473. -----div
  2474. ------a
  2475. -------div: 多图标签、R18标签
  2476. -------div: 里面是 img (以及 svg 动图标签)
  2477. ------div: 里面是 like 相关的元素
  2478. ---a: 作品标题,跳转链接
  2479. ---div: 作者头像和昵称
  2480. */
  2481. let clearAndUpdateWorks = function () {
  2482. filterAndSort().then(function() {
  2483.  
  2484. let container = Pages[PageType.Search].GetImageListContainer();
  2485. let firstImageElement = Pages[PageType.Search].GetFirstImageElement();
  2486. if (imageElementTemplate == null) {
  2487. imageElementTemplate = firstImageElement.cloneNode(true);
  2488.  
  2489. // 清理模板
  2490. // image
  2491. let img = $($(imageElementTemplate).find('img').get(0));
  2492. let imageDiv = img.parent();
  2493. let imageLink = imageDiv.parent();
  2494. let imageLinkDiv = imageLink.parent();
  2495. let titleLinkParent = imageLinkDiv.parent().next();
  2496. if (img == null || imageDiv == null || imageLink == null || imageLinkDiv == null || titleLinkParent == null) {
  2497. DoLog(LogLevel.Error, 'Can not found some elements!');
  2498. }
  2499. let titleLink = $('<a></a>');
  2500. if (titleLinkParent.children().length == 0) {
  2501. titleLinkParent.append(titleLink);
  2502. } else {
  2503. titleLink = titleLinkParent.children('a:first');
  2504. }
  2505.  
  2506. // author
  2507. let authorDiv = titleLinkParent.next();
  2508. let authorLinkProfileImage = authorDiv.find('a:first');
  2509. let authorLink = authorDiv.find('a:last');
  2510. let authorName = authorLink;
  2511. let authorImage = $(authorDiv.find('img').get(0));
  2512.  
  2513. // others
  2514. let bookmarkDiv = imageLink.next();
  2515. let bookmarkSvg = bookmarkDiv.find('svg');
  2516. let additionTagDiv = imageDiv.prev();
  2517. let animationTag = imageDiv.find('svg');
  2518.  
  2519. let bookmarkCountDiv = additionTagDiv.clone();
  2520. bookmarkCountDiv.css({ 'top': 'auto', 'bottom': '0px', 'width': '50%' });
  2521. additionTagDiv.parent().append(bookmarkCountDiv);
  2522.  
  2523. // 添加 class,方便后面修改内容
  2524. img.addClass('ppImg');
  2525. imageLink.addClass('ppImageLink');
  2526. //if (titleLink.get(0).tagName == 'A') {
  2527. titleLink.addClass('ppTitleLink');
  2528. //} else {
  2529. // titleLink.append('<a class="ppTitleLink"></a>');
  2530. //}
  2531. authorLinkProfileImage.addClass('ppAuthorLinkProfileImage');
  2532. authorLink.addClass('ppAuthorLink');
  2533. authorName.addClass('ppAuthorName');
  2534. authorImage.addClass('ppAuthorImage');
  2535. bookmarkSvg.attr('class', bookmarkSvg.attr('class') + ' ppBookmarkSvg');
  2536. additionTagDiv.addClass('ppAdditionTag');
  2537. bookmarkCountDiv.addClass('ppBookmarkCount');
  2538.  
  2539. img.attr('src', '');
  2540. additionTagDiv.empty();
  2541. bookmarkCountDiv.empty();
  2542. animationTag.remove();
  2543. bookmarkSvg.find('path:first').css('fill', 'rgb(31, 31, 31)');
  2544. bookmarkSvg.find('path:last').css('fill', 'rgb(255, 255, 255)');
  2545.  
  2546. if (g_settings.linkBlank) {
  2547. imageLink.attr('target', '_blank');
  2548. titleLink.attr('target', '_blank');
  2549. authorLinkProfileImage.attr('target', '_blank');
  2550. authorLink.attr('target', '_blank');
  2551. }
  2552. }
  2553.  
  2554. $(container).empty();
  2555. for (let i = 0; i < works.length; i++) {
  2556. let li = $(imageElementTemplate.cloneNode(true));
  2557.  
  2558. let regularUrl = works[i].url;
  2559. if (g_settings.fullSizeThumb) {
  2560. regularUrl = convertThumbUrlToSmall(works[i].url);
  2561. }
  2562. li.find('.ppImg').attr('src', regularUrl).css('object-fit', 'contain');
  2563. li.find('.ppImageLink').attr('href', '/artworks/' + works[i].id);
  2564. li.find('.ppTitleLink').attr('href', '/artworks/' + works[i].id).text(works[i].title);
  2565. 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});
  2566. li.find('.ppAuthorName').text(works[i].userName);
  2567. li.find('.ppAuthorImage').attr('src', works[i].profileImageUrl);
  2568. li.find('.ppBookmarkSvg').attr('illustId', works[i].id);
  2569. if (works[i].bookmarkData) {
  2570. li.find('.ppBookmarkSvg').find('path').css('fill', 'rgb(255, 64, 96)');
  2571. li.find('.ppBookmarkSvg').attr('bookmarkId', works[i].bookmarkData.id);
  2572. }
  2573. if (works[i].xRestrict !== 0) {
  2574. 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>';
  2575. li.find('.ppAdditionTag').append(R18HTML);
  2576. }
  2577. if (works[i].pageCount > 1) {
  2578. 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>';
  2579. li.find('.ppAdditionTag').append(pageCountHTML);
  2580. }
  2581. 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>';
  2582. li.find('.ppBookmarkCount').append(bookmarkCountHTML);
  2583. if (works[i].illustType == 2) {
  2584. 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>';
  2585. li.find('.ppImg').after(animationHTML);
  2586. }
  2587.  
  2588. $(container).append(li);
  2589. }
  2590.  
  2591. // 监听加入书签点击事件,监听父节点,但是按照 <svg> 节点处理
  2592. $('.ppBookmarkSvg').parent().on('click', function (ev) {
  2593. if (g_csrfToken == '') {
  2594. DoLog(LogLevel.Error, 'No g_csrfToken, failed to add bookmark!');
  2595. alert('获取 Token 失败,无法添加,请到详情页操作。');
  2596. return;
  2597. }
  2598. // 非公开收藏
  2599. let restrict = 0;
  2600. if (ev.ctrlKey) {
  2601. restrict = 1;
  2602. }
  2603.  
  2604. let _this = $(this).children('svg:first');
  2605. let illustId = _this.attr('illustId');
  2606. let bookmarkId = _this.attr('bookmarkId');
  2607. if (bookmarkId == null || bookmarkId == '') {
  2608. DoLog(LogLevel.Info, 'Add bookmark, illustId: ' + illustId);
  2609. $.ajax('/ajax/illusts/bookmarks/add', {
  2610. method: 'POST',
  2611. contentType: 'application/json;charset=utf-8',
  2612. headers: { 'x-csrf-token': g_csrfToken },
  2613. data: '{"illust_id":"' + illustId + '","restrict":' +restrict + ',"comment":"","tags":[]}',
  2614. success: function (data) {
  2615. DoLog(LogLevel.Info, 'addBookmark result: ');
  2616. DoLog(LogLevel.Elements, data);
  2617. if (data.error) {
  2618. DoLog(LogLevel.Error, 'Server returned an error: ' + data.message);
  2619. return;
  2620. }
  2621. let bookmarkId = data.body.last_bookmark_id;
  2622. DoLog(LogLevel.Info, 'Add bookmark success, bookmarkId is ' + bookmarkId);
  2623. _this.attr('bookmarkId', bookmarkId);
  2624. _this.find('path').css('fill', 'rgb(255, 64, 96)');
  2625. }
  2626. });
  2627. } else {
  2628. DoLog(LogLevel.Info, 'Delete bookmark, bookmarkId: ' + bookmarkId);
  2629. $.ajax('/rpc/index.php', {
  2630. method: 'POST',
  2631. headers: { 'x-csrf-token': g_csrfToken },
  2632. data: { "mode": "delete_illust_bookmark", "bookmark_id": bookmarkId },
  2633. success: function (data) {
  2634. DoLog(LogLevel.Info, 'addBookmark result: ');
  2635. DoLog(LogLevel.Elements, data);
  2636. if (data.error) {
  2637. DoLog(LogLevel.Error, 'Server returned an error: ' + data.message);
  2638. return;
  2639. }
  2640. DoLog(LogLevel.Info, 'Delete bookmark success.');
  2641. _this.attr('bookmarkId', '');
  2642. _this.find('path:first').css('fill', 'rgb(31, 31, 31)');
  2643. _this.find('path:last').css('fill', 'rgb(255, 255, 255)');
  2644. }
  2645. });
  2646. }
  2647.  
  2648. _this.parent().focus();
  2649. });
  2650.  
  2651. $('.ppAuthorLink').on('mouseenter', function(e){
  2652. let _this = $(this);
  2653.  
  2654. function getOffset(e) {
  2655. if (e.offsetParent) {
  2656. let offset = getOffset(e.offsetParent);
  2657. return {
  2658. offsetTop: e.offsetTop + offset.offsetTop,
  2659. offsetLeft: e.offsetLeft + offset.offsetLeft,
  2660. };
  2661. } else {
  2662. return {
  2663. offsetTop: e.offsetTop,
  2664. offsetLeft: e.offsetLeft,
  2665. };
  2666. }
  2667. }
  2668.  
  2669. let isFollowed = false;
  2670. $.ajax('https://www.pixiv.net/ajax/user/' + _this.attr('userId') + '?full=1', {
  2671. method: 'GET',
  2672. async: false,
  2673. success: function(data) {
  2674. if (data.error == false && data.body.isFollowed) {
  2675. isFollowed = true;
  2676. }
  2677. },
  2678. });
  2679.  
  2680. $('.pp-authorDiv').remove();
  2681. 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>');
  2682. $('body').append(pres);
  2683. let offset = getOffset(this);
  2684. pres.find('.ppa-main').css({'top': offset.offsetTop - 196 + 'px', 'left': offset.offsetLeft - 113 + 'px'});
  2685. pres.find('.ppa-authorLink').attr('href', '/member.php?id=' + _this.attr('userId'));
  2686. pres.find('.ppa-authorImage').attr('src', _this.attr('profileImageUrl'));
  2687. pres.find('.ppa-authorName').text(_this.attr('userName'));
  2688. if (isFollowed) {
  2689. 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>';
  2690. }
  2691. pres.find('.ppa-follow').attr('userId', _this.attr('userId'));
  2692. pres.on('mouseleave', function(e) {
  2693. $(this).remove();
  2694. }).on('mouseenter', function() {
  2695. $(this).addClass('mouseenter');
  2696. });
  2697.  
  2698. pres.find('.ppa-follow').on('click', function() {
  2699. let userId = $(this).attr('userId');
  2700. if ($(this).hasClass('followed')) {
  2701. // 取关
  2702. $.ajax('https://www.pixiv.net/rpc_group_setting.php', {
  2703. method: 'POST',
  2704. headers: { 'x-csrf-token': g_csrfToken },
  2705. data: 'mode=del&type=bookuser&id=' + userId,
  2706. success: function(data) {
  2707. DoLog(LogLevel.Info, 'delete bookmark result: ');
  2708. DoLog(LogLevel.Elements, data);
  2709.  
  2710. if (data.type == 'bookuser') {
  2711. $('.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>';
  2712. }
  2713. else {
  2714. DoLog(LogLevel.Error, 'Delete follow failed!');
  2715. }
  2716. }
  2717. });
  2718. } else {
  2719. // 关注
  2720. $.ajax('https://www.pixiv.net/bookmark_add.php', {
  2721. method: 'POST',
  2722. headers: {'x-csrf-token': g_csrfToken},
  2723. data: 'mode=add&type=user&user_id=' + userId + '&tag=&restrict=0&format=json',
  2724. success: function (data) {
  2725. DoLog(LogLevel.Info, 'addBookmark result: ');
  2726. DoLog(LogLevel.Elements, data);
  2727. // success
  2728. if (data.length === 0) {
  2729. $('.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>';
  2730. } else {
  2731. DoLog(LogLevel.Error, 'Follow failed!');
  2732. }
  2733. }
  2734. });
  2735. }
  2736. });
  2737. }).on('mouseleave', function(e) {
  2738. setTimeout(function() {
  2739. if (!$('.pp-authorDiv').hasClass('mouseenter')) {
  2740. $('.pp-authorDiv').remove();
  2741. }
  2742. }, 200);
  2743. });
  2744.  
  2745. if (works.length === 0) {
  2746. $(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>';
  2747. }
  2748.  
  2749. // 恢复显示
  2750. $('#loading').remove();
  2751. $(container).show();
  2752.  
  2753. Pages[PageType.Search].ProcessPageElements();
  2754.  
  2755. // 监听键盘的左右键,用来翻页
  2756. $(document).keydown(function (e) {
  2757. if (g_settings.pageByKey != 1) {
  2758. return;
  2759. }
  2760. if (e.keyCode == 39) {
  2761. let btn = $('.pp-nextPage');
  2762. if (btn.length < 1 || btn.attr('hidden') == 'hidden') {
  2763. return;
  2764. }
  2765. // 很奇怪不能用 click()
  2766. location.href = btn.attr('href');
  2767. } else if (e.keyCode == 37) {
  2768. let btn = $('.pp-prevPage');
  2769. if (btn.length < 1 || btn.attr('hidden') == 'hidden') {
  2770. return;
  2771. }
  2772. location.href = btn.attr('href');
  2773. }
  2774. });
  2775.  
  2776. if (callback) {
  2777. callback();
  2778. }
  2779. });
  2780. }
  2781. };
  2782. /* ---------------------------------------- 设置 ---------------------------------------- */
  2783. function SetCookie(name, value, days) {
  2784. let Days = 180;
  2785. if (days) {
  2786. Days = days;
  2787. }
  2788. let exp = new Date();
  2789. exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000);
  2790. let str = JSON.stringify(value);
  2791. document.cookie = name + "=" + str + ";expires=" + exp.toGMTString() + ';path=\/';
  2792. }
  2793. function GetCookie(name) {
  2794. let arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
  2795. if (arr = document.cookie.match(reg)) {
  2796. return unescape(arr[2]);
  2797. }
  2798. else {
  2799. return null;
  2800. }
  2801. }
  2802. function ShowInstallMessage() {
  2803. $('#pp-bg').remove();
  2804. let bg = $('<div id="pp-bg"></div>').css({
  2805. 'width': document.documentElement.clientWidth + 'px', 'height': document.documentElement.clientHeight + 'px', 'position': 'fixed',
  2806. 'z-index': 999999, 'background-color': 'rgba(0,0,0,0.8)',
  2807. 'left': '0px', 'top': '0px'
  2808. });
  2809. $('body').append(bg);
  2810.  
  2811. 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;
  2812. $('#pps-close').click(function () {
  2813. $('#pp-bg').remove();
  2814. });
  2815. }
  2816. function GetSettings() {
  2817. let settings;
  2818.  
  2819. let cookie = GetCookie('PixivPreview');
  2820. // 新安装
  2821. if (cookie == null || cookie == 'null') {
  2822. settings = g_defaultSettings;
  2823. SetCookie('PixivPreview', settings);
  2824.  
  2825. ShowInstallMessage();
  2826. } else {
  2827. settings = JSON.parse(cookie);
  2828. }
  2829.  
  2830. // 升级
  2831. if (settings.version != g_version) {
  2832. ShowInstallMessage();
  2833. }
  2834.  
  2835. if (settings.version == null || settings.version != g_version) {
  2836. settings.version = g_version;
  2837. SetCookie('PixivPreview', settings);
  2838. }
  2839.  
  2840. return settings;
  2841. }
  2842. function ShowSetting() {
  2843. let screenWidth = document.documentElement.clientWidth;
  2844. let screenHeight = document.documentElement.clientHeight;
  2845.  
  2846. $('#pp-bg').remove();
  2847. let bg = $('<div id="pp-bg"></div>').css({
  2848. 'width': screenWidth + 'px', 'height': screenHeight + 'px', 'position': 'fixed',
  2849. 'z-index': 999999, 'background-color': 'rgba(0,0,0,0.8)',
  2850. 'left': '0px', 'top': '0px'
  2851. });
  2852. $('body').append(bg);
  2853.  
  2854. let settings = GetSettings();
  2855.  
  2856. let settingHTML = '<div style="color: white; font-size: 1em;">' +
  2857. '<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;">' +
  2858. '<div style="position: absolute; width: 60%; left: 25%; top: 10%; overflow: hidden;">' +
  2859. '<ul id="pps-ul" style="list-style: none; padding: 0; margin: 0;"></ul></div>' +
  2860. '<div style="margin-top: 10px;position: absolute;bottom: 20%;width: 100%;text-align: center;">' +
  2861. '<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>' +
  2862. '<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>' +
  2863. '</div></div>';
  2864.  
  2865. bg.get(0).innerHTML = settingHTML;
  2866. let ul = $('#pps-ul');
  2867. function getImageAction(id) {
  2868. 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;"/>';
  2869. }
  2870. function getInputAction(id) {
  2871. return '<input id="' + id + '" style="font-size: 24px; padding: 0; margin-right: 16px; border-width: 0px; width: 64px; text-align: center;"/>'
  2872. }
  2873. function getSelectAction(id) {
  2874. return '<select id="' + id + '" style="font-size: 20px; margin-right: 10px;"></select>';
  2875. }
  2876. function addItem(action, text) {
  2877. ul.append('<li style="font-size: 25px; padding-bottom: 5px;">' + action + text + '</li>');
  2878. }
  2879. ul.empty();
  2880. addItem(getSelectAction('pps-lang'), Texts[g_language].setting_language);
  2881. addItem(getImageAction('pps-preview'), Texts[g_language].setting_preview);
  2882. addItem(getImageAction('pps-sort'), Texts[g_language].setting_sort);
  2883. addItem(getImageAction('pps-anime'), Texts[g_language].setting_anime);
  2884. addItem(getImageAction('pps-original'), Texts[g_language].setting_origin);
  2885. addItem(getInputAction('pps-previewDelay'), Texts[g_language].setting_previewDelay);
  2886. addItem('', '&nbsp');
  2887. addItem(getInputAction('pps-maxPage'), Texts[g_language].setting_maxPage);
  2888. addItem(getInputAction('pps-hideLess'), Texts[g_language].setting_hideWork);
  2889. addItem(getImageAction('pps-hideBookmarked'), Texts[g_language].setting_hideFav);
  2890. addItem(getImageAction('pps-hideFollowed'), Texts[g_language].setting_hideFollowed + '&nbsp<button id="pps-clearFollowingCache" style="cursor:pointer;background-color:gold;border-radius:12px;" title="' + Texts[g_language].setting_clearFollowingCacheHelp + '">' + Texts[g_language].setting_clearFollowingCache + '</button>');
  2891. addItem(getImageAction('pps-newTab'), Texts[g_language].setting_blank);
  2892. addItem(getImageAction('pps-pageKey'), Texts[g_language].setting_turnPage);
  2893. addItem(getImageAction('pps-fullSizeThumb'), Texts[g_language].sort_fullSizeThumb);
  2894.  
  2895. let imgOn = 'https://pp-1252089172.cos.ap-chengdu.myqcloud.com/On.png';
  2896. let imgOff = 'https://pp-1252089172.cos.ap-chengdu.myqcloud.com/Off.png'
  2897. $('#pps-preview').attr('src', settings.enablePreview ? imgOn : imgOff).addClass(settings.enablePreview ? 'on' : 'off').css('cursor: pointer');
  2898. $('#pps-sort').attr('src', settings.enableSort ? imgOn : imgOff).addClass(settings.enableSort ? 'on' : 'off').css('cursor: pointer');
  2899. $('#pps-anime').attr('src', settings.enableAnimeDownload ? imgOn : imgOff).addClass(settings.enableAnimeDownload ? 'on' : 'off').css('cursor: pointer');
  2900. $('#pps-original').attr('src', settings.original ? imgOn : imgOff).addClass(settings.original ? 'on' : 'off').css('cursor: pointer');
  2901. $('#pps-previewDelay').val(settings.previewDelay == null ? g_defaultSettings.previewDelay : settings.previewDelay);
  2902. $('#pps-maxPage').val(settings.pageCount == null ? g_defaultSettings.pageCount : settings.pageCount);
  2903. $('#pps-hideLess').val(settings.favFilter == null ? g_defaultSettings.favFilter : settings.favFilter);
  2904. $('#pps-hideBookmarked').attr('src', settings.hideFavorite ? imgOn : imgOff).addClass(settings.hideFavorite ? 'on' : 'off').css('cursor: pointer');
  2905. $('#pps-hideFollowed').attr('src', settings.hideFollowed ? imgOn : imgOff).addClass(settings.hideFollowed ? 'on' : 'off').css('cursor: pointer');
  2906. $('#pps-newTab').attr('src', settings.linkBlank ? imgOn : imgOff).addClass(settings.linkBlank ? 'on' : 'off').css('cursor: pointer');
  2907. $('#pps-pageKey').attr('src', settings.pageByKey ? imgOn : imgOff).addClass(settings.pageByKey ? 'on' : 'off').css('cursor: pointer');
  2908. $('#pps-fullSizeThumb').attr('src', settings.fullSizeThumb ? imgOn : imgOff).addClass(settings.fullSizeThumb ? 'on' : 'off').css('cursor: pointer');
  2909.  
  2910. $('#pps-lang')
  2911. .append('<option value="-1">Auto</option>')
  2912. .append('<option value="' + Lang.zh_CN + '">简体中文</option>')
  2913. .append('<option value="' + Lang.en_US + '">English</option>')
  2914. .append('<option value="' + Lang.ru_RU + '">Русский язык</option>')
  2915. .val(g_settings.lang == undefined ? Lang.auto : g_settings.lang);
  2916.  
  2917. $('#pps-ul').find('img').click(function () {
  2918. let _this = $(this);
  2919.  
  2920. if (_this.hasClass('on')) {
  2921. _this.attr('src', imgOff).removeClass('on').addClass('off');
  2922. } else {
  2923. _this.attr('src', imgOn).removeClass('off').addClass('on');
  2924. }
  2925. });
  2926. $('#pps-clearFollowingCache').click(function() {
  2927. let user_id = dataLayer[0].user_id;
  2928. SetCookie('followingOfUid-' + user_id, null, -1);
  2929. alert(Texts[g_language].setting_followingCacheCleared);
  2930. });
  2931.  
  2932. $('#pps-save').click(function () {
  2933. if ($('#pps-maxPage').val() === '') {
  2934. $('#pps-maxPage').val(g_defaultSettings.pageCount);
  2935. }
  2936. if ($('#pps-hideLess').val() == '') {
  2937. $('#pps-hideLess').val(g_defaultSettings.favFilter);
  2938. }
  2939.  
  2940. let settings = {
  2941. 'lang': $('#pps-lang').val(),
  2942. 'enablePreview': $('#pps-preview').hasClass('on') ? 1 : 0,
  2943. 'enableSort': $('#pps-sort').hasClass('on') ? 1 : 0,
  2944. 'enableAnimeDownload': $('#pps-anime').hasClass('on') ? 1 : 0,
  2945. 'original': $('#pps-original').hasClass('on') ? 1 : 0,
  2946. 'previewDelay': parseInt($('#pps-previewDelay').val()),
  2947. 'pageCount': parseInt($('#pps-maxPage').val()),
  2948. 'favFilter': parseInt($('#pps-hideLess').val()),
  2949. 'hideFavorite': $('#pps-hideBookmarked').hasClass('on') ? 1 : 0,
  2950. 'hideFollowed': $('#pps-hideFollowed').hasClass('on') ? 1 : 0,
  2951. 'linkBlank': $('#pps-newTab').hasClass('on') ? 1 : 0,
  2952. 'pageByKey': $('#pps-pageKey').hasClass('on') ? 1 : 0,
  2953. 'fullSizeThumb': $('#pps-fullSizeThumb').hasClass('on') ? 1 : 0,
  2954. 'version': g_version,
  2955. }
  2956.  
  2957. SetCookie('PixivPreview', settings);
  2958.  
  2959. location.href = location.href;
  2960. });
  2961.  
  2962. $('#pps-reset').click(function () {
  2963. let comfirmText = Texts[g_language].setting_resetHint;
  2964. if (confirm(comfirmText)) {
  2965. SetCookie('PixivPreview', null);
  2966. location.href = location.href;
  2967. }
  2968. });
  2969.  
  2970. $('#pps-close').click(function () {
  2971. $('#pp-bg').remove();
  2972. });
  2973.  
  2974. if (screenWidth < 1400) {
  2975. let fontSize = parseInt(25 - (1400 - screenWidth) / 40);
  2976. $('#pp-bg').find('li').css('font-size', fontSize + 'px');
  2977. }
  2978. }
  2979. function SetTargetBlank(returnMap) {
  2980. if (g_settings.linkBlank) {
  2981. let target = [];
  2982. $.each(returnMap.controlElements, function(i, e) {
  2983. if (e.tagName == 'A') {
  2984. target.push(e);
  2985. }
  2986. });
  2987.  
  2988. $.each($(returnMap.controlElements).find('a'), function(i, e) {
  2989. target.push(e);
  2990. });
  2991.  
  2992. $.each(target, function(i, e) {
  2993. $(e).attr({'target': '_blank', 'rel': 'external'});
  2994. // 主页这里用的是js监听跳转,特殊处理
  2995. if (g_pageType == PageType.Home || g_pageType == PageType.Member || g_pageType == PageType.Artwork) {
  2996. e.addEventListener("click", function(ev) {
  2997. ev.stopPropagation();
  2998. })
  2999. }
  3000. });
  3001. }
  3002. }
  3003. /* --------------------------------------- 主函数 --------------------------------------- */
  3004. let loadInterval = null;
  3005. let itv = null;
  3006. function Load() {
  3007. // 匹配当前页面
  3008. for (let i = 0; i < PageType.PageTypeCount; i++) {
  3009. if (Pages[i].CheckUrl(location.href)) {
  3010. g_pageType = i;
  3011. break;
  3012. }
  3013. }
  3014. if (g_pageType >= 0) {
  3015. DoLog(LogLevel.Info, 'Current page is ' + Pages[g_pageType].PageTypeString);
  3016. } else {
  3017. DoLog(LogLevel.Info, 'Unsupported page.');
  3018. clearInterval(loadInterval);
  3019. return;
  3020. }
  3021.  
  3022. // 设置按钮
  3023. let toolBar = Pages[g_pageType].GetToolBar();
  3024. if (toolBar) {
  3025. DoLog(LogLevel.Elements, toolBar);
  3026. clearInterval(loadInterval);
  3027. } else {
  3028. DoLog(LogLevel.Warning, 'Get toolbar failed.');
  3029. return;
  3030. }
  3031.  
  3032. window.onresize = function() {
  3033. if ($('#pps-save').length > 0) {
  3034. let screenWidth = document.documentElement.clientWidth;
  3035. let screenHeight = document.documentElement.clientHeight;
  3036. $('#pp-bg').css({'width': screenWidth + 'px', 'height': screenHeight + 'px'});
  3037.  
  3038. if (screenWidth < 1400) {
  3039. let fontSize = parseInt(25 - (1400 - screenWidth) / 40);
  3040. $('#pp-bg').find('li').css('font-size', fontSize + 'px');
  3041. }
  3042. }
  3043. };
  3044.  
  3045. if ($('#pp-settings').length == 0) {
  3046. toolBar.appendChild(toolBar.firstChild.cloneNode(true));
  3047. 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>';
  3048. $(toolBar.lastChild).css('margin-top', '10px');
  3049. $(toolBar.lastChild).css('opacity', '0.8');
  3050. $(toolBar.lastChild).click(function () {
  3051. ShowSetting();
  3052. });
  3053. }
  3054.  
  3055. // 读取设置
  3056. g_settings = GetSettings();
  3057.  
  3058. // 自动检测语言
  3059. g_language = g_settings.lang == undefined ? Lang.auto : g_settings.lang;
  3060. if (g_language == Lang.auto) {
  3061. let lang = $('html').attr('lang');
  3062. if (lang && lang.indexOf('zh') != -1) {
  3063. // 简体中文和繁体中文都用简体中文
  3064. g_language = Lang.zh_CN;
  3065. } else {
  3066. // 其他的统一用英语,其他语言也不知道谷歌翻译得对不对
  3067. g_language = Lang.en_US;
  3068. }
  3069. }
  3070.  
  3071. // g_csrfToken
  3072. if (g_pageType == PageType.Search) {
  3073. $.get(location.href, function (data) {
  3074. let matched = data.match(/token":"([a-z0-9]{32})/);
  3075. if (matched.length > 0) {
  3076. g_csrfToken = matched[1];
  3077. DoLog(LogLevel.Info, 'Got g_csrfToken: ' + g_csrfToken);
  3078. } else {
  3079. DoLog(LogLevel.Error, 'Can not get g_csrfToken, so you can not add works to bookmark when sorting has enabled.');
  3080. }
  3081. });
  3082. }
  3083.  
  3084. // 排序、预览
  3085. itv = setInterval(function () {
  3086. let returnMap = Pages[g_pageType].ProcessPageElements();
  3087. if (!returnMap.loadingComplete) {
  3088. return;
  3089. }
  3090.  
  3091. DoLog(LogLevel.Info, 'Process page comlete, sorting and prevewing begin.');
  3092. DoLog(LogLevel.Elements, returnMap);
  3093.  
  3094. clearInterval(itv);
  3095.  
  3096. SetTargetBlank(returnMap);
  3097.  
  3098. try {
  3099. if (g_pageType == PageType.Artwork) {
  3100. Pages[g_pageType].Work();
  3101. if (g_settings.enablePreview) {
  3102. PixivPreview();
  3103. }
  3104. }
  3105. else if (g_pageType == PageType.Search) {
  3106. if (g_settings.enableSort) {
  3107. g_sortComplete = false;
  3108. PixivSK(function() {
  3109. g_sortComplete = true;
  3110. if (g_settings.enablePreview) {
  3111. PixivPreview();
  3112. }
  3113. });
  3114. } else if (g_settings.enablePreview) {
  3115. PixivPreview();
  3116. }
  3117. } else if (g_settings.enablePreview) {
  3118. PixivPreview();
  3119. }
  3120. }
  3121. catch (e) {
  3122. DoLog(LogLevel.Error, 'Unknown error: ' + e);
  3123. }
  3124. }, 500);
  3125. }
  3126. loadInterval = setInterval(Load, 1000);
  3127. setInterval(function() {
  3128. if (location.href != initialUrl) {
  3129. // 排序中点击搜索tag,可能导致进行中的排序出现混乱,加取消太麻烦,直接走刷新
  3130. if (!g_sortComplete) {
  3131. location.href = location.href;
  3132. return;
  3133. }
  3134. // fix 主页预览图出现后点击图片,进到详情页,预览图不消失的问题
  3135. if ($('.pp-main').length > 0) {
  3136. $('.pp-main').remove();
  3137. }
  3138. initialUrl = location.href;
  3139. clearInterval(loadInterval);
  3140. clearInterval(itv);
  3141. clearInterval(autoLoadInterval);
  3142. autoLoadInterval = null;
  3143. g_pageType = -1;
  3144. loadInterval = setInterval(Load, 300);
  3145. }
  3146. }, 1000);