SmartNicorepo

ニコレポの「投稿」以外をデフォルトで折りたたむ & お気に入りユーザーに最終更新を表示

当前为 2017-06-03 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name SmartNicorepo
  3. // @namespace https://github.com/segabito/
  4. // @description ニコレポの「投稿」以外をデフォルトで折りたたむ & お気に入りユーザーに最終更新を表示
  5. // @include http://www.nicovideo.jp/my/*
  6. // @include http://www.nicovideo.jp/user/*
  7. // @include http://www.nicovideo.jp/my/fav/user
  8. // @include http://www.nicovideo.jp/mylist/*
  9. // @version 2.4.4
  10. // @grant none
  11. // @noframes
  12. // @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. var monkey =
  17. (function() {
  18. var $ = window.jQuery;
  19.  
  20. function addStyle(styles, id) {
  21. var elm = document.createElement('style');
  22. elm.type = 'text/css';
  23. if (id) { elm.id = id; }
  24.  
  25. var text = styles.toString();
  26. text = document.createTextNode(text);
  27. elm.appendChild(text);
  28. var head = document.getElementsByTagName('head');
  29. head = head[0];
  30. head.appendChild(elm);
  31. return elm;
  32. }
  33.  
  34. var __nicorepocss__ = (`
  35. .nicorepo .log.log-user-upload,
  36. .nicorepo .log.log-user-video-upload {
  37. background: #ffe;
  38. }
  39. .nicorepo .log.log-user-upload .log-target-thumbnail,
  40. .nicorepo .log.log-user-video-upload .log-target-thumbnail {
  41. width: auto; height: auto; margin-left: -30px;
  42. }
  43. .nicorepo .log.log-user-upload .video,
  44. .nicorepo .log.log-user-video-upload .video,
  45. .nicorepo .log.log-user-upload .seiga_illust,
  46. .nicorepo .log.log-user-upload .seiga_image,
  47. .nicorepo .log.log-user-upload .manga_episode
  48. {
  49. height: auto !important;
  50. width: 130px !important;
  51. margin-top: 0px;
  52. margin-bottom: 0 !important;
  53. margin-left: 0 !important;
  54. }
  55. #nicorepo .timeline > .log {
  56. max-height: 500px;
  57. transition: max-height 0.2s ease-in-out 0.2s;
  58. overflow: auto;
  59. }
  60. #nicorepo.show-upload-only .timeline > .log:not(.log-user-upload).log:not(.log-user-video-upload) {
  61. max-height: 22px;
  62. overflow: hidden;
  63. }
  64. #nicorepo .timeline > .log:not(.log-user-upload):not(.log-user-video-upload):hover {
  65. max-height: 500px !important;
  66. overflow: hidden;
  67. transition: max-height 0.4s ease-in-out 0.2s;
  68. }
  69. .toggleUpload {
  70. position: absolute;
  71. top: 0px;
  72. right: 16px;
  73. font-weight: bolder;
  74. cursor: pointer;
  75. color: #888;
  76. padding: 6px 8px;
  77. z-index: 1000;
  78. }
  79. .toggleUpload.bottom {
  80. top: auto; right: 32px; bottom: 32px;
  81. }
  82. .show-upload-only .toggleUpload {
  83. color: red;
  84. }
  85. .toggleUpload:after {
  86. content: ': OFF';
  87. }
  88. .show-upload-only .toggleUpload:after {
  89. content: ': ON';
  90. }
  91.  
  92. .togglePagerize {
  93. position: fixed;
  94. bottom: 0;
  95. right: 0;
  96. color: #888;
  97. font-weight: bolder;
  98. cursor: pointer;
  99. border: 2px solid #666;
  100. }
  101. .togglePagerize.enable {
  102. color: red;
  103. }
  104. .togglePagerize:after {
  105. content: ': OFF';
  106. }
  107. .togglePagerize.enable:after {
  108. content: ': ON';
  109. }
  110.  
  111. `).trim();
  112.  
  113. var __favusercss__ = (`
  114.  
  115. #favUser .outer.updating {
  116. }
  117. #favUser .outer.updating * {
  118. cursor: wait;
  119. }
  120. #favUser .outer.done .showNicorepo {
  121. display: none;
  122. }
  123.  
  124. #favUser .nicorepo {
  125. color: #800;
  126. clear: both;
  127. margin-bottom: 24px;
  128. }
  129. #favUser .uploadVideoList, #favUser .seigaUserPage {
  130. font-size: 80%;
  131. margin-left: 16px;
  132. }
  133.  
  134. #favUser .nicorepo.fail {
  135. color: #800;
  136. clear: both;
  137. margin-left: 64px;
  138. }
  139.  
  140.  
  141. #favUser .nicorepo.success {
  142. padding: 8px;
  143. overflow: auto;
  144. border: 1px inset;
  145. max-height: 300px;
  146. }
  147.  
  148. .nicorepo .log-target-thumbnail,
  149. .nicorepo .log-target-info {
  150. display: inline-block;
  151. vertical-align: middle;
  152. }
  153. .nicorepo .log-target-thumbnail .imageContainer {
  154. width: 64px;
  155. height: 48px;
  156. background-color: #fff;
  157. background-size: contain;
  158. background-repeat: no-repeat;
  159. background-position: center;
  160. transition: 0.2s width ease 0.4s, 0.2s height ease 0.4s;
  161. }
  162. .nicorepo .log-target-thumbnail .imageContainer:hover {
  163. width: 128px;
  164. height: 96px;
  165. }
  166. .nicorepo .log-target-info .time {
  167. display: block;
  168. font-size: 80%;
  169. color: black;
  170. }
  171. .nicorepo .log-target-info .logComment {
  172. display: block;
  173. font-size: 80%;
  174. color: black;
  175. }
  176. .nicorepo .log-target-info .logComment:before {
  177. content: '「';
  178. }
  179. .nicorepo .log-target-info .logComment:after {
  180. content: '」';
  181. }
  182. .nicorepo .log-target-info a {
  183. display: inline-block;
  184. min-width: 100px;
  185. }
  186. .nicorepo .log-target-info a:hover {
  187. background: #ccf;
  188. }
  189.  
  190.  
  191. .nicorepo .log.log-user-video-round-number-of-view-counter {
  192. display: none;
  193. }
  194.  
  195. .nicorepo .log-content {
  196. margin: 4px 8px;
  197. position: relative;
  198. }
  199. .nicorepo .log-footer {
  200. position: absolute;
  201. top: 0;
  202. left: 138px;
  203. }
  204. .nicorepo .log-footer a {
  205. font-size: 80%;
  206. color: black;
  207. }
  208.  
  209. .nicorepo .log .time:after {
  210. background: #888;
  211. color: #fff;
  212. border-radius: 4px;
  213. display: inline-block;
  214. padding: 2px 4px;
  215. }
  216. .nicorepo .log.log-user-register-chblog .time:after,
  217. .nicorepo .log.log-user-upload .time:after,
  218. .nicorepo .log.log-user-seiga-image-upload .time:after {
  219. content: '投稿';
  220. background: #866;
  221. }
  222.  
  223. .nicorepo .log.log-user-mylist-add-blomaga .time:after,
  224. .nicorepo .log.log-user-mylist-add .time:after {
  225. content: 'マイリスト';
  226. }
  227. .nicorepo .log.log-user-live-broadcast .time:after {
  228. content: '放送';
  229. }
  230. .nicorepo .log.log-user-seiga-image-clip .time:after {
  231. content: 'クリップ';
  232. }
  233. .nicorepo .log.log-user-video-review .time:after {
  234. content: 'レビュー';
  235. }
  236. .nicorepo .log.log-user-uad-advertise .time:after {
  237. content: '広告';
  238. }
  239.  
  240. .nicorepo .log.log-user-upload {
  241. background: #ffe;
  242. }
  243.  
  244. .nicorepo .log.log-user-upload .log-target-thumbnail,
  245. .nicorepo .log.log-user-seiga-image-upload .log-target-thumbnail {
  246. }
  247. .nicorepo .log.log-user-upload .video,
  248. .nicorepo .log.log-user-seiga-image-upload .seiga_image {
  249. }
  250.  
  251.  
  252. .nicorepo .log-author,
  253. .nicorepo .log-body,
  254. .nicorepo .log-res,
  255. .nicorepo .log-comment,
  256. .nicorepo .log-footer {
  257. display: none !important;
  258. }
  259. `).trim();
  260.  
  261. var __large_thumbnail_css__ = (`
  262.  
  263. .largeThumbnailLink {
  264. display: inline-block;
  265. }
  266.  
  267. .largeThumbnailLink::after {
  268. content: "";
  269. position: fixed;
  270. bottom: -280px;
  271. width: 360px;
  272. height: 270px;
  273. left: 24px;
  274. opacity: 0;
  275. background-color: #fff;
  276. background-size: contain;
  277. background-repeat: no-repeat;
  278. background-position: center center;
  279. transition:
  280. bottom 0.5s ease 0.5s,
  281. z-index 0.5s ease,
  282. box-shadow 0.5s ease 0.5s,
  283. opacity 0.5s ease 0.5s;
  284. z-index: 100000;
  285. pointer-events: none;
  286. }
  287.  
  288. #PAGEBODY .largeThumbnailLink::after {
  289. left: auto;
  290. right: 24px;
  291. }
  292.  
  293. .largeThumbnailLink:hover::after {
  294. position: fixed;
  295. bottom: 32px;
  296. opacity: 1;
  297. box-shadow: 4px 4px 4px #333;
  298. transition:
  299. bottom 0.2s ease,
  300. z-index 0.2s ease,
  301. box-shadow 0.2s ease,
  302. opacity 0.2s ease;
  303. z-index: 100100;
  304. }
  305. `).trim();
  306.  
  307. var __tagrepocss__ = (`
  308. .newVideoChannel .post-item,
  309. .newVideoUser .post-item {
  310. {* background: #ffe;*}
  311. }
  312.  
  313. .newLiveChannel .post-item,
  314. .newLiveUser .post-item {
  315. background: #eee;
  316. }
  317.  
  318. .newVideoUser .contents-thumbnail img.largeThumbnail,
  319. .newVideoOfficial .contents-thumbnail img.largeThumbnail,
  320. .newVideoChannel .contents-thumbnail img.largeThumbnail {
  321. margin-top: -21px;
  322. }
  323.  
  324. `).trim();
  325.  
  326. var failedUrl = {};
  327. var initializeLargeThumbnail = function(type, container, selector) {
  328. console.log('%cinitializeLargeThumbnail: type=%s', 'background: lightgreen;', type);
  329. addStyle(__large_thumbnail_css__);
  330.  
  331. // 大サムネが存在する最初の動画ID。 ソースはちゆ12歳
  332. // ※この数字以降でもごく稀に例外はある。
  333. var threthold = 16371888;
  334. var hasLargeThumbnail = function(videoId) { // return true;
  335. var cid = videoId.substr(0, 2);
  336. if (cid !== 'sm') { return false; }
  337.  
  338. var fid = videoId.substr(2) * 1;
  339. if (fid < threthold) { return false; }
  340.  
  341. return true;
  342. };
  343.  
  344. var onLoadImageError = function() {
  345. console.log('%c large thumbnail load error!', 'background: red;', this);
  346. var src = this.src.replace('.L', '');
  347. var $this = $(this);
  348. $this.off('error').addClass('large-thumbnail-fail');
  349.  
  350. failedUrl[src] = true;
  351. if (this.src !== src) {
  352. this.src = src;
  353. $this
  354. .removeClass('largeThumbnail')
  355. .closest('a')
  356. .removeClass('largeThumbnailLink');
  357. }
  358. };
  359.  
  360. var updatedItems = [];
  361. var each = function(i, v) {
  362. //console.log(i, v); //return;
  363. var href = v.href;
  364. if (typeof href !== 'string' || href.toString().indexOf('/watch/sm') < 0) {
  365. return;
  366. }
  367.  
  368. var videoId = href.replace(/^.+(sm\d+).*$/, '$1');
  369.  
  370. if (!hasLargeThumbnail(videoId)) {
  371. return;
  372. }
  373.  
  374. var $this = $(v);
  375. var $thumbnail = $this.find('img');
  376. var src = $thumbnail.attr('src');
  377. var org = $thumbnail.attr('data-original');
  378. var attr = org ? 'data-original' : 'src';
  379. src = org ? org : src;
  380.  
  381. //console.log('', attr, src, org);
  382.  
  383. if ($this.hasClass('large-thumbnail-fail')) { return; }
  384.  
  385. if (failedUrl[src]) { return; }
  386.  
  387. if (src && src.indexOf('.L') < 0 && src.indexOf('/smile?i=') > 0) {
  388. var url = src.replace('.M', '') + '.L';
  389. if (failedUrl[url]) { return; }
  390. $thumbnail
  391. .on('error', onLoadImageError)
  392. .addClass('largeThumbnail ' + videoId)
  393. .attr(attr, url);
  394.  
  395. $this.addClass('largeThumbnailLink ' + videoId);
  396. updatedItems.push([videoId, url]);
  397. }
  398. };
  399.  
  400. var cssExist = {};
  401. var updateCss = function(items) {
  402. if (items.length < 1) { return; }
  403. var css = [];
  404. for (var i = 0, len = items.length; i < len; i++) {
  405. var videoId = items[i][0], src = items[i][1];
  406. if (cssExist[videoId]) {
  407. continue;
  408. }
  409. cssExist[videoId] = true;
  410. css.push([
  411. '.largeThumbnailLink.', videoId, '::after {',
  412. ' background-image: url(', src, ');',
  413. // ' content: url(', src, ');',
  414. '}',
  415. '\n'].join(''));
  416. }
  417. if (css.length > 0) {
  418. addStyle(css.join(''));
  419. }
  420. };
  421.  
  422. var delayTimer;
  423. var update = function() {
  424. if (delayTimer) {
  425. clearTimeout(delayTimer);
  426. }
  427.  
  428. delayTimer = setTimeout(function() {
  429. //console.log('%cupdate large thumbnail', 'background: lightgreen;');
  430. updatedItems = [];
  431. $(selector).each(each);
  432. updateCss(updatedItems);
  433. delayTimer = null;
  434. }, 500);
  435. };
  436.  
  437. update();
  438.  
  439. $(container).on('DOMNodeInserted', update);
  440. };
  441.  
  442. var initializeSeigaThumbnail = function(type, container, selector) {
  443. console.log('%cinitializeSeigaThumbnail: type=%s', 'background: lightgreen;', type);
  444.  
  445. var onLoadImageError = function() {
  446. console.log('%c large thumbnail load error!', 'background: red;', this);
  447. this.src = this.src.replace(/i$/, 'z');
  448. $(this)
  449. .removeClass('largeThumbnail')
  450. .closest('a')
  451. .removeClass('largeThumbnailLink');
  452. };
  453.  
  454. var updatedItems = [];
  455. var each = function(i, v) {
  456. var href = v.href;
  457. if (typeof href !== 'string' || href.indexOf('/seiga/im') < 0) {
  458. //console.log(i, href, href.indexOf('/seiga/im'), typeof href , v);
  459. return;
  460. }
  461.  
  462. var seigaId = href.replace(/^.+(im\d+).*$/, '$1');
  463.  
  464. var $this = $(v);
  465. var $thumbnail = $this.find('img');
  466. var src = $thumbnail.attr('src');
  467. var org = $thumbnail.attr('data-original');
  468. var attr = org ? 'data-original' : 'src';
  469. src = org ? org : src;
  470.  
  471. if (src && src.match(/thumb\/\d+z$/)) {
  472. var url = src.replace(/z$/, 'i');
  473. $thumbnail
  474. .on('error', onLoadImageError)
  475. .addClass('largeThumbnail ' + seigaId)
  476. .attr(attr, url);
  477.  
  478. $this.addClass('largeThumbnailLink ' + seigaId);
  479. updatedItems.push([seigaId, url]);
  480. }
  481.  
  482. };
  483.  
  484. var cssExist = {};
  485. var updateCss = function(items) {
  486. if (items.length < 1) { return; }
  487. var css = [];
  488. for (var i = 0, len = items.length; i < len; i++) {
  489. var seigaId = items[i][0], src = items[i][1];
  490. if (cssExist[seigaId]) {
  491. continue;
  492. }
  493. cssExist[seigaId] = true;
  494. css.push([
  495. '.largeThumbnailLink.', seigaId, '::after {',
  496. ' background-image: url(', src, ');',
  497. '}',
  498. '\n'].join(''));
  499. }
  500. if (css.length > 0) {
  501. addStyle(css.join(''));
  502. }
  503. };
  504.  
  505. var delayTimer;
  506. var update = function() {
  507. if (delayTimer) {
  508. clearTimeout(delayTimer);
  509. }
  510.  
  511. delayTimer = setTimeout(function() {
  512. //console.log('%cupdate seiga thumbnail', 'background: lightgreen;');
  513. updatedItems = [];
  514. $(selector).each(each);
  515. updateCss(updatedItems);
  516. delayTimer = null;
  517. }, 500);
  518. };
  519.  
  520. update();
  521.  
  522. $(container).on('DOMNodeInserted', update);
  523. };
  524.  
  525.  
  526.  
  527. window.SmartNicorepo = {
  528. model: {},
  529. util: {},
  530. initialize: function() {
  531. this.initializeUserConfig();
  532. if (location.pathname === '/my/fav/user') {
  533. this.initializeFavUser();
  534. } else
  535. if (location.pathname.indexOf('/my/tagrepo/') === 0) {
  536. this.initializeTagrepo();
  537. } else {
  538. this.initializeNicorepo();
  539. this.initializeAutoPageRize();
  540. }
  541. },
  542. initializeUserConfig: function() {
  543. var prefix = 'SmartNicorepo_';
  544. var conf = {
  545. showUploadOnly: false,
  546. autoPagerize: true
  547. };
  548.  
  549. this.config = {
  550. get: function(key) {
  551. try {
  552. if (window.localStorage.hasOwnProperty(prefix + key)) {
  553. return JSON.parse(window.localStorage.getItem(prefix + key));
  554. }
  555. return conf[key];
  556. } catch (e) {
  557. return conf[key];
  558. }
  559. },
  560. set: function(key, value) {
  561. //console.log('%cupdate config {"%s": "%s"}', 'background: cyan', key, value);
  562. window.localStorage.setItem(prefix + key, JSON.stringify(value));
  563. }
  564. };
  565. },
  566. initializeNicorepo: function() {
  567. addStyle(__nicorepocss__, 'nicorepoCss');
  568.  
  569. let config = this.config;
  570. let $nicorepo = $('#nicorepo');
  571. let toggle = () => {
  572. $nicorepo.toggleClass('show-upload-only');
  573. config.set('showUploadOnly', $nicorepo.hasClass('show-upload-only'));
  574. };
  575.  
  576. //$nicorepo.on('dblblick', toggle);
  577. let $button = $('<button class="toggleUpload">投稿だけ表示</button>').click(toggle);
  578. let app;
  579. let ver = (app = document.querySelector('#MyPageNicorepoApp')) ? 'new' : 'old';
  580. //console.log('ver:', ver);
  581. const updateClass = () => {
  582. const query = ver === 'new' ?
  583. '.NicorepoTimelineItem .log-body strong' :
  584. '.log.log-user-video-upload .log-body, .log.log-user-seiga-image-upload .log-body';
  585. Array.from(document.querySelectorAll(query)).forEach(elm => {
  586. const log = elm.closest(ver === 'new' ? '.NicorepoTimelineItem' : '.log');
  587. if (!log) { return; }
  588. log.classList.add('log-user-upload');
  589. });
  590. };
  591.  
  592. const mutationObserver = new window.MutationObserver((mutations) => {
  593. let isAdded = false;
  594. mutations.forEach(mutation => {
  595. if (mutation.addedNodes && mutation.addedNodes.length > 0) {
  596. isAdded = true;
  597. }
  598. });
  599. if (isAdded) { updateClass(); }
  600. });
  601.  
  602. updateClass();
  603.  
  604. //console.log(app, document.querySelector(ver === 'new ' ? '#MyPageNicorepoApp' : '#nicorepo .timeline'));
  605. mutationObserver.observe(
  606. //document.querySelector('#MyPageNicorepoApp .nicorepo .timeline'),
  607. //document.querySelector('#nicorepo'),
  608. //document.querySelector(ver === 'new ' ? '#MyPageNicorepoApp' : '#nicorepo .timeline'),
  609. (app || document.querySelector('#nicorepo .timeline')),
  610. {childList: true, characterData: false, attributes: false, subtree: true}
  611. );
  612.  
  613. if (ver === 'new') {
  614. $('.setting-container').before($button);
  615. $('#MyPageNicorepoApp').after($button.clone(true).addClass('bottom'));
  616. } else {
  617. $('.timeline>*:first').before($button);
  618. $('.timeline>*:last').before($button.clone(true).addClass('bottom'));
  619. }
  620. $nicorepo.toggleClass('show-upload-only', config.get('showUploadOnly'));
  621. },
  622. initializeTagrepo: function() {
  623. console.log('%cinitializeTagrepo', 'background: lightgreen;');
  624. addStyle(__tagrepocss__, 'tagrepoCss');
  625. },
  626. initializeFavUser: function() {
  627. addStyle(__favusercss__, 'favUserCss');
  628. // this.loadFavUserList()
  629. // .then($.proxy(function(watchitems) {
  630. // console.log('%c ok:', 'background: #8f8;', watchitems.length);
  631. //
  632. // this._itemList = new window.SmartNicorepo.model.WatchItemList(watchitems);
  633. //
  634. // console.log('item list', this._itemList.getSortedItems());
  635. //
  636. // }, this));
  637. $('.posRight .arrow').each(function(i, elm) {
  638. var $elm = $(elm), $lnk = $elm.clone();
  639. $lnk
  640. .html('<span></span> ニコレポを表示&nbsp;')
  641. .addClass('showNicorepo');
  642. $elm.before($lnk);
  643. });
  644.  
  645. $('.outer .section a').each(function(i, elm) {
  646. var $elm = $(elm), href = $elm.attr('href');
  647. if (href.match(/\/(\d+)$/)) {
  648. var userId = RegExp.$1;
  649. var $video = $('<a class="uploadVideoList">動画一覧</a>')
  650. .attr('href', '/user/' + userId + '/video');
  651. var $seiga = $('<a class="seigaUserPage">静画一覧</a>')
  652. .attr('href', 'http://seiga.nicovideo.jp/user/illust/' + userId);
  653. $elm.after($seiga).after($video);
  654. }
  655. });
  656.  
  657. var getClearBusy = function($elm) {
  658. return function() {
  659. $elm.removeClass('updating').addClass('done');
  660. };
  661. };
  662.  
  663. $('#favUser .showNicorepo').off().on('click', $.proxy(function(e) {
  664. if (e.button !== 0 || e.metaKey || e.shiftKey || e.altKey || e.ctrlKey) {
  665. return;
  666. }
  667. e.preventDefault();
  668. e.stopPropagation();
  669. var $elm = $(e.target);
  670. var userId = $elm.attr('data-nico-nicorepolistid');
  671. if (!userId) { return; }
  672. var $outer = $elm.closest('.outer');
  673. if ($outer.hasClass('updating')) {
  674. return;
  675. }
  676.  
  677. var clearBusy = getClearBusy($outer);
  678. $outer.addClass('updating');
  679. window.setTimeout(clearBusy, 3000);
  680.  
  681. this.loadNicorepo(userId, $outer).then(clearBusy, clearBusy);
  682.  
  683. }, this));
  684. },
  685. initializeAutoPageRize: function() {
  686. let config = this.config;
  687. let $button = $('<button class="togglePagerize">自動読込</button>');
  688. let timer = null;
  689.  
  690. var onButtonClick = () => {
  691. toggle();
  692. updateView();
  693. };
  694. var toggle = () => {
  695. this._isAutoPagerizeEnable = !this._isAutoPagerizeEnable;
  696. config.set('autoPagerize', this._isAutoPagerizeEnable);
  697. if (this._isAutoPagerizeEnable) {
  698. bind();
  699. } else {
  700. unbind();
  701. }
  702. };
  703. let updateView = () => {
  704. $button.toggleClass('enable', this._isAutoPagerizeEnable);
  705. };
  706. let onWindowScroll = _.debounce(this._onWindowScroll.bind(this), 100);
  707. let bind = () => {
  708. $(window).on('scroll', onWindowScroll);
  709. timer = window.setInterval(this._autoPagerize.bind(this), 5000);
  710. };
  711. let unbind = () => {
  712. $(window).off('scroll', onWindowScroll);
  713. window.clearInterval(timer);
  714. };
  715.  
  716.  
  717. $button.click(onButtonClick);
  718. $('body').append($button);
  719.  
  720. this._isAutoPagerizeEnable = config.get('autoPagerize');
  721. if (this._isAutoPagerizeEnable) { bind(); }
  722.  
  723. updateView();
  724. },
  725. _onWindowScroll: function() {
  726. this._autoPagerize();
  727. },
  728. _autoPagerize: function() {
  729. if (!this._isAutoPagerizeEnable) { return; }
  730. //let ver = document.querySelector('#MyPageNicorepoApp') ? 'new' : 'old';
  731.  
  732. // TODO: IntersectionObserverつかえ
  733. let nextPage = //this._nextPage ||
  734. document.querySelector('#MyPageNicorepoApp .next-page, #nicorepo .next-page');
  735. if (!nextPage) { return; }
  736. //this._nextPage = nextPage;
  737.  
  738. let rect = nextPage.getBoundingClientRect();
  739. let isLoading = nextPage.classList.contains('loading');
  740. let isScrollIn = window.innerHeight - rect.top > 0;
  741. //console.info('?', isLoading, isScrollIn, window.innerHeight - rect.top);
  742. if (isScrollIn && !isLoading) {
  743. document.querySelector('#nicorepo .next-page-link').click();
  744. }
  745. },
  746. loadNicorepo: function(userId, $container) {
  747. // http://www.nicovideo.jp/user/[userId]/top?innerPage=1
  748. var url = 'http://www.nicovideo.jp/user/' + userId + '/top?innerPage=1';
  749.  
  750. var fail = function(msg) {
  751. var $fail = $('<div class="nicorepo fail">' + msg + '</div>');
  752. $container.append($fail);
  753. autoScrollIfNeed($fail);
  754. };
  755.  
  756. // ニコレポが画面の一番下よりはみ出していたら見える位置までスクロール
  757. var autoScrollIfNeed = function($target) {
  758. var
  759. scrollTop = $('html').scrollTop(),
  760. targetOffset = $target.offset(),
  761. clientHeight = $(window).innerHeight(),
  762. clientBottom = scrollTop + clientHeight,
  763. targetBottom = targetOffset.top + $target.outerHeight();
  764.  
  765. if (targetBottom > clientBottom) {
  766. $('html').animate({
  767. scrollTop: scrollTop + $target.outerHeight()
  768. }, 500);
  769. }
  770. };
  771.  
  772. var success = function($dom, $logBody) {
  773. var $result = $('<div class="nicorepo success" />');
  774. var $img = $logBody.find('img'), $log = $logBody.find('.log');
  775. $img.each(function() {
  776. var $this = $(this), $parent = $this.parent();
  777. var lazyImg = $this.attr('data-original');
  778. if (lazyImg) {
  779. var $imageContainer = $('<div class="imageContainer"/>');
  780. $imageContainer.css('background-image', 'url(' + lazyImg + ')');
  781. $this.before($imageContainer);
  782. $this.remove();
  783. }
  784. if (window.WatchItLater) {
  785. var href = $parent.attr('href');
  786. if (href) {
  787. $parent.attr('href', href.replace('http://www.nicovideo.jp/watch/', 'http://nico.ms/'));
  788. }
  789. }
  790. });
  791. $logBody.each(function() {
  792. var $this = $(this), time = $this.find('time:first').text(), logComment = $this.find('.log-comment').text();
  793.  
  794. $this.find('.log-target-info>*:first')
  795. .before($('<span class="time">' + time + '</span>'));
  796. if (logComment) {
  797. $this.find('.log-target-info')
  798. .append($('<span class="logComment">' + logComment + '</span>'));
  799. }
  800. });
  801.  
  802. $result.append($logBody);
  803. $container.append($result);
  804. $result.scrollTop(0);
  805.  
  806. autoScrollIfNeed($result);
  807. };
  808.  
  809. return $.ajax({
  810. url: url,
  811. timeout: 30000
  812. }).then(
  813. function(resp) {
  814. var
  815. $dom = $(resp),
  816. // 欲しいのはそのユーザーの「行動」なので、
  817. // xx再生やスタンプみたいなのはいらない
  818. $logBody = $dom.find('.log:not(.log-user-video-round-number-of-view-counter):not(.log-user-action-stamp):not(.log-user-live-video-introduced)');
  819. if ($logBody.length < 1) {
  820. fail('ニコレポが存在しないか、取得に失敗しました');
  821. } else {
  822. success($dom, $logBody);
  823. }
  824. },
  825. function() {
  826. fail('ニコレポの取得に失敗しました');
  827. });
  828.  
  829. },
  830. loadFavUserList: function() {
  831. var def = new $.Deferred();
  832. // このAPIのupdate_timeが期待していた物と違ったのでボツ
  833. // create_timeとupdate_timeはどちらも同じ値が入ってるだけだった。(なんのためにあるんだ?)
  834. //
  835. $.ajax({
  836. url: 'http://www.nicovideo.jp/api/watchitem/list',
  837. timeout: 30000,
  838. complete: function(resp) {
  839. var json;
  840. try {
  841. json = JSON.parse(resp.responseText);
  842. } catch (e) {
  843. console.log('%c parse error: ', 'background: #f88', e);
  844. return def.reject('json parse error');
  845. }
  846.  
  847. if (json.status !== 'ok') {
  848. console.log('%c status error: ', 'background: #f88', json.status);
  849. return def.reject('status error', json.status);
  850. }
  851. return def.resolve(json.watchitem);
  852. },
  853. error: function(req, status, thrown) {
  854. if (status === 'parsererror') {
  855. return;
  856. }
  857. console.log('%c ajax error: ' + status, 'background: #f88', thrown);
  858. return def.reject(status);
  859. }
  860. });
  861. return def.promise();
  862. }
  863.  
  864. };
  865.  
  866.  
  867. window.SmartNicorepo.model.WatchItem = function() { this.initialize.apply(this, arguments); };
  868. window.SmartNicorepo.model.WatchItem.prototype = {
  869. initialize: function(seed) {
  870. this._seed = seed;
  871. this.itemType = seed.item_type || '1';
  872. this.itemId = seed.item_id || '';
  873. if (typeof seed.item_data === 'object') {
  874. var data = seed.item_data;
  875. this.userId = data.id;
  876. this.nickname = data.nickname;
  877. this.thumbnailUrl = data.thumbnail_url;
  878. }
  879. var now = (new Date()).getTime();
  880. this.createTime = new Date(seed.create_time ? seed.create_time * 1000 : now);
  881. this.updateTime = new Date(seed.update_time ? seed.update_time * 1000 : now);
  882. }
  883. };
  884.  
  885. window.SmartNicorepo.model.WatchItemList = function() { this.initialize.apply(this, arguments); };
  886. window.SmartNicorepo.model.WatchItemList.prototype = {
  887. initialize: function(watchItems) {
  888. this._seed = watchItems;
  889. this._items = {};
  890. this._itemArray = [];
  891. for (var i = 0, len = watchItems.length; i < len; i++) {
  892. var item = new window.SmartNicorepo.model.WatchItem(watchItems[i]);
  893. this._items[item.userId] = item;
  894. this._itemArray.push(item);
  895. }
  896. },
  897. getItem: function(userId) {
  898. return this._items[userId];
  899. },
  900. getSortedItems: function() {
  901. var result = this._itemArray.concat();
  902. result.sort(function(a, b) {
  903. return (a.updateTime < b.updateTime) ? 1 : -1;
  904. });
  905. return result;
  906. }
  907. };
  908.  
  909. var removeQuery = function(container) {
  910. const reg = /(^\?nicorepo|ref=)/;
  911. $(container + ' a').each((i, a) => {
  912. const search = a.search || '';
  913. if (reg.test(search)) {
  914. a.search = '';
  915. }
  916. });
  917. };
  918.  
  919.  
  920. window.Nico.onReady(function() {
  921. console.log('%cNico.onReady', 'background: lightgreen;');
  922. if (location.pathname.indexOf('/my/top') === 0) {
  923. initializeLargeThumbnail('nicorepo', '.nicorepo', '.log-target-thumbnail a[href*=nicovideo.jp/watch/sm]:not(.largeThumbnailLink)');
  924. initializeSeigaThumbnail('nicorepo', '.nicorepo', '.log-target-thumbnail a:not(.largeThumbnailLink)');
  925. //initializeSeigaThumbnail('nicorepo', '.nicorepo', '.log-target-thumbnail a[href*=/seiga/im]:not(.largeThumbnailLink)');
  926. $('.nicorepo').on('DOMNodeInserted', _.debounce(() => {
  927. removeQuery('.nicorepo');
  928. }, 1000));
  929. } else
  930. if (location.pathname.indexOf('/my/mylist') === 0) {
  931. initializeLargeThumbnail('mylist', '#mylist', '.thumbContainer a:not(.largeThumbnailLink)');
  932. } else
  933. if (location.pathname.indexOf('/my/video') === 0) {
  934. initializeLargeThumbnail('video', '#video', '.thumbContainer a:not(.largeThumbnailLink)');
  935. } else
  936. if (location.pathname.indexOf('/mylist') === 0) {
  937. initializeLargeThumbnail('openMylist', '#PAGEBODY', '.SYS_box_item a:not(.watch):not(.largeThumbnailLink):visible');
  938. } else
  939. if (location.pathname.match(/\/user\/\d+\/video/)) {
  940. initializeLargeThumbnail('video', '#video', '.thumbContainer a:not(.largeThumbnailLink)');
  941. } else
  942. if (location.pathname.match(/\/user\/\d+\/top/)) {
  943. initializeLargeThumbnail('nicorepo', '.nicorepo', '.log-target-thumbnail a[href*=nicovideo.jp/watch/sm]:not(.largeThumbnailLink)');
  944. } else
  945. if (location.pathname.match(/\/my\/tagrepo\//)) {
  946. initializeLargeThumbnail('tagrepo', '#tagrepo', '.newVideoUser .contents-thumbnail a[href*=nicovideo.jp/watch/sm]:not(.largeThumbnailLink)');
  947. }
  948. });
  949.  
  950. if (location.pathname.indexOf('/mylist') < 0) {
  951. window.SmartNicorepo.initialize();
  952. }
  953.  
  954. }); // end of monkey
  955.  
  956. var gm = document.createElement('script');
  957. gm.id = 'smartNicorepoScript';
  958. gm.setAttribute("type", "text/javascript");
  959. gm.setAttribute("charset", "UTF-8");
  960. gm.appendChild(document.createTextNode("(" + monkey + ")(window)"));
  961. document.body.appendChild(gm);
  962.  
  963. })();