SmartNicorepo

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

当前为 2014-04-15 提交的版本,查看 最新版本

  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. // @version 2.0.1
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. var monkey =
  14. (function() {
  15. var $ = window.jQuery;
  16.  
  17. function addStyle(styles, id) {
  18. var elm = document.createElement('style');
  19. elm.type = 'text/css';
  20. if (id) { elm.id = id; }
  21.  
  22. var text = styles.toString();
  23. text = document.createTextNode(text);
  24. elm.appendChild(text);
  25. var head = document.getElementsByTagName('head');
  26. head = head[0];
  27. head.appendChild(elm);
  28. return elm;
  29. }
  30.  
  31. var __nicorepocss__ = (function() {/*
  32. .nicorepo .log.log-user-video-upload {
  33. background: #ffe;
  34. }
  35. .nicorepo .log.log-user-video-upload .log-target-thumbnail ,.nicorepo .log.log-user-seiga-image-upload .log-target-thumbnail {
  36. width: auto; margin-left: -30px;
  37. }
  38. .nicorepo .log.log-user-video-upload .video , .nicorepo .log.log-user-seiga-image-upload .seiga_image {
  39. height: auto !important; width: 130px !important; margin-top: 0px;
  40. }
  41. #nicorepo .timeline > .log {
  42. max-height: 500px;
  43. transition: max-height 0.4s ease-in-out;
  44. }
  45. #nicorepo.show-upload-only .timeline > .log:not(.log-user-video-upload):not(.log-user-seiga-image-upload):not(.log-user-register-chblog) {
  46. max-height: 22px;
  47. overflow: hidden;
  48. }
  49. #nicorepo .timeline > .log:not(.log-user-video-upload):not(.log-user-seiga-image-upload):not(.log-user-register-chblog):hover {
  50. max-height: 500px;
  51. overflow: hidden;
  52. transition: max-height 0.4s ease-in-out 0.8s;
  53. }
  54. .toggleUpload {
  55. position: absolute; top: 32px; right: 32px; font-weight: bolder; cursor: pointer; color: #888; padding: 8px;
  56. z-index: 1000;
  57. box-shadow: 2px 2px 2px #333;
  58. }
  59. .toggleUpload.bottom {
  60. top: auto; right: 32px; bottom: 32px;
  61. }
  62. .show-upload-only .toggleUpload {
  63. color: red;
  64. }
  65. .toggleUpload:after {
  66. content: ': OFF';
  67. }
  68. .show-upload-only .toggleUpload:after {
  69. content: ': ON';
  70. }
  71.  
  72. */}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1].replace(/\{\*/g, '/*').replace(/\*\}/g, '*/');
  73.  
  74. var __favusercss__ = (function() {/*
  75.  
  76. #favUser .outer.updating {
  77. }
  78. #favUser .outer.updating * {
  79. cursor: wait;
  80. }
  81. #favUser .outer.done .showNicorepo {
  82. display: none;
  83. }
  84.  
  85. #favUser .nicorepo {
  86. color: #800;
  87. clear: both;
  88. margin-bottom: 24px;
  89. }
  90. #favUser .uploadVideoList, #favUser .seigaUserPage {
  91. font-size: 80%;
  92. margin-left: 16px;
  93. }
  94.  
  95. #favUser .nicorepo.fail {
  96. color: #800;
  97. clear: both;
  98. margin-left: 64px;
  99. }
  100.  
  101.  
  102. #favUser .nicorepo.success {
  103. padding: 8px;
  104. overflow: auto;
  105. border: 1px inset;
  106. max-height: 300px;
  107. }
  108.  
  109. .nicorepo .log-target-thumbnail,
  110. .nicorepo .log-target-info {
  111. display: inline-block;
  112. vertical-align: middle;
  113. }
  114. .nicorepo .log-target-thumbnail .imageContainer {
  115. width: 64px;
  116. height: 48px;
  117. background-color: #fff;
  118. background-size: contain;
  119. background-repeat: no-repeat;
  120. background-position: center;
  121. transition: 0.2s width ease 0.4s, 0.2s height ease 0.4s;
  122. }
  123. .nicorepo .log-target-thumbnail .imageContainer:hover {
  124. width: 128px;
  125. height: 96px;
  126. }
  127. .nicorepo .log-target-info .time {
  128. display: block;
  129. font-size: 80%;
  130. color: black;
  131. }
  132. .nicorepo .log-target-info .logComment {
  133. display: block;
  134. font-size: 80%;
  135. color: black;
  136. }
  137. .nicorepo .log-target-info .logComment:before {
  138. content: '「';
  139. }
  140. .nicorepo .log-target-info .logComment:after {
  141. content: '」';
  142. }
  143. .nicorepo .log-target-info a {
  144. display: inline-block;
  145. min-width: 100px;
  146. }
  147. .nicorepo .log-target-info a:hover {
  148. background: #ccf;
  149. }
  150.  
  151.  
  152. .nicorepo .log.log-user-video-round-number-of-view-counter {
  153. display: none;
  154. }
  155.  
  156. .nicorepo .log-content {
  157. margin: 4px 8px;
  158. position: relative;
  159. }
  160. .nicorepo .log-footer {
  161. position: absolute;
  162. top: 0;
  163. left: 138px;
  164. }
  165. .nicorepo .log-footer a {
  166. font-size: 80%;
  167. color: black;
  168. }
  169.  
  170. .nicorepo .log .time:after {
  171. background: #888;
  172. color: #fff;
  173. border-radius: 4px;
  174. display: inline-block;
  175. padding: 2px 4px;
  176. }
  177. .nicorepo .log.log-user-register-chblog .time:after,
  178. .nicorepo .log.log-user-video-upload .time:after,
  179. .nicorepo .log.log-user-seiga-image-upload .time:after {
  180. content: '投稿';
  181. background: #866;
  182. }
  183.  
  184. .nicorepo .log.log-user-mylist-add-blomaga .time:after,
  185. .nicorepo .log.log-user-mylist-add .time:after {
  186. content: 'マイリスト';
  187. }
  188. .nicorepo .log.log-user-live-broadcast .time:after {
  189. content: '放送';
  190. }
  191. .nicorepo .log.log-user-seiga-image-clip .time:after {
  192. content: 'クリップ';
  193. }
  194. .nicorepo .log.log-user-video-review .time:after {
  195. content: 'レビュー';
  196. }
  197. .nicorepo .log.log-user-uad-advertise .time:after {
  198. content: '広告';
  199. }
  200.  
  201. .nicorepo .log.log-user-video-upload {
  202. background: #ffe;
  203. }
  204.  
  205. .nicorepo .log.log-user-video-upload .log-target-thumbnail,
  206. .nicorepo .log.log-user-seiga-image-upload .log-target-thumbnail {
  207. }
  208. .nicorepo .log.log-user-video-upload .video,
  209. .nicorepo .log.log-user-seiga-image-upload .seiga_image {
  210. }
  211.  
  212.  
  213. .nicorepo .log-author,
  214. .nicorepo .log-body,
  215. .nicorepo .log-res,
  216. .nicorepo .log-comment,
  217. .nicorepo .log-footer {
  218. display: none !important;
  219. }
  220. */}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1].replace(/\{\*/g, '/*').replace(/\*\}/g, '*/');
  221.  
  222. window.SmartNicorepo = {
  223. model: {},
  224. util: {},
  225. initialize: function() {
  226. if (location.pathname === '/my/fav/user') {
  227. this.initializeFavUser();
  228. } else {
  229. this.initializeNicorepo();
  230. }
  231. },
  232. initializeNicorepo: function() {
  233. addStyle(__nicorepocss__, 'nicorepoCss');
  234. $('#nicorepo')
  235. .toggleClass('show-upload-only')
  236. .dblclick(function() { $('#nicorepo').toggleClass('show-upload-only'); });
  237.  
  238. var $button = $('<button class="toggleUpload">投稿だけ表示</button>').click(
  239. function() {
  240. $('#nicorepo').toggleClass('show-upload-only');
  241. }
  242. );
  243.  
  244. $('.timeline>*:first').before($button);
  245. $('.timeline>*:last').before($button.clone(true).addClass('bottom'));
  246. },
  247. initializeFavUser: function() {
  248. addStyle(__favusercss__, 'favUserCss');
  249. // this.loadFavUserList()
  250. // .then($.proxy(function(watchitems) {
  251. // console.log('%c ok:', 'background: #8f8;', watchitems.length);
  252. //
  253. // this._itemList = new window.SmartNicorepo.model.WatchItemList(watchitems);
  254. //
  255. // console.log('item list', this._itemList.getSortedItems());
  256. //
  257. // }, this));
  258. $('.posRight .arrow').each(function(i, elm) {
  259. var $elm = $(elm), $lnk = $elm.clone();
  260. $lnk.html('<span></span> ニコレポを表示&nbsp;').addClass('showNicorepo');
  261. $elm.before($lnk);
  262. });
  263.  
  264. $('.outer .section a').each(function(i, elm) {
  265. var $elm = $(elm), href = $elm.attr('href');
  266. if (href.match(/\/(\d+)$/)) {
  267. var userId = RegExp.$1;
  268. var $video = $('<a class="uploadVideoList">動画一覧</a>')
  269. .attr('href', '/user/' + userId + '/video');
  270. var $seiga = $('<a class="seigaUserPage">静画一覧</a>')
  271. .attr('href', 'http://seiga.nicovideo.jp/user/illust/' + userId);
  272. $elm.after($seiga).after($video);
  273. }
  274. });
  275.  
  276. var getClearBusy = function($elm) {
  277. return function() {
  278. $elm.removeClass('updating').addClass('done');
  279. };
  280. };
  281.  
  282. $('#favUser .showNicorepo').on('click', $.proxy(function(e) {
  283. e.preventDefault();
  284. e.stopPropagation();
  285. var $elm = $(e.target);
  286. var userId = $elm.attr('data-nico-nicorepolistid');
  287. if (!userId) { return; }
  288. var $outer = $elm.closest('.outer');
  289. if ($outer.hasClass('updating')) {
  290. return;
  291. }
  292.  
  293. var clearBusy = getClearBusy($outer);
  294. $outer.addClass('updating');
  295. window.setTimeout(clearBusy, 3000);
  296.  
  297. this.loadNicorepo(userId, $outer).then(clearBusy, clearBusy);
  298.  
  299. }, this));
  300. },
  301. loadNicorepo: function(userId, $container) {
  302. // http://www.nicovideo.jp/user/[userId]/top?innerPage=1
  303. var url = 'http://www.nicovideo.jp/user/' + userId + '/top?innerPage=1';
  304.  
  305. var fail = function(msg) {
  306. var $fail = $('<div class="nicorepo fail">' + msg + '</div>');
  307. $container.append($fail);
  308. autoScrollIfNeed($fail);
  309. };
  310.  
  311. // ニコレポが画面の一番下よりはみ出していたら見える位置までスクロール
  312. var autoScrollIfNeed = function($target) {
  313. var
  314. scrollTop = $('html').scrollTop(),
  315. targetOffset = $target.offset(),
  316. clientHeight = $(window).innerHeight(),
  317. clientBottom = scrollTop + clientHeight,
  318. targetBottom = targetOffset.top + $target.outerHeight();
  319.  
  320. if (targetBottom > clientBottom) {
  321. $('html').animate({
  322. scrollTop: scrollTop + $target.outerHeight()
  323. }, 500);
  324. }
  325. };
  326.  
  327. var success = function($dom, $logBody) {
  328. var $result = $('<div class="nicorepo success" />');
  329. var $img = $logBody.find('img'), $log = $logBody.find('.log');
  330. $img.each(function() {
  331. var $this = $(this), $parent = $this.parent();
  332. var lazyImg = $this.attr('data-src');
  333. if (lazyImg) {
  334. var $imageContainer = $('<div class="imageContainer"/>');
  335. $imageContainer.css('background-image', 'url(' + lazyImg + ')');
  336. $this.before($imageContainer);
  337. $this.remove();
  338. }
  339. if (window.WatchItLater) {
  340. var href = $parent.attr('href');
  341. if (href) {
  342. $parent.attr('href', href.replace('http://www.nicovideo.jp/watch/', 'http://nico.ms/'));
  343. }
  344. }
  345. });
  346. $logBody.each(function() {
  347. var $this = $(this), time = $this.find('time:first').text(), logComment = $this.find('.log-comment').text();
  348.  
  349. $this.find('.log-target-info>*:first')
  350. .before($('<span class="time">' + time + '</span>'));
  351. if (logComment) {
  352. $this.find('.log-target-info')
  353. .append($('<span class="logComment">' + logComment + '</span>'));
  354. }
  355. });
  356.  
  357. $result.append($logBody);
  358. $container.append($result);
  359. $result.scrollTop(0);
  360.  
  361. autoScrollIfNeed($result);
  362. };
  363.  
  364. return $.ajax({
  365. url: url,
  366. timeout: 30000
  367. }).then(
  368. function(resp) {
  369. var
  370. $dom = $(resp),
  371. // 欲しいのはそのユーザーの「行動」なので、
  372. // xx再生やスタンプみたいなのはいらない
  373. $logBody = $dom.find('.log:not(.log-user-video-round-number-of-view-counter):not(.log-user-action-stamp):not(.log-user-live-video-introduced)');
  374. if ($logBody.length < 1) {
  375. fail('ニコレポが存在しないか、取得に失敗しました');
  376. } else {
  377. success($dom, $logBody);
  378. }
  379. },
  380. function() {
  381. fail('ニコレポの取得に失敗しました');
  382. });
  383.  
  384. },
  385. loadFavUserList: function() {
  386. var def = new $.Deferred();
  387. // このAPIのupdate_timeが期待していた物と違ったのでボツ
  388. // create_timeとupdate_timeはどちらも同じ値が入ってるだけだった。(なんのためにあるんだ?)
  389. //
  390. $.ajax({
  391. url: 'http://www.nicovideo.jp/api/watchitem/list',
  392. timeout: 30000,
  393. complete: function(resp) {
  394. var json;
  395. try {
  396. json = JSON.parse(resp.responseText);
  397. } catch (e) {
  398. console.log('%c parse error: ', 'background: #f88', e);
  399. return def.reject('json parse error');
  400. }
  401.  
  402. if (json.status !== 'ok') {
  403. console.log('%c status error: ', 'background: #f88', json.status);
  404. return def.reject('status error', json.status);
  405. }
  406. return def.resolve(json.watchitem);
  407. },
  408. error: function(req, status, thrown) {
  409. if (status === 'parsererror') {
  410. return;
  411. }
  412. console.log('%c ajax error: ' + status, 'background: #f88', thrown);
  413. return def.reject(status);
  414. }
  415. });
  416. return def.promise();
  417. }
  418.  
  419. };
  420.  
  421.  
  422. window.SmartNicorepo.model.WatchItem = function() { this.initialize.apply(this, arguments); };
  423. window.SmartNicorepo.model.WatchItem.prototype = {
  424. initialize: function(seed) {
  425. this._seed = seed;
  426. this.itemType = seed.item_type || '1';
  427. this.itemId = seed.item_id || '';
  428. if (typeof seed.item_data === 'object') {
  429. var data = seed.item_data;
  430. this.userId = data.id;
  431. this.nickname = data.nickname;
  432. this.thumbnailUrl = data.thumbnail_url;
  433. }
  434. var now = (new Date()).getTime();
  435. this.createTime = new Date(seed.create_time ? seed.create_time * 1000 : now);
  436. this.updateTime = new Date(seed.update_time ? seed.update_time * 1000 : now);
  437. }
  438. };
  439.  
  440. window.SmartNicorepo.model.WatchItemList = function() { this.initialize.apply(this, arguments); };
  441. window.SmartNicorepo.model.WatchItemList.prototype = {
  442. initialize: function(watchItems) {
  443. this._seed = watchItems;
  444. this._items = {};
  445. this._itemArray = [];
  446. for (var i = 0, len = watchItems.length; i < len; i++) {
  447. var item = new window.SmartNicorepo.model.WatchItem(watchItems[i]);
  448. this._items[item.userId] = item;
  449. this._itemArray.push(item);
  450. }
  451. },
  452. getItem: function(userId) {
  453. return this._items[userId];
  454. },
  455. getSortedItems: function() {
  456. var result = this._itemArray.concat();
  457. result.sort(function(a, b) {
  458. return (a.updateTime < b.updateTime) ? 1 : -1;
  459. });
  460. return result;
  461. }
  462. };
  463.  
  464. window.SmartNicorepo.initialize();
  465.  
  466. }); // end of monkey
  467.  
  468. var gm = document.createElement('script');
  469. gm.id = 'smartNicorepoScript';
  470. gm.setAttribute("type", "text/javascript");
  471. gm.setAttribute("charset", "UTF-8");
  472. gm.appendChild(document.createTextNode("(" + monkey + ")(window)"));
  473. document.body.appendChild(gm);
  474.  
  475. })();