WatchItLater

動画を見る前にマイリストに登録したいGreasemonkey (Chrome/Fx用)

目前為 2014-06-08 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name WatchItLater
  3. // @namespace https://github.com/segabito/
  4. // @description 動画を見る前にマイリストに登録したいGreasemonkey (Chrome/Fx用)
  5. // @include http://www.nicovideo.jp/*
  6. // @include http://i.nicovideo.jp/*
  7. // @include http://ch.nicovideo.jp/*
  8. // @include http://ext.nicovideo.jp/thumb/*
  9. // @exclude http://ads*.nicovideo.jp/*
  10. // @exclude http://live*.nicovideo.jp/*
  11. // @exclude http://dic.nicovideo.jp/*
  12. // @exclude http://www.upload.nicovideo.jp/*
  13. // @exclude http://upload.nicovideo.jp/*
  14. // @exclude http://ch.nicovideo.jp/tool/*
  15. // @exclude http://flapi.nicovideo.jp/*
  16. // @match http://www.nicovideo.jp/*
  17. // @match http://ch.nicovideo.jp/*
  18. // @match http://i.nicovideo.jp/*
  19. // @match http://*.nicovideo.jp/*
  20. // @match http://ext.nicovideo.jp/*
  21. // @match http://search.nicovideo.jp/*
  22. // @grant GM_xmlhttpRequest
  23. // @version 1.140607
  24. // ==/UserScript==
  25.  
  26. /**
  27. *
  28. *
  29. * やりたい事・アイデア
  30. * ・検索画面にコミュニティ動画一覧を表示 (チャンネルより難しい。力技でなんとかする)
  31. * ・シークバーのサムネイルを並べて表示するやつ (単体スクリプトのほうがよさそう)
  32. * ・キーワード検索/タグ検索履歴の改修
  33. * ・ユーザーの投稿動画一覧に「xxさんのニコレポ」という架空のマイリストフォルダを出す
  34. * ・動画ランキングを「ニコニコ動画さん」という架空ユーザーの公開マイリストにする
  35. * ・「ニコニコチャンネルさん」という架空ユーザーを作って各ジャンルの新着を架空マイリストにする
  36. * ・横スクロールを賢くする
  37. * ・お気に入りユーザーの時は「@ジャンプ」許可
  38. * ・軽量化
  39. * ・綺麗なコード
  40. *
  41. * ・タグ領域の圧縮方法をShinjukuWatch形式にする
  42. */
  43. //
  44. // * ver 1.140524
  45. // * ver 1.140522
  46. // - 本家のサムネイル仕様変更に対応
  47.  
  48. // * ver 1.140428
  49. // - 本家の仕様変更で使えなくなっていた、プレイリストのブックマーク保存機能を復活
  50.  
  51. // * ver 1.140319
  52. // - 謎の技術によって、ニコメンドがなくても説明文の動画リンクにサムネイルを出せるように
  53. // - 細かなスタイル調整
  54.  
  55. // * ver 1.140303
  56. // - 動画選択画面で再生リストの開閉が記憶されなくなったのに対抗
  57. // - 動画切換え時に一番上までスクロールするようになったのに対抗
  58. // - 本家の内部仕様変更(jQuery ver up等)に対応
  59.  
  60. // * ver 1.140227
  61. // - タグ検索のソート順が毎回リセットされるようになったのに対抗
  62.  
  63. // * ver 1.140218
  64. // - コメント重複を勝手に直してたけど不要になったので除去
  65. // - 二本目以降の動画だけ自動再生を追加
  66.  
  67. // * ver 1.140207
  68. // - テレビちゃんメニューの表示修正
  69. // - スレッドIDのリンクからもタグを取得できるように(watchページ内のみ)
  70. // - マイリスト選択メニュー部分の右クリックでとりマイの位置に戻る隠し機能
  71.  
  72. // * ver 1.140122
  73. // - テレビちゃんメニューをShinjukuWatch仕様に
  74.  
  75. // * ver 1.140110
  76. // - 検索フォームのオートコンプリートを調整
  77. // - ニコメンドまわりのコード除去
  78. // - 微妙にNicorenizerとの相性を改善
  79.  
  80. (function() {
  81. var isNativeGM = true;
  82. var monkey =
  83. (function(isNativeGM){
  84. var w;
  85. try { w = unsafeWindow || window; } catch (e) { w = window;}
  86. var document = w.document;
  87.  
  88. var conf = {
  89. autoBrowserFull: false, // 再生開始時に自動全画面化
  90. disableAutoBrowserFullIfNicowari: false, // ユーザーニコ割があるときは自動全画面化しない
  91. autoNotFull: true, // 再生完了時にフルスクリーン解除(原宿と同じにする)
  92. autoTagPin: false,
  93. topPager: true, // 検索ボックスのページャを上にする
  94. hideLeftIchiba: false,
  95. autoClosePlaylistInFull: true, // 全画面時にプレイリストを自動で閉じる
  96. autoOpenSearch: false, // 再生開始時に自動検索画面
  97. autoScrollToPlayer: true, // プレイヤー位置に自動スクロール(自動全画面化オフ時)
  98. hideNewsInFull: true, // 全画面時にニュースを閉じる
  99. wideCommentPanel: false, // コメントパネルをワイドにする
  100. removeLeftPanel: true, // 左パネルを消滅させる
  101. leftPanelJack: false, // 左パネルに動画情報を表示
  102. rightPanelJack: true, // 右パネルに動画情報を表示
  103. headerViewCounter: false, // ヘッダに再生数コメント数を表示
  104. popupViewCounter: 'full', // 動画切り替わり時にポップアップで再生数を表示
  105. ignoreJumpCommand: false, // @ジャンプ無効化
  106. nicoSSeekCount: -1, // @ジャンプによるシーク(ループなど)を許可する回数 -1=無限 0以上はその回数だけ許可
  107. doubleClickScroll: true, // 空白部分ををダブルクリックで動画の位置にスクロールする
  108. hidePlaylist: true, // プレイリストを閉じる
  109. hidePlaylistInVideoExplorer: true, // 動画選択画面でプレイリストを閉じる
  110. hidariue: false, // てれびちゃんメニュー内に、原宿以前のランダム画像復活
  111. videoExplorerHack: true, // 検索画面を乗っ取る
  112. squareThumbnail: true, // 検索画面のサムネを4:3にする
  113. enableFavTags: false, // 動画検索画面にお気に入りタグを表示
  114. enableFavMylists: false, // 動画検索画面にお気に入りマイリストを表示
  115. searchPageItemCount: 50, // 検索モードの1ページあたりの表示数
  116. enableMylistDeleteButton: false, // 動画検索画面で、自分のマイリストから消すボタンを追加する
  117. enableHoverPopup: true, // 動画リンクのマイリストポップアップを有効にする
  118. enableAutoTagContainerHeight: true, // タグが2行以内なら自動で高さ調節(ピン留め時のみ
  119. autoSmallScreenSearch: false, // ポップアップからのタグ検索でもプレイヤーを小さくする
  120. enableNewsHistory: false, // ニコニコニュースの履歴を保持する
  121. defaultSearchOption: '', // 検索時のデフォルトオプション
  122. autoPlayIfWindowActive: 'no', // 'yes' = ウィンドウがアクティブの時だけ自動再生する
  123. autoPlay2ndVideo: false, // 2本目以降の動画だけ自動再生
  124. enableYukkuriPlayButton: false, // スロー再生ボタンを表示する
  125. noNicoru: false, // ニコるボタンをなくす
  126. enoubleTouchPanel: false, // タッチパネルへの対応を有効にする
  127. mouseClickWheelVolume: 0, // マウスボタン+ホイールで音量調整を有効にする 1 = 左ボタン 2 = 右ボタン
  128. enableQTouch: false, // タッチパネルモード有効
  129. commentVisibility: 'visible', // 'visible', 'hidden', 'lastState'
  130. lastCommentVisibility: 'visible',
  131. controllerVisibilityInFull: '', // 全画面時に操作パネルとコメント入力欄を出す設定
  132. enableTrueBrowserFull: false, // フチなし全画面モードにする (Chromeは画面ダブルクリックで切り替え可能)
  133. enableSharedNgSetting: false, //
  134. hideNicoNews: false, // ニコニコニュースを消す
  135. hashPlaylistMode: 0, // location.hashにプレイリストを保持 0 =無効 1=連続再生時 2=常時
  136. storagePlaylistMode: '', // localStorageにプレイリストを保持
  137. compactVideoInfo: true, //
  138. hoverMenuDelay: 0.4, // リンクをホバーした時のメニューが出るまでの時間(秒)
  139. enableFullScreenMenu: true, // 全画面時にホイールでメニューを出す
  140. enableHeatMap: false, //
  141. heatMapDisplayMode: 'hover', // 'always' 'hover'
  142. replacePopupMarquee: true, //
  143. enableRelatedTag: true, // 関連タグを表示するかどうか
  144. // playerTabAutoOpenNicommend: 'enable', // 終了時にニコメンドを自動で開くかどうか 'enable' 'auto' 'disable'
  145. autoPauseInvisibleInput: true, //
  146. customPlayerSize: '', //
  147. removeCommentPanelHoverEvent: false, //
  148. disableVideoExplorer: false, //
  149. disableTagReload: false, //
  150. disableHorizontalScroll: false, // 横スクロールバーを出なくする
  151. hideCommentPanelSocialButtons: false, // コメントパネル下のソーシャルボタンを隠す
  152. mylistPanelPosition: '',
  153. enableDescriptionThumbnail: false, // 説明文の動画リンクにサムネイルとタイトル表示
  154.  
  155. enableLocalMylistCache: false,
  156.  
  157. rankingCategory_g_ent2_Close: true,
  158. rankingCategory_g_life2_Close: true,
  159. rankingCategory_g_tech_Close: true,
  160. rankingCategory_g_culture2_Close: true,
  161. rankingCategory_g_other_Close: true,
  162.  
  163. searchEngine: 'sugoi', // 'normal' 'sugoi'
  164. searchStartTimeRange: '', //
  165. searchLengthSecondsRange: '', //
  166. searchMusicDlFilter: false, //
  167.  
  168. hideVideoExplorerExpand: true, // 「動画をもっと見る」ボタンを小さくする
  169. // nicommendVisibility: 'visible', // ニコメンドの表示 'visible', 'underIchiba', 'hidden'
  170. ichibaVisibility: 'visible', // 市場の表示 '', 'visible', 'hidden'
  171. reviewVisibility: 'visible', // レビューの表示 'visible', 'hidden'
  172. bottomContentsVisibility: 'hidden', // 動画下のコンテンツ表示表示非表示
  173. hideMenuInFull: 'hide', // 全画面時にマイリストメニューを隠す '', 'hide' = 目立たなくする, 'hideAll' = 完全非表示
  174.  
  175. flatDesignMode: '', // 'on' グラデーションや角丸をなくす。 7/25からQwatchがフラットデザインになったので不要になった
  176. playerBgStyle: '', // ↑ の後継パラメータ 'gray' 'white' ''
  177.  
  178. shortcutTogglePlay: {char: 'P', shift: false, ctrl: false, alt: true, enable: false}, // 停止/再生
  179. shortcutDefMylist: {char: 'M', shift: true, ctrl: false, alt: false, enable: false}, // とりマイ登録のショートカット
  180. shortcutMylist: {char: 'M', shift: false, ctrl: true , alt: false, enable: false}, // マイリスト登録のショートカット
  181. shortcutOpenSearch: {char: 'S', shift: true, ctrl: false, alt: false, enable: false}, // 検索オープンのショートカット
  182. shortcutOpenDefMylist: {char: 'D', shift: true, ctrl: false, alt: false, enable: false}, // とりマイオープンのショートカット
  183. shortcutOpenRecommend: {char: 'R', shift: true, ctrl: false, alt: false, enable: false}, // 関連動画(オススメ)を開くショートカット
  184. shortcutCommentVisibility: {char: 'V', shift: true, ctrl: false, alt: false, enable: false}, // コメント表示ON/OFFのショートカット
  185. shortcutScrollToNicoPlayer: {char: 'P', shift: true, ctrl: false, alt: false, enable: false}, // プレイヤーまでスクロールのショートカット
  186. shortcutShowOtherVideo: {char: 'U', shift: true, ctrl: false, alt: false, enable: false}, // 投稿者の関連動画表示のショートカット
  187. shortcutMute: {char: 'T', shift: true, ctrl: false, alt: false, enable: false}, // 音量ミュートのショートカット
  188. shortcutDeepenedComment: {char: 'B', shift: true, ctrl: false, alt: false, enable: false}, // コメント背面表示
  189. shortcutToggleStageVideo: {char: 'H', shift: true, ctrl: false, alt: false, enable: false}, // ハードウェアアクセラレーション(StageVideo)のショートカット
  190.  
  191. shortcutInvisibleInput: {char: 'C', shift: false, ctrl: false, alt: true, enable: true}, // 停止/再生
  192.  
  193. watchCounter: 0, // お前は今までに見た動画の数を覚えているのか?をカウントする
  194. forceEnableStageVideo: false,
  195. forceExpandStageVideo: false,
  196. enableAutoPlaybackContinue: false, // 一定時間操作しなかくても自動再生を続行
  197. lastLeftTab: 'videoInfo',
  198. lastRightTab: 'w_videoInfo',
  199. lastRightTabInExplorer: 'comment',
  200. lastControlPanelPosition: '',
  201. enableSortTypeMemory: true, // 検索のソート順を記憶する
  202. searchSortType: 'n', //
  203. searchSortOrder: 'd', // 'd'=desc 'a' = asc
  204. fxInterval: 40, // アニメーションのフレームレート 40 = 25fps
  205. enableGpuLayer: false, // 一部の要素でGPU描画を有効にしてみる?
  206. debugMode: false
  207. };
  208.  
  209.  
  210. //===================================================
  211. //===================================================
  212. //===================================================
  213.  
  214. function addStyle(styles, id) {
  215. var elm = document.createElement('style');
  216. window.setTimeout(function() {
  217. elm.type = 'text/css';
  218. if (id) { elm.id = id; }
  219.  
  220. var text = styles.toString();
  221. text = document.createTextNode(text);
  222. elm.appendChild(text);
  223. var head = document.getElementsByTagName('head');
  224. head = head[0];
  225. head.appendChild(elm);
  226. }, 0);
  227. return elm;
  228. }
  229.  
  230. if (!isNativeGM) {
  231. this.GM_xmlhttpRequest = function(options) {
  232. try {
  233. var req = new XMLHttpRequest();
  234. var method = options.method || 'GET';
  235. req.onreadystatechange = function() {
  236. if (req.readyState === 4) {
  237. if (typeof options.onload === "function") options.onload(req);
  238. }
  239. };
  240. req.open(method, options.url, true);
  241. if (options.headers) {
  242. for (var h in options.headers) {
  243. req.setRequestHeader(h, options.headers[h]);
  244. }
  245. }
  246.  
  247. req.send(options.data || null);
  248. } catch (e) {
  249. if (conf.debugMode) console.log(e);
  250. }
  251. };
  252. }
  253.  
  254. (function() { // 各ページ共通
  255. var __css__ = (function() {/*
  256. .tagItemsPopup {
  257. background: #eef;
  258. }
  259. .popupMenu {
  260. position: absolute;
  261. min-width: 200px;
  262. font-Size: 12pt;
  263. z-index: 2000000;
  264. box-shadow: 2px 2px 2px #888;
  265. }
  266. .popupMenu ul, .popupMenu ul li {
  267. position: relative;
  268. list-style-type: none;
  269. margin: 0; padding: 0;
  270. white-space: nowrap;
  271. }
  272. .tagItemsPopup .icon{
  273. width: 17px;
  274. height: 15px;
  275. }
  276. .tagItemsPopup .nicodic, .tagItemsPopup .newsearch {
  277. margin: 1px 4px 1px 1px;
  278. }
  279. .tagItemsPopup .nicodic:hover, .tagItemsPopup .newsearch:hover {
  280. margin: 0px 3px 0px 0px;
  281. border: 1px outset;
  282. }
  283.  
  284. .mylistListPopup {
  285. position:absolute;
  286. background: #fff;
  287. overflow: visible;
  288. padding: 8px;
  289. border: 1px outset #333;
  290. transition: opacity 0.3s ease;
  291. }
  292. .mylistListPopup.popupMenu ul li {
  293. position: relative;
  294. margin: 2px 8px;
  295. overflow-y: visible;
  296. }
  297. .mylistListPopup:not(.show) {
  298. left: -9999px;
  299. top: -9999px;
  300. opacity: 0;
  301. }
  302. .mylistListPopup.show {
  303. opacity: 1;
  304. }
  305. .mylistListPopup .listInner {
  306. -webkit-column-width: auto; -moz-column-width: auto;
  307. -webkit-column-count: 1 !important; {* Chromeだけバグるので *}
  308. }
  309. .mylistListPopup .icon {
  310. display: inline-block;
  311. width: 18px;
  312. height: 14px;
  313. margin: -4px 4px 0 0;
  314. vertical-align: middle;
  315. margin-right: 15px;
  316. background: url("http://uni.res.nimg.jp/img/zero_my/icon_folder_default.png") no-repeat scroll 0 0 transparent;
  317. transform: scale(1.5); -webkit-transform: scale(1.5);
  318. transform-origin: 0 0 0; -webkit-transform-origin: 0 0 0;
  319. transition: transform 0.1s ease, box-shadow 0.1s ease;
  320. -webkit-transition: -webkit-transform 0.1s ease, box-shadow 0.1s ease;
  321. cursor: pointer;
  322. }
  323. .mylistListPopup .icon:hover {
  324. background-color: #ff9;
  325. transform: scale(2); -webkit-transform: scale(2);
  326. }
  327. .mylistListPopup .icon:after {
  328. content: '開く';
  329. position: absolute;
  330. bottom: 0px;
  331. right: 12px;
  332. padding: 2px;
  333. opacity: 0;
  334. transform: scale(0.5); -webkit-transform: scale(0.5);
  335. z-index: -1;
  336. }
  337. .mylistListPopup .icon:hover:after {
  338. {*box-shadow: 2px 2px 2px #888;*}
  339. background: #fff;
  340. z-index: 100;
  341. opacity: 1;
  342. }
  343. .mylistListPopup .deflist .icon { background-position: 0 -253px; }
  344. .mylistListPopup .folder1 .icon { background-position: 0 -23px;}
  345. .mylistListPopup .folder2 .icon { background-position: 0 -46px;}
  346. .mylistListPopup .folder3 .icon { background-position: 0 -69px;}
  347. .mylistListPopup .folder4 .icon { background-position: 0 -92px;}
  348. .mylistListPopup .folder5 .icon { background-position: 0 -115px;}
  349. .mylistListPopup .folder6 .icon { background-position: 0 -138px;}
  350. .mylistListPopup .folder7 .icon { background-position: 0 -161px;}
  351. .mylistListPopup .folder8 .icon { background-position: 0 -184px;}
  352. .mylistListPopup .folder9 .icon { background-position: 0 -207px;}
  353.  
  354.  
  355. .mylistListPopup .name {
  356. display: inline-block;
  357. vertical-align: middle;
  358. font-size: 110%;
  359. color: #666;
  360. text-derocation: none !important;
  361. }
  362. .mylistListPopup .name:hover {
  363. color: #000;
  364. background-color: #ff9;
  365. }
  366. .mylistListPopup .name:after {
  367. content: ' に登録';
  368. font-size: 75%;
  369. color: #fff;
  370. }
  371. .mylistListPopup .name.exist:after {
  372. content: ' に登録済';
  373. color: #933;
  374. }
  375. .mylistListPopup .name:hover:after {
  376. color: #666;
  377. }
  378.  
  379. {* マイリスト登録パネル *}
  380. .mylistPopupPanel {
  381. height: 24px;
  382. z-index: 10000;
  383. {*border: 1px solid silver;
  384. border-radius: 3px; *}
  385. padding: 0;
  386. margin: 0;
  387. overflow: hidden;
  388. display: inline-block;
  389. background: #eee;
  390. }
  391. .mylistPopupPanel.fixed {
  392. position: fixed; right: 0; bottom: 0;
  393. transition: right 0.1s ease-out;
  394. }
  395. .mylistPopupPanel.fixed.left {
  396. right: auto;
  397. left: 0;
  398. }
  399. .mylistPopupPanel.fixed.top {
  400. bottom: auto;
  401. top: 0;
  402. }
  403.  
  404. .mylistPopupPanel.fixed>* {
  405. transition: opacity 0.1s ease-out;
  406. }
  407. .mylistPopupPanel>*>* {
  408. transition: background 0.5s ease 0.5s, color 0.5s ease 0.5s;
  409. }
  410. .full_with_browser .mylistPopupPanel, .mylistPopupPanel.black {
  411. background: #000; border: 0;
  412. }
  413. body.full_with_browser .mylistPopupPanel *,body .mylistPopupPanel.black.fixed * {
  414. background: #000; color: #888; border-color: #333;
  415. }
  416. .full_with_browser .mylistPopupPanel.hideAllInFull{
  417. display: none;
  418. }
  419. .full_with_browser .mylistPopupPanel.hideInFull.fixed:not(:hover) {
  420. right: -100px;
  421. transition: right 0.8s ease-in-out 0.5s;
  422. }
  423. .full_with_browser .mylistPopupPanel.hideInFull.fixed.right:not(:hover) {
  424. left: -100px; right: auto;
  425. transition: left 0.8s ease-in-out 0.5s;
  426. }
  427. .full_with_browser .mylistPopupPanel.hideInFull:not(:hover)>*{
  428. opacity: 0;
  429. transition: opacity 0.3s ease-out 1s;
  430. }
  431. .mylistPopupPanel.w_touch {
  432. height: 40px;
  433. }
  434. iframe.popup {
  435. position: absolute;
  436. }
  437. {* マウスホバーで出るほうのマイリスト登録パネル *}
  438. .mylistPopupPanel.popup {
  439. position: absolute;
  440. z-index: 1000000;
  441. box-shadow: 2px 2px 2px #888;
  442. }
  443. {* マイリスト登録パネルの中の各要素 *}
  444. .mylistPopupPanel .mylistSelect {
  445. width: 64px;
  446. margin: 0;
  447. padding: 0;
  448. font-size: 80%;
  449. white-space: nowrap;
  450. {*background: #eee;*}
  451. border: 1px solid silver;
  452. }
  453. .mylistSelect:focus {
  454. border-style: outset;
  455. }
  456. {* 誤操作を減らすため、とりマイの時だけスタイルを変える用 *}
  457. .mylistPopupPanel.w_touch button {
  458. padding: 8px 18px;
  459. }
  460. .mylistPopupPanel.w_touch .mylistSelect {
  461. font-size: 170%; width: 130px; border: none;
  462. }
  463. .mylistPopupPanel.w_touch .mylistSelect:focus {
  464. border: 1px dotted;
  465. }
  466. .mylistPopupPanel.deflistSelected button {
  467. }
  468. .mylistPopupPanel.mylistSelected button {
  469. color: #ccf;
  470. }
  471. .mylistPopupPanel button {
  472. margin: 0;
  473. font-weight: bolder;
  474. cursor: pointer;
  475. }
  476. .mylistPopupPanel button:active, #outline .playlistToggle:active, #outline .openVideoExplorer:active, #content .openConfButton:active {
  477. border:1px inset !important
  478. }
  479. .mylistPopupPanel button:hover, #outline .playlistToggle:hover, #outline .openVideoExplorer:hover, #outline .openConfButton:hover, #yukkuriPanel .yukkuriButton:hover {
  480. border:1px outset
  481. }
  482. .mylistPopupPanel .mylistAdd, .mylistPopupPanel .tagGet, #yukkuriPanel .yukkuriButton {
  483. border:1px solid #777; cursor: pointer; font-family:arial, helvetica, sans-serif; padding: 0px 4px 0px 4px; text-shadow: -1px -1px 0 rgba(0,0,0,0.3);font-weight:bold; text-align: center; color: #eee; background-color: #888; margin: 0;
  484. }
  485. .mylistPopupPanel .mylistAdd:focus, .mylistPopupPanel .tagGet:focus, #yukkuriPanel .yukkuriButton:focus {
  486. border-style: outset; color: orange;
  487. }
  488. .mylistPopupPanel.deflistSelected {
  489. color: #ff9;
  490. }
  491. .mylistPopupPanel .deflistRemove, #yukkuriPanel .yukkuriButton.active{
  492. border:1px solid #ebb7b7; font-family:arial, helvetica, sans-serif; padding: 0px 6px 0px 6px; text-shadow: -1px -1px 0 rgba(0,0,0,0.3);font-weight:bold; text-align: center; color: #FFFFFF; background-color: #f7e3e3;
  493. }
  494. .mylistPopupPanel.deflistSelected {
  495. color: #ff9;
  496. }
  497. .mylistPopupPanel .deflistRemove, #yukkuriPanel .yukkuriButton.active{
  498. border:1px solid #ebb7b7; font-family:arial, helvetica, sans-serif; padding: 0px 6px 0px 6px; text-shadow: -1px -1px 0 rgba(0,0,0,0.3); text-align: center; color: #FFFFFF; background-color: #f7e3e3;
  499. }
  500. .mylistPopupPanel.mylistSelected .deflistRemove {
  501. display: none;
  502. }
  503. .mylistPopupPanel .closeButton{
  504. color: #339;
  505. padding: 0;
  506. margin: 0;
  507. font-size: 80%;
  508. text-decoration: none;
  509. }
  510. .mylistPopupPanel .newTabLink{
  511. padding: 0 2px; text-decoration: underline; text-shadow: -1px -1px 0px #442B2B;
  512. }
  513. .mylistPopupPanel.fixed .newTabLink, .mylistPopupPanel.fixed .closeButton {
  514. display: none;
  515. }
  516. .w_fullScreenMenu .mylistPopupPanel.fixed { bottom: 2px; }
  517.  
  518. {* ポイントが無いときは表示しない *}
  519. .item:not(.silver):not(.gold) .uadContainer {
  520. display: none !important;
  521. }
  522.  
  523. .watchItLaterAPILoaderFrame {
  524. width: 1px;
  525. height: 1px;
  526. position: fixed;
  527. top: -100px;
  528. left: -100px;
  529. border: 0;
  530. overflow: hidden;
  531. }
  532.  
  533. */}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1]
  534. .replace(/\{\*/g, '/*').replace(/\*\}/g, '*/');
  535. addStyle(__css__, 'watchItLaterCommonStyle');
  536. })(); // end of commoncss
  537.  
  538.  
  539.  
  540.  
  541. (function() { // watchページだけのstyle
  542. if (!w.WatchApp) { return; }
  543. var __css__ = (function() { /*
  544. {* 動画タグとプレイリストのポップアップ *}
  545. #videoTagPopupContainer {
  546. }
  547. #videoTagPopupContainer.w_touch {
  548. line-height: 200%; font-size: 130%;
  549. }
  550. #videoTagPopupContainer.w_touch .nicodic{
  551. margin: 4px 14px;
  552. }
  553. .playlistMenuPopup {
  554. background: #666; color: white; padding: 4px 8px;
  555. }
  556. .playlistMenuPopup.w_touch {
  557. line-height: 250%;
  558. }
  559. #playlistSaveDialog {
  560. display: none;
  561. }
  562. #playlistSaveDialog.show {
  563. display: block;
  564. }
  565. #playlistSaveDialog.show .shadow{
  566. position: fixed;
  567. top: 0; left: 0; width: 100%; height: 100%;
  568. background: #000; opacity: 0.5;
  569. z-index: 30000;
  570. }
  571. #playlistSaveDialog.show .formWindow{
  572. position: fixed;
  573. margin: 0 auto;
  574. text-align: center;
  575. width: 100%;
  576. top: 45%;
  577. z-index: 30001;
  578. }
  579. #playlistSaveDialog .formWindow .formWindowInner{
  580. -webkit-transition: opacity 1s ease-out;
  581. transition: opacity 1s ease-out;
  582. opacity: 0;
  583. }
  584. #playlistSaveDialog.show .formWindow .formWindowInner{
  585. text-align: left;
  586. opacity: 1;
  587. margin: 0 auto;
  588. background: #f4f4f4;
  589. width: 500px;
  590. padding: 8px;
  591. border: 1px solid;
  592. }
  593. #playlistSaveDialog.show .formWindow .formWindowInner a{
  594. font-weight: bolder;
  595. }
  596. #playlistSaveDialog.show .formWindow .formWindowInner a:hover{
  597. text-decoration: underline; background: white;
  598. }
  599. #playlistSaveDialog.show .formWindow .formWindowInner label{
  600. margin: 8px;
  601. }
  602. #playlistSaveDialog.show .formWindow .formWindowInner input{
  603.  
  604. }
  605. #playlistSaveDialog.show .formWindow .formWindowInner .desc{
  606. font-size: 80%;
  607. }
  608. .playlistMenuPopup ul li {
  609. cursor: pointer;
  610. }
  611. .playlistMenuPopup ul li.savelist {
  612. color: #aaa;
  613. }
  614. .playlistMenuPopup ul li:hover {
  615. text-decoration: underline; background: #888;
  616. }
  617. #yukkuriPanel .yukkuriButton.active {
  618. border:1px inset
  619. }
  620.  
  621. #content .openConfButton {
  622. border:1px solid #bbb; cursor: pointer; font-family:arial, helvetica, sans-serif; padding: 4px; text-shadow: 1px 1px 0 rgba(0,0,0,0.3); text-align: center; color: #444; background-color: #ccc; margin: 0;
  623. }
  624. #outline .playlistToggle, #outline .openVideoExplorer, #outline .openConfButton {
  625. border:1px solid #444; cursor: pointer; font-family:arial, helvetica, sans-serif; padding: 0px 4px 0px 4px; box-shadow: 1px 1px 0 rgba(0,0,0,0.3); text-align: center; color: #444; background-color: #ccc; margin: 0;
  626. height: 24px; border-radius: 0 0 8px 8px;
  627. }
  628. #outline .openConfButton { padding: 0 8px; letter-spacing: 4px; width: 60px; }
  629.  
  630. {* 全画面時にタグとプレイリストを表示しない時*}
  631. body.full_and_mini.full_with_browser #playerAlignmentArea{
  632. margin-bottom: 0 !important;
  633. }
  634. body.full_and_mini.full_with_browser #playlist{
  635. z-index: auto;
  636. }
  637. body.full_and_mini.full_with_browser .generationMessage{
  638. display: inline-block;
  639. }
  640. {* 全画面時にタグとプレイリストを表示する時 *}
  641. body.full_with_browser #playlist{
  642. z-index: 100;
  643. }
  644. body.full_with_browser .generationMessage{
  645. display: none;
  646. }
  647. body.full_with_browser .browserFullOption{
  648. padding-right: 200px;
  649. }
  650. {* 全画面時にニュースを隠す時 *}
  651. body.full_with_browser.hideNewsInFull #playerAlignmentArea{
  652. margin-bottom: -37px;
  653. }
  654. {* 少しでも縦スクロールを減らすため、動画情報を近づける。人によっては窮屈に感じるかも *}
  655. #outline {
  656. margin-top: -16px;
  657. }
  658. #outline #feedbackLink{
  659.  
  660. }
  661. #outline .videoEditMenuExpand{
  662. position: absolute;right: 0;top: 26px; z-index: 1;
  663. }
  664. {* ヘッダに表示する再生数 *}
  665. #videoCounter {
  666. color: #ff9; font-size: 70%;
  667. }
  668. {* 右に表示する動画情報 *}
  669. .sidePanel .sideVideoInfo, .sidePanel .sideIchibaPanel, .sidePanel .sideReviewPanel {
  670. padding: 0px 0px 0 0px; width: 196px; height: 100%; z-index: 10019;
  671. position:absolute; top:0; right:0; border: 1px solid #000;
  672. overflow-x: visible; overflow-y: auto;
  673. }
  674. {* 右に表示する動画情報 *}
  675. #playerTabWrapper.sidePanel .sideVideoInfo, #playerTabWrapper.sidePanel .sideIchibaPanel, #playerTabWrapper.sidePanel .sideReviewPanel {
  676. padding: 0px 0px 0 0px; width: 324px; height: 100%;
  677. position: absolute; top: 0; right:0;
  678. }
  679.  
  680. body:not(.full_with_browser) .w_wide #playerTabWrapper .sideVideoInfo,
  681. body:not(.full_with_browser) .w_wide #playerTabWrapper .sideIchibaPanel,
  682. body:not(.full_with_browser) .w_wide #playerTabWrapper .sideReviewPanel,
  683. .videoExplorer #playerTabWrapper .sideVideoInfo,
  684. .videoExplorer #playerTabWrapper .sideIchibaPanel,
  685. .videoExplorer #playerTabWrapper .sideReviewPanel {
  686. width: 418px;
  687. }
  688. #playerTabWrapper.w_videoInfo #appliPanel, #playerTabWrapper.w_ichiba #appliPanel, #playerTabWrapper.w_review #appliPanel {
  689. top: -9999px;
  690. }
  691. .sidePanel .sideVideoInfo {
  692. background: #f4f4f4; text-Align: left; overflow-x: hidden; overflow-Y: auto; box-shadow: none; font-size: 90%; border: 1px solid black;
  693. }
  694. .sidePanel .sideIchibaPanel, .sidePanel .sideReviewPanel {
  695. background: #f4f4f4; text-Align: center; overflow-x: hidden; overflow-Y: auto; box-shadow: none; font-size: 90%;
  696. }
  697. .sidePanel .sideVideoInfo .sideVideoInfoInner {
  698. padding: 0 4px; position: relative;
  699. }
  700. .sidePanel .sideVideoInfo .videoTitleContainer {
  701. background: #ddd; text-align: center; color: #000; margin: 6px 6px 0;
  702. border: solid; border-width: 2px 2px 0; border-color: #888;
  703. }
  704. .sidePanel .sideVideoInfo .videoOwnerInfoContainer {
  705. margin: 0 6px; border: solid; border-width: 0 2px 0; border-color: #888;
  706. }
  707. .sidePanel .sideVideoInfo .videoThumbnailContainer {
  708. background: #ddd; text-align: center; color: #000; margin: 0;
  709. }
  710. .sidePanel .sideVideoInfo .videoThumbnailContainer img {
  711. cursor: pointer;
  712. }
  713. .sidePanel .sideVideoInfo .videoTitle {
  714. padding: 8px;
  715. }
  716. .sidePanel .sideVideoInfo .videoPostedAt {
  717. color: #333;
  718. }
  719. .sidePanel .sideVideoInfo .videoStats{
  720. font-size:90%;
  721. }
  722. .sidePanel .sideVideoInfo .videoStats li{
  723. display: inline-block; margin: 0 2px;
  724. }
  725. .sidePanel .sideVideoInfo .videoStats li span{
  726. font-weight: bolder;
  727. }
  728. .sidePanel .sideVideoInfo .videoStats .ranking{
  729. display: none !important;
  730. }
  731. .sidePanel .sideVideoInfo .videoInfo{
  732. background: #ddd; text-align: center; padding: 4px; margin: 0 6px 6px;
  733. border: solid; border-width: 0 2px 2px; border-color: #888;
  734. }
  735. .sideVideoInfo .sideVideoInfoInner{
  736. -webkit-transition: opacity 1s ease-out, color 3s ease-out;
  737. transition: opacity 1s ease-out, color 3s ease-out;
  738. opacity: 0;
  739. }
  740. .sideVideoInfo.show .sideVideoInfoInner{
  741. opacity: 1;
  742. }
  743. .videoCount.blink {
  744. color: #ccc;
  745. }
  746. .sidePanel .videoCountDiff {
  747. position: absolute; color: white; right: 0; opacity: 0; z-index: 100; text-shadow: 1px 1px 0 orange;
  748. }
  749. .sidePanel .videoCountDiff.blink {
  750. opacity: 1; color: white;
  751. }
  752. #siteHeader .videoCount, #siteHeader .videoCountDiff {
  753. min-width: 32px; text-align: right; display: inline-block;
  754. }
  755. #siteHeader .videoCountDiff, #trueBrowserFullShield .videoCountDiff {
  756. position: absolute; color: yellow; opacity: 0; font-weight: bolder; text-shadow: 1px 1px 0 red;
  757. }
  758. #siteHeader .videoCountDiff.blink, #trueBrowserFullShield .videoCountDiff.blink {
  759. opacity: 1; color: yellow;
  760. }
  761. #trueBrowserFullShield .blink, #videoCounter .blink {
  762. color: #000;
  763. }
  764. .videoCountDiff:before {content: '+';}
  765. .videoCountDiff.down:before {content: ''; }
  766. #popupMarquee .videoCountDiff {display: none;}
  767. .sidePanel .sideVideoInfo .videoDescription{
  768. overflow-x: hidden; text-align: left;
  769. }
  770. .sidePanel .sideVideoInfo .videoDescriptionInner{
  771. margin: 0 8px;
  772. }
  773. .sidePanel .sideVideoInfo .videoDetails{
  774. min-width: 150px;
  775. }
  776. .sidePanel .sideVideoInfo .videoDetails a{
  777. margin: auto 4px;
  778. }
  779. .sidePanel .sideVideoInfo .videoDetails a:visited{
  780. color: #04618c;
  781. }
  782. .sidePanel .sideVideoInfo .videoDetails a.watch{
  783. margin: auto 30px auto 4px;
  784. display:inline-block;
  785. }
  786. .sideVideoInfo .userName, .sideVideoInfo .channelName{
  787. display: block;
  788. font-size: 120%;
  789. cursor: pointer;
  790. }
  791. .sideVideoInfo .userIconContainer, .sideVideoInfo .channelIconContainer {
  792. background: #ddd; width: 100%; text-align: center; float: none;
  793. }
  794. .sidePanel .userIcon, .sidePanel .channelIcon{
  795. min-width: 128px; max-width: 150px; width: auto; height: auto; border: 0;
  796. box-shadow: 0 0 4px #666; cursor: pointer;
  797. }
  798. .sideVideoInfo .descriptionThumbnail {
  799. text-align: left; font-size: 90%; padding: 4px; background: #ddd;
  800. min-height: 50px; margin: 4px 8px; font-weight: normal; color: black;
  801. }
  802. .sideVideoInfo .descriptionThumbnail p {
  803. margin: 0 8px;
  804. font-weight: bolder;
  805. }
  806. .sideVideoInfo .descriptionThumbnail .uploadAt {
  807. font-size: 90%;
  808. margin: 4px;
  809. color: #333;
  810. }
  811. .sideVideoInfo .descriptionThumbnail .counterContainer {
  812. text-align: center;
  813. }
  814. .sideVideoInfo .descriptionThumbnail .view,
  815. .sideVideoInfo .descriptionThumbnail .comment,
  816. .sideVideoInfo .descriptionThumbnail .mylist
  817. {
  818. font-size: 90%;
  819. white-space: nowrap;
  820. margin-right: 4px;
  821. color: #333;
  822. }
  823. .sideVideoInfo .descriptionThumbnail .count {
  824. font-weight: bolder;
  825. }
  826.  
  827. .sideVideoInfo .descriptionThumbnail.video img{
  828. height: 50px; cursor: pointer; float: left;
  829. }
  830. .sideVideoInfo .descriptionThumbnail.mylist img{
  831. height: 40px; cursor: pointer;
  832. }
  833. .sideVideoInfo .descriptionThumbnail.illust img{
  834. height: 60px; cursor: pointer;
  835. }
  836. .sideVideoInfo a.otherSite {
  837. font-weight: bolder; text-decoration: underline;
  838. }
  839. body:not(.videoExplorer) #leftPanel.removed {
  840. display: none; left: 0px;
  841. }
  842. body:not(.videoExplorer) #leftPanel.removed .sideVideoInfo {
  843. display: none; width: 0px !important; border: none; margin: 0; padding: 0; right: auto;
  844. }
  845. .sideVideoInfo .userIconContainer.isUserVideoPublic .notPublic { display: none; }
  846. .sideVideoInfo .userIconContainer .isPublic { display: none; }
  847. .sideVideoInfo .userIconContainer.isUserVideoPublic .isPublic { display: inline; }
  848. body.videoExplorer #content.w_adjusted .sideIchibaPanel .ichiba_mainitem,
  849. .sidePanel .sideIchibaPanel .ichiba_mainitem {
  850. width: 180px; display:inline-block; vertical-align: top;
  851. margin: 4px 3px; border 1px solid silver;
  852. }
  853.  
  854. body.videoExplorer #content.w_adjusted .sideIchibaPanel .ichiba_mainitem .thumbnail span {
  855. font-size: 60px;
  856. }
  857. body.videoExplorer #content.w_adjusted .sideIchibaPanel .ichiba_mainitem>div>dt {
  858. height: 50px;position: relative;
  859. }
  860. body.videoExplorer #content.w_adjusted .sideIchibaPanel .ichiba_mainitem .balloonUe {
  861. position: absolute;width: 100%;
  862. }
  863. body.videoExplorer #content.w_adjusted .sideIchibaPanel .ichiba_mainitem .balloonUe {
  864. position: absolute;
  865. }
  866. body.videoExplorer #content.w_adjusted .sideIchibaPanel .ichiba_mainitem .balloonShita {
  867. position: absolute;
  868. }
  869.  
  870. .sidePanel.videoInfo, .sidePanel.ichiba{
  871. background: none;
  872. }
  873.  
  874. .sideVideoInfo.isFavorite .userName:after, .sideVideoInfo.isFavorite.isChannel .videoOwnerInfoContainer .channelName:after{
  875. content: ' ★ '; color: gold; text-shadow: 1px 1px 1px black;
  876. }
  877.  
  878. .sidePanel.videoInfo #leftPanelContent, .sidePanel.ichiba #leftPanelContent {
  879. display: none;
  880. }
  881. .sidePanel.w_comment #playerTabContainer,
  882. .sidePanel.videoInfo .sideVideoInfo,
  883. .sidePanel.ichiba .sideIchibaPanel,
  884. .sidePanel.w_videoInfo .sideVideoInfo,
  885. .sidePanel.w_ichiba .sideIchibaPanel,
  886. .sidePanel.w_review .sideReviewPanel {
  887. display: block; z-index: 10060;
  888. }
  889. .sidePanel:not(.w_comment) .watchWatchContainer {
  890. display: none;
  891. }
  892.  
  893. #leftPanelTabContainer {
  894. display:none; background: #666; position: absolute; right: 4px; top: -27px; list-style-type: none; padding: 4px 6px 3px 60px; height: 20px;
  895. }
  896. #sidePanelTabContainer {
  897. display: none;
  898. position: absolute; list-style-type: none;
  899. padding: 5px 10px 0; right: -408px; top: 0; width: 350px; height: 34px;
  900. transform: rotate(90deg); transform-origin: 0 0 0;
  901. -webkit-transform: rotate(90deg); -webkit-transform-origin: 0 0 0;
  902. z-index: 1000;
  903. }
  904. .full_with_browser #sidePanelTabContainer {
  905. background: #000 !important;
  906. }
  907. body:not(.videoExplorer):not(.full_with_browser) #sidePanelTabContainer.left {
  908. background: #000; right: auto; left: -375px; padding: 0; height: 27px;
  909. transform: rotate(-90deg); transform-origin: 100% 0 0;
  910. -webkit-transform: rotate(-90deg); -webkit-transform-origin: 100% 0 0;
  911. }
  912.  
  913. #leftPanelTabContainer.w_touch {
  914. top: -40px; height: 33px;
  915. }
  916. .sidePanel:hover #sidePanelTabContainer, .sidePanel:hover #leftPanelTabContainer {
  917. display: block;
  918. }
  919. #leftPanelTabContainer .tab{
  920. display: inline-block; cursor: pointer; background: #999; padding: 2px 4px 0px; border-width: 2px 2px 0px;
  921. }
  922. #leftPanelTabContainer.w_touch .tab, #sidePanelTabContainer.w_touch .tab {
  923. padding: 8px 12px 8px;
  924. }
  925. #sidePanelTabContainer .tab {
  926. background: none repeat scroll 0 0 #999999; border-width: 2px 2px 0; cursor: pointer;
  927. display: inline-block; font-size: 13px; padding: 5px 10px 8px;
  928. border-radius: 8px 8px 0px 0px;
  929. }
  930. body:not(.videoExplorer):not(.full_with_browser) #sidePanelTabContainer.left .tab {
  931. display: inline-block; font-size: 13px; padding: 5px 10px 0px;
  932. }
  933. #leftPanel.videoInfo .tab.videoInfo, #leftPanel.ichiba .tab.ichiba {
  934. background: #f4f4f4; border-style: outset;
  935. }
  936. #playerTabWrapper.w_comment .tab.comment,
  937. #playerTabWrapper.w_videoInfo .tab.videoInfo,
  938. #playerTabWrapper.w_ichiba .tab.ichiba,
  939. #playerTabWrapper.w_review .tab.review
  940. {
  941. background: #dfdfdf; border-style: outset;
  942. }
  943.  
  944. #playerTabWrapper.w_videoInfo #playerCommentPanel,
  945. #playerTabWrapper.w_ichiba #playerCommentPanel,
  946. #playerTabWrapper.w_review #playerCommentPanel {
  947. {*display: none;*} top: -9999px;
  948. }
  949. .sidePanel.ichibaEmpty .tab.ichiba, .sidePanel.reviewEmpty .tab.review {
  950. color: #ccc;
  951. }
  952.  
  953. .sideIchibaPanel .ichibaPanelInner {
  954. margin:0; color: #666;
  955. }
  956. .sideIchibaPanel .ichibaPanelHeader .logo{
  957. text-shadow: 1px 1px 1px #666; cursor: pointer; padding: 4px 0px 4px; font-size: 125%;
  958. }
  959. .sideIchibaPanel .ichibaPanelFooter{
  960. text-align: center;
  961. }
  962. .sideIchibaPanel .ichiba_mainitem {
  963. margin: 0 0 8px 0; background: white; border-bottom : 1px dotted #ccc;
  964. }
  965. .sideIchibaPanel .ichiba_mainitem a:hover{
  966. background: #eef;
  967. }
  968. .sideIchibaPanel .ichiba_mainitem>div {
  969. max-width: 266px; margin: auto; text-align: center;
  970. }
  971. .sideIchibaPanel .ichiba_mainitem .blomagaArticleNP {
  972. background: url("http://ichiba.nicovideo.jp/embed/zero/img/bgMainBlomagaArticleNP.png") no-repeat scroll 0 0 transparent;
  973. height: 170px;
  974. margin: 0 auto;
  975. width: 180px;
  976. }
  977. .sideIchibaPanel .ichiba_mainitem .blomagaLogo {
  978. color: #FFFFFF;font-size: 9px;font-weight: bold;padding-left: 10px;padding-top: 8px;
  979. }
  980. .sideIchibaPanel .ichiba_mainitem .blomagaLogo span{
  981. background: none repeat scroll 0 0 #AAAAAA;padding: 0 3px;
  982. }
  983. .sideIchibaPanel .ichiba_mainitem .blomagaText {
  984. color: #666666;font-family: 'HGS明朝E','MS 明朝';font-size: 16px;height: 100px;
  985. padding: 7px 25px 0 15px;text-align: center;white-space: normal;word-break: break-all;word-wrap: break-word;
  986. }
  987. .sideIchibaPanel .ichiba_mainitem .blomagaAuthor {
  988. color: #666666; font-size: 11px;padding: 0 20px 0 10px;text-align: right;
  989. }
  990. .sideIchibaPanel .ichiba_mainitem .balloonUe{
  991. bottom: 12px; display: block; max-width: 266px;
  992. }
  993. .sideIchibaPanel .ichiba_mainitem .balloonUe a{
  994. background: url("/img/watch_zero/ichiba/imgMainBalloonUe.png") no-repeat scroll center top transparent;
  995. color: #666666 !important;
  996. display: block;
  997. font-size: 108%;
  998. line-height: 1.2em;
  999. margin: 0 auto;
  1000. padding: 8px 15px 3px;
  1001. text-align: center;
  1002. text-decoration: none;
  1003. word-wrap: break-word;
  1004. }
  1005. .sideIchibaPanel .ichiba_mainitem .balloonShita{
  1006. height: 12px; bottom: 0; left: 0;
  1007. }
  1008. .sideIchibaPanel .ichiba_mainitem .balloonShita img{
  1009. vertical-align: top !important;
  1010. }
  1011. .sideIchibaPanel .ichiba_mainitem .ichibaMarquee {
  1012. display: none;
  1013. }
  1014. .sideIchibaPanel .ichiba_mainitem .thumbnail span {
  1015. font-size: 22px; color: #0066CC;
  1016. font-family: 'ヒラギノ明朝 Pro W3','Hiragino Mincho Pro','MS P明朝','MS PMincho',serif;
  1017. }
  1018. .sideIchibaPanel .ichiba_mainitem .action {
  1019. font-size: 85%;
  1020. }
  1021. .sideIchibaPanel .ichiba_mainitem .action .buy {
  1022. font-weight: bolder; color: #f60;
  1023. }
  1024. .sideIchibaPanel .ichiba_mainitem .itemname {
  1025. font-weight: bolder;
  1026. }
  1027. .sideIchibaPanel .ichiba_mainitem .maker {
  1028. font-size: 77%; margin-bottom: 2px;
  1029. }
  1030. .sideIchibaPanel .ichiba_mainitem .price {
  1031. }
  1032. .sideIchibaPanel .ichiba_mainitem .action .click {
  1033. font-weight: bolder;
  1034. }
  1035. .sideIchibaPanel .ichiba_mainitem .goIchiba {
  1036. font-size: 77%; margin: 5px 0;
  1037. }
  1038. .sideIchibaPanel .addIchiba, .sideIchibaPanel .reloadIchiba {
  1039. cursor: pointer;
  1040. }
  1041. .sideIchibaPanel .noitem {
  1042. cursor: pointer;
  1043. }
  1044.  
  1045. #outline .bottomAccessContainer {
  1046. position: absolute; top: 12px;
  1047. }
  1048. #outline .bottomConfButtonContainer {
  1049. position: absolute; top: 12px; right: 0px;
  1050. }
  1051. body.videoExplorer .bottomAccessContainer{
  1052. display: none;
  1053. }
  1054. #outline.under960 .bottomAccessContainer{
  1055. right: 60px;
  1056. }
  1057. .watchItLaterSettingMenu {
  1058. font-weight: bolder;
  1059. white-space: nowrap;
  1060. }
  1061. #outline .sidebar {
  1062. -webkit-transition: margin-top 0.3s ease-out;
  1063. transition: margin-top 0.3s ease-out;
  1064. }
  1065. #outline.under960 .sidebar {
  1066. margin-top: 24px;
  1067. }
  1068. #videoHeader.menuClosed .watchItLaterMenu, #videoHeader.menuClosed .hidariue { display: none; }
  1069. #videoHeader .watchItLaterMenu {
  1070. position: absolute; width: 100px; left: -55px; top: 32px;
  1071. }
  1072. {* タイトルクリックでヘッダが開閉できるのをわかりやすく *}
  1073. .videoDetailToggleButton:hover {
  1074. text-decoration: underline;
  1075. }
  1076. .videoDetailToggleButton:hover:after {
  1077. content: '▼';
  1078. position: absolute;
  1079. width: 32px;
  1080. height: 20px;
  1081. top: 0;
  1082. bottom: 0;
  1083. right: -32px;
  1084. margin: auto;
  1085. color: #888;
  1086. font-size: 80%;
  1087. }
  1088. .infoActive .videoDetailToggleButton:hover:after {
  1089. content: '▲';
  1090. }
  1091.  
  1092. {* プレイリスト出したり隠したり *}
  1093. #playlist>* {
  1094. -webkit-transition: opacity 0.6s; transition: opacity 0.6s;
  1095. }
  1096. body:not(.full_with_browser):not(.videoExplorer) #playlist.w_closing>* {
  1097. opacity: 0;
  1098. }
  1099. body:not(.full_with_browser):not(.videoExplorer) #playlist:not(.w_show){
  1100. position: absolute; top: -9999px;
  1101. }
  1102. #playlist.w_show{
  1103. {*max-height: 180px;*}
  1104. }
  1105. .playlistToggle:after {
  1106. content: "▼";
  1107. }
  1108. .playlistToggle.w_show:after {
  1109. content: "▲";
  1110. }
  1111.  
  1112. body.videoExplorer #content.w_adjusted #playlist .playlistInformation {
  1113. white-space: nowrap;
  1114. }
  1115. body.videoExplorer #content.w_adjusted #playlist .playlistInformation .playbackOption {
  1116. position: absolute;
  1117. }
  1118. body.videoExplorer #content.w_adjusted #playlist .playlistInformation .generationMessage{
  1119. margin-left: 90px; max-width: 350px; overflow: hidden; text-overflow: ellipsis;
  1120. }
  1121. body.videoExplorer #content.w_adjusted #playlist .playlistInformation .browserFullOption {
  1122. position: absolute; right: 0; top: 0;
  1123. }
  1124. body.videoExplorer #content.w_adjusted #playlist .playlistInformation .browserFullOption a {
  1125. background: #444;
  1126. }
  1127. #playlistContainerInner .thumbContainer, #playlistContainerInner .balloon{
  1128. cursor: move;
  1129. }
  1130.  
  1131.  
  1132. {* ページャーの字が小さくてクリックしにくいよね *}
  1133. #resultPagination {
  1134. padding: 5px; font-weight: bolder; border: 1px dotted silter; font-size: 130%;
  1135. }
  1136.  
  1137. #playlistContainer #playlistContainerInner .playlistItem .balloon {
  1138. bottom: auto; top: -2px; padding: auto;
  1139. }
  1140.  
  1141. body.w_channel #leftPanel .userIconContainer{
  1142. display: none;
  1143. }
  1144. {* WatchItLater設定パネル *}
  1145. #watchItLaterConfigPanel {
  1146. position: fixed; bottom: 0px; right: 16px; z-index: 10001;
  1147. width: 460px; padding: 0;
  1148. transition: transform 0.4s ease-in-out; -webkit-transition: -webkit-transform 0.4s ease-in-out;
  1149. transform-origin: 50% 0; -webkit-transform-origin: 50% 0;
  1150. transform: scaleY(0); -webkit-transform: scaleY(0);
  1151. }
  1152. #watchItLaterConfigPanel.open {
  1153. transform: scaleY(1); -webkit-transform: scaleY(1);
  1154. }
  1155. #watchItLaterConfigPanelShadow {
  1156. position: fixed; bottom: 16px; right: 16px; z-index: 10000;
  1157. width: 460px; height: 559px; padding: 0;
  1158. background: #000; {*box-shadow: 0 0 2px black; border-radius: 8px;*} -webkit-filter: opacity(70%);
  1159. transition: transform 0.4s ease-in-out; -webkit-transition: -webkit-transform 0.4s ease-in-out;
  1160. transform-origin: 50% 0; -webkit-transform-origin: 50% 0;
  1161. transform: scaleY(0); -webkit-transform: scaleY(0);
  1162. }
  1163. #watchItLaterConfigPanelShadow.open {
  1164. transform: scaleY(1); -webkit-transform: scaleY(1);
  1165. }
  1166. #watchItLaterConfigPanelShadowTop {
  1167. position: fixed; bottom: 563px; right:0px; z-index: 10000; background: #333;
  1168. width: 492px; height: 20px; padding: 0; border-radius: 32px; -webkit-filter: opacity(90%); display: none;
  1169. }
  1170. #watchItLaterConfigPanelOverShadow {
  1171. position: fixed; bottom: 575px; right: 0px; width: 488px; height: 8px;
  1172. box-shadow: 0 4px 16px #333;z-index: 10002; display: none;
  1173. }
  1174. #watchItLaterConfigPanel .head {
  1175. background-color: #CCCCCC;border-radius: 0;color: black;height: 50px;
  1176. overflow: hidden;padding: 5px 0 0 16px;position: relative;
  1177. }
  1178. #watchItLaterConfigPanel .head h2 {
  1179. font-size: 135%;
  1180. }
  1181. #watchItLaterConfigPanel .inner{
  1182. height: 500px; overflow-y: auto;border-width: 4px 16px 16px 16px; border-radius: 0 0 16px 16px;
  1183. border-style: solid;border-color: #ccc;
  1184. }
  1185. #watchItLaterConfigPanel ul{
  1186. border-style: inset; border-color: #ccc; border-width: 0 1px 0;
  1187. }
  1188. #watchItLaterConfigPanel ul.shortcutContainer{
  1189. border-width: 0 1px 1px;
  1190. }
  1191. #watchItLaterConfigPanel ul.videoStart{
  1192. border-width: 1px 1px 0;
  1193. }
  1194. #watchItLaterConfigPanel li{
  1195. }
  1196. #watchItLaterConfigPanel li:hover{
  1197. {*background: #ddd;*}
  1198. }
  1199. #watchItLaterConfigPanel li.buggy{
  1200. color: #888;
  1201. }
  1202. #watchItLaterConfigPanel label{
  1203. margin: 0 5px;
  1204. }
  1205. #watchItLaterConfigPanel label:hover{
  1206. }
  1207. #watchItLaterConfigPanel .foot {
  1208. text-align: right; padding: 0 12px;
  1209. }
  1210. #watchItLaterConfigPanel .closeButton{
  1211. border: 0 none;border-radius: 0 0 4px 4px;box-shadow: 0 1px 2px white;color: #666; border: 1px solid #999;
  1212. cursor: pointer;float: right;margin-top: 8px;position: absolute;right: 16px;
  1213. text-shadow: 0 1px 0 white;top: -10px; width: 60px;
  1214. }
  1215. #watchItLaterConfigPanel.autoBrowserFull_false .disableAutoBrowserFullIfNicowari,
  1216. #watchItLaterConfigPanel.autoBrowserFull_true .autoScrollToPlayer,
  1217. #watchItLaterConfigPanel.autoBrowserFull_true .autoOpenSearch,
  1218. #watchItLaterConfigPanel.removeLeftPanel_true .leftPanelJack {
  1219. color: #ccc; text-shadow: -1px -1px 0 #888;
  1220. }
  1221. #watchItLaterConfigPanel .reload .title:after {
  1222. content: ' (※)'; font-size: 80%; color: #900;
  1223. }
  1224. #watchItLaterConfigPanel .debugOnly {
  1225. display: none;
  1226. }
  1227. #watchItLaterConfigPanel.debugMode .debugOnly {
  1228. display: block; background: #888;
  1229. }
  1230. #watchItLaterConfigPanel .section {
  1231. border-style: solid;border-width: 10px 12px 10px 12px;color: white; font-size: 135%; position: relative;
  1232. font-weight: bolder; cursor: pointer; {*text-shadow: 2px 2px 1px #000000;*}
  1233. transition: border-width 0.2s ease-in-out 0.4s, color 0.3s; -webkit-transition: border-width 0.2s ease-in-out 0.4s, color 0.4s;
  1234. }
  1235. #watchItLaterConfigPanel .open .section {
  1236. border-width: 20px 12px 12px 12px;
  1237. transition: border-width 0.2s ease-in-out ; -webkit-transition: border-width 0.2s ease-in-out ;
  1238. }
  1239. #watchItLaterConfigPanel .section:hover:after {
  1240. content: '▼';
  1241. position: absolute; top: 0px; right: 10px; font-size: 150%;
  1242. transition: transform 0.2s ease-in-out 0.4s; -webkit-transition: -webkit-transform 0.2s ease-in-out 0.4s;
  1243. }
  1244. #watchItLaterConfigPanel .open .section:after {
  1245. content: '▼';
  1246. position: absolute; top: 0px; right: 10px; font-size: 150%;
  1247. transform: rotate(180deg); -webkit-transform: rotate(180deg);
  1248. transition: transform 0.2s ease-in-out ; -webkit-transition: -webkit-transform 0.2s ease-in-out;
  1249. }
  1250. #watchItLaterConfigPanel .section > div {
  1251. padding: 8px 0 8px 12px; box-shadow: 0 0 4px black;
  1252. }
  1253. #watchItLaterConfigPanel .section > div > span {
  1254. {*background: #333;*}
  1255. }
  1256. #watchItLaterConfigPanel li:not(.section) {
  1257. background: #fff; border-width: 0px 0px 0px 24px; border-style: solid; border-color: #fff;
  1258. max-height: 0px; overflow: hidden;
  1259. transition: max-height 0.4s ease-in-out , border-width 0.4s ease-in-out;
  1260. }
  1261. #watchItLaterConfigPanel .open li:not(.section) {
  1262. max-height: 100px; border-width: 4px 0px 4px 24px;
  1263. transition: max-height 0.4s ease-in-out 0.2s, border-width 0.4s ease-in-out 0.2s;
  1264. }
  1265. #watchItLaterConfigPanel .section .description{
  1266. display: block; font-size: 80%;;
  1267. }
  1268. #watchItLaterConfigPanel .shortcutSetting:not(.enable) span :not(.enable){
  1269. color: silver;
  1270. }
  1271. #watchItLaterConfigPanel .shortcutSetting .enable {
  1272. cursor: pointer; margin: auto 10px;
  1273. }
  1274. #watchItLaterConfigPanel .shortcutSetting .enable:before {
  1275. content: '○ ';
  1276. }
  1277. #watchItLaterConfigPanel .shortcutSetting.enable .enable:before {
  1278. content: '㋹ '; color: blue;
  1279. }
  1280. #watchItLaterConfigPanel .shortcutSetting .ctrl, #watchItLaterConfigPanel .shortcutSetting .alt, #watchItLaterConfigPanel .shortcutSetting .shift {
  1281. cursor: pointer; border: 2px outset; margin: 4px 4px; padding: 2px 4px; width: 180px; border-radius: 4px;background: #eee;
  1282. }
  1283. #watchItLaterConfigPanel .shortcutSetting.ctrl .ctrl, #watchItLaterConfigPanel .shortcutSetting.alt .alt, #watchItLaterConfigPanel .shortcutSetting.shift .shift {
  1284. border: 2px inset; color: blue;
  1285. }
  1286. #watchItLaterConfigPanel .hoverMenuDelay input {
  1287. width: 50px; ime-mode: disabled; text-align: center;
  1288. }
  1289.  
  1290.  
  1291. {* 動画検索画面に出るお気に入りタグ・お気に入りマイリスト *}
  1292. .videoExplorerMenu .watchItLaterMenu.open,
  1293. .videoExplorerMenu .watchItLaterMenu.opening {
  1294. background: -moz-linear-gradient(center top , #D1D1D1, #FDFDFD) repeat scroll 0 0 transparent !important;
  1295. background: -webkit-gradient(linear, left top, left bottom, from(#D1D1D1), to(#FDFDFD)) !important;
  1296. border-bottom: 0 !important;
  1297. }
  1298. .videoExplorerMenu .watchItLaterMenu {
  1299. position: relative;
  1300. {*background: -moz-linear-gradient(center top , whitesmoke 0%, #E1E1E1 100%) repeat scroll 0 0 transparent;*}
  1301. {*box-shadow: 0 -1px 1px rgba(0, 0, 0, 0.1) inset;*}
  1302. {*background: #f5f5f5;*}
  1303. border-bottom: 1px solid #CCCCCC;
  1304. }
  1305. .videoExplorerMenu .watchItLaterMenu:hover{
  1306. background: #dbdbdb;
  1307. }
  1308. .videoExplorerMenu .watchItLaterMenu {
  1309. padding: 0 12px; display: block; color: black;
  1310. }
  1311. .videoExplorerMenu .slideMenu{
  1312. width: 100%; height: auto !important;
  1313. overflow-x: hidden;
  1314. overflow-y: auto;
  1315. padding: 0;
  1316. background: #fdfdfd;
  1317. border-top: 0 !important;
  1318. display: block;
  1319. max-height: 0;
  1320. transition: max-height 0.5s ease-in-out;
  1321. }
  1322. .videoExplorerMenu .slideMenu.open {
  1323. max-height: 2000px;
  1324. transition: max-height 1s ease-in-out;
  1325. }
  1326. .videoExplorerMenu .toggleVideoExplorerMenu a {
  1327. color: black; display: block;
  1328. }
  1329. .videoExplorerMenu .toggleVideoExplorerMenu a:after {
  1330. content: "▼"; position: absolute; background: none; top: 0px; right: 10px; color: #ccc;
  1331. }
  1332. .videoExplorerMenu .toggleVideoExplorerMenu.open a:after {
  1333. content: "▲";
  1334. }
  1335.  
  1336. .videoRankingList .isCategory {
  1337. position: relative;
  1338. }
  1339.  
  1340. .rankingCategoryToggle {
  1341. position: absolute;
  1342. display: none;
  1343. height: 20px;
  1344. padding: 0px 8px;
  1345. right: 14px;
  1346. top: 0;
  1347. cursor: pointer;
  1348. border: 1px solid;
  1349. color: #666;
  1350. outline: none;
  1351. }
  1352. .rankingCategoryToggle::-moz-focus-inner {
  1353. border: 0px;
  1354. }
  1355. .slideMenu.open .isCategory:hover .rankingCategoryToggle {
  1356. display: block;
  1357. }
  1358. .categoryClose .rankingCategoryToggle .close, .rankingCategoryToggle .open{
  1359. display: none;
  1360. }
  1361. .categoryClose .rankingCategoryToggle .open{
  1362. display: inline;
  1363. }
  1364. .videoRankingList li:not(.isCategory) {
  1365. transition: max-height 0.5s;
  1366. max-height: 50px; overflow:hidden;
  1367. margin-left: 8px;
  1368. }
  1369. .videoRankingList .categoryClose:not(.isCategory) {
  1370. max-height: 0px;
  1371. }
  1372.  
  1373.  
  1374. .videoExplorerMenu .slideMenu ul{
  1375. }
  1376. .videoExplorerMenu .slideMenu ul li{
  1377. background: #fdfdfd; padding: 0; border: 0;font-size: 90%; height: auto !important;
  1378. }
  1379. .videoExplorerMenu .slideMenu ul li a{
  1380. line-height: 165%; background: none; display: block;
  1381. }
  1382. .videoExplorerMenu.w_touch .slideMenu ul li a{
  1383. line-height: 300%; font-size: 120%; color: black;
  1384. }
  1385. .videoExplorerMenu .slideMenu ul li a:before{
  1386. background: url("http://uni.res.nimg.jp/img/zero_my/icon_folder_default.png") no-repeat scroll 0 0 transparent;
  1387. display: inline-block;
  1388. height: 14px;
  1389. margin: -4px 4px 0 0;
  1390. vertical-align: middle;
  1391. width: 18px;
  1392. content: ""
  1393. }
  1394. .videoExplorerMenu .slideMenu ul li a.defMylist:before{ background-position: 0 -253px;}
  1395. .videoExplorerMenu .slideMenu ul li.folder0 a:before{ background-position: 0 0;}
  1396. .videoExplorerMenu .slideMenu ul li.folder1 a:before{ background-position: 0 -23px;}
  1397. .videoExplorerMenu .slideMenu ul li.folder2 a:before{ background-position: 0 -46px;}
  1398. .videoExplorerMenu .slideMenu ul li.folder3 a:before{ background-position: 0 -69px;}
  1399. .videoExplorerMenu .slideMenu ul li.folder4 a:before{ background-position: 0 -92px;}
  1400. .videoExplorerMenu .slideMenu ul li.folder5 a:before{ background-position: 0 -115px;}
  1401. .videoExplorerMenu .slideMenu ul li.folder6 a:before{ background-position: 0 -138px;}
  1402. .videoExplorerMenu .slideMenu ul li.folder7 a:before{ background-position: 0 -161px;}
  1403. .videoExplorerMenu .slideMenu ul li.folder8 a:before{ background-position: 0 -184px;}
  1404. .videoExplorerMenu .slideMenu ul li.folder9 a:before{ background-position: 0 -207px;}
  1405.  
  1406. .videoExplorerMenu .slideMenu ul li.g_ent2 a:before { background-position: 0 -23px;}
  1407. .videoExplorerMenu .slideMenu ul li.g_life2 a:before { background-position: 0 -46px;}
  1408. .videoExplorerMenu .slideMenu ul li.g_politics a:before { background-position: 0 -69px;}
  1409. .videoExplorerMenu .slideMenu ul li.g_tech a:before { background-position: 0 -92px;}
  1410. .videoExplorerMenu .slideMenu ul li.g_culture2 a:before { background-position: 0 -115px;}
  1411. .videoExplorerMenu .slideMenu ul li.g_other a:before { background-position: 0 -138px;}
  1412. .videoExplorerMenu .slideMenu ul li.r18 a:before { background-position: 0 -207px;}
  1413. .videoExplorerMenu .slideMenu ul li.all a.all,
  1414. .videoExplorerMenu .slideMenu ul li.g_ent2 a.g_ent2,
  1415. .videoExplorerMenu .slideMenu ul li.g_life2 a.g_life2,
  1416. .videoExplorerMenu .slideMenu ul li.g_politics a.g_politics,
  1417. .videoExplorerMenu .slideMenu ul li.g_tech a.g_tech,
  1418. .videoExplorerMenu .slideMenu ul li.g_culture2 a.g_culture2,
  1419. .videoExplorerMenu .slideMenu ul li.g_other a.g_other,
  1420. .videoExplorerMenu .slideMenu ul li.r18 a.r18
  1421. { font-weight: bolder; border-top: 1px dotted #ccc; }
  1422.  
  1423.  
  1424. .videoExplorerMenu .slideMenu ul li a:after{
  1425. background: none !important;
  1426. }
  1427. .videoExplorerMenu .slideMenu ul li a:hover{
  1428. background: #f0f0ff;
  1429. }
  1430. .videoExplorerMenu .slideMenu ul .reload{
  1431. cursor: pointer; border: 1px solid; padding: 0;
  1432. }
  1433.  
  1434. .videoExplorerMenu .tagSearchHistory {
  1435. border-radius: 0px; margin-top: 2px; padding: 4px; background: #ccc;
  1436. }
  1437. .videoExplorerMenu .itemList > li, #videoExplorerExpand {
  1438. background: #f5f5f5;
  1439. }
  1440. .videoExplorerMenu .itemList ul > li:hover {
  1441. background: #e7e7e7;
  1442. }
  1443. .videoExplorerMenu .itemList ul > li.active {
  1444. background: #343434;
  1445. }
  1446.  
  1447.  
  1448. {* 動画タグが1行以下の時 *}
  1449. body:not(.full_with_browser) .tag1Line #videoTagContainer .tagInner #videoHeaderTagList .toggleTagEdit {
  1450. height: 12px; padding: 6px 4px 2px;
  1451. }
  1452. body:not(.full_with_browser) .tag1Line #videoTagContainer .tagInner #videoHeaderTagList .toggleTagEdit .toggleText{
  1453. display: none;
  1454. }
  1455. {* 動画タグが2行以下の時 *}
  1456. body:not(.full_with_browser) .tag2Lines #videoTagContainer .tagInner #videoHeaderTagList .toggleTagEdit {
  1457. height: 36px;
  1458. }
  1459. {* タグ領域とプレイヤーの隙間をなくす *}
  1460. body:not(.full_with_browser) #videoTagContainer, body:not(.full_with_browser) #videoHeader .videoMenuToggle {
  1461. margin-bottom: -10px;
  1462. }
  1463. #videoHeaderMenu .searchContainer .searchText {
  1464. margin-top: -8px;
  1465. }
  1466. #videoHeaderMenu .clear-button {
  1467. position: absolute;
  1468. top: -8px;
  1469. right: 0px;
  1470. }
  1471.  
  1472. body.size_small #playerContainerWrapper {
  1473. padding: 0;
  1474. }
  1475.  
  1476. {* ニュース履歴 *}
  1477. body.videoExplorer #textMarquee .openNewsHistory, body.videoExplorer #textMarquee .newsHistory {
  1478. display: none;
  1479. }
  1480. #textMarquee .openNewsHistory {
  1481. position: absolute; width: 30px;
  1482. font-size: 13px; padding: 0; margin: 0; height: 28px;
  1483. cursor: pointer;
  1484. bottom: 0;
  1485. background: none repeat scroll 0 0 transparent;
  1486. border: 1px none;
  1487. border-radius: 2px 2px 2px 2px;
  1488. cursor: pointer;
  1489. right: 18px;
  1490. z-index: 200;
  1491. }
  1492. #textMarquee .newsHistory {
  1493. position: absolute;
  1494. bottom: 0px; right: 0px; width: 100%;
  1495. max-height: 132px;
  1496. min-height: 40px;
  1497. overflow-y: auto;
  1498. overflow-x: hidden;
  1499. z-index: 1;
  1500. padding: 4px;
  1501. display: none;
  1502. background: #333;
  1503. text-align: left;
  1504. font-size: 14px;
  1505. padding: 0;
  1506. }
  1507. #textMarquee .newsHistory li{
  1508. padding: 0 2px;
  1509. }
  1510. #textMarquee .newsHistory li:nth-child(odd){
  1511. background: #444;
  1512. }
  1513. #textMarquee .newsHistory li:nth-child(even){
  1514. background: #333;
  1515. }
  1516. body.full_with_browser.hideNewsInFull #textMarquee .newsHistory {
  1517. display: none !important;
  1518. }
  1519. body #popupMarquee {
  1520. width: 360px;
  1521. }
  1522. {* 半透明だとflashの上に来ると描画されないので強制的に黒にする(Chromeは平気) *}
  1523. body.full_with_browser #popupMarquee.popupMarqueeBottomLeft {
  1524. background: #000 !important; width: 400px; opacity: 1;
  1525. }
  1526. body.full_with_browser #playerContainer {
  1527. margin-left: 0 !important;
  1528. }
  1529. body:not(.full_with_browser) #playerContainer {
  1530. {*top: -8px;*}
  1531. }
  1532. body:not(.full_with_browser) #playerContainerWrapper {
  1533. padding: 0px;
  1534. }
  1535. body.full_with_browser #playerContainer, body.size_small #playerContainer {
  1536. top: auto;
  1537. }
  1538. body.full_with_browser.no_setting_panel .videoExplorerMenu {
  1539. display:none;
  1540. }
  1541.  
  1542.  
  1543. body:not(.videoExplorer) {*#playlist:not(.nico-bucket-videoExplorer-b)*} #videoExplorerExpand {
  1544. display: none;
  1545. }
  1546. #outline .openVideoExplorer {
  1547. display: none;
  1548. }
  1549. #outline.w_hideSearchExpand .openVideoExplorer {
  1550. display: inline-block;
  1551. }
  1552.  
  1553. .videoExplorerMenu .quickSearchInput {
  1554. background: none repeat scroll 0 0 #F4F4F4;
  1555. border: 1px inset silver;
  1556. left: 60px;
  1557. padding-left: 4px;
  1558. position: absolute;
  1559. top: 2px;
  1560. width: 180px;
  1561. }
  1562. .videoExplorerMenu.w_touch .quickSearchInput {
  1563. top: 4px; font-size: 20px;
  1564. }
  1565. .videoExplorerMenu .clear-button {
  1566. position: absolute;
  1567. width: 20px;
  1568. height: 20px;
  1569. top: 4px;
  1570. left: 250px;
  1571. line-height: 18px;
  1572. font-size: 16px;
  1573. padding: 0;
  1574. background: #e5e5e5;
  1575. text-align: center;
  1576. color: #999;
  1577. cursor: pointer;
  1578. display: none;
  1579. box-sizing: border-box;
  1580. -webkit-box-sizing: border-box;
  1581. -moz-box-sizing: border-box;
  1582. }
  1583.  
  1584. .videoExplorerContent .contentItemList .column4 {
  1585. text-align: center;
  1586. }
  1587. .videoExplorerContent .contentItemList .column4 .balloon {
  1588. bottom: auto; top: 10px;
  1589. }
  1590. .videoExplorerContent .contentItemList .column4 .videoInformation>.info {
  1591. font-size: 85%;
  1592. }
  1593. .videoExplorerContent .contentItemList .column4 .videoInformation>.info .info{
  1594. color: #000;
  1595. }
  1596. .videoExplorerContent .contentItemList .column4 .videoInformationOuter {
  1597. width: 100px; height: 48px; margin: auto; color: #666; text-align: left;
  1598. }
  1599. .videoExplorerBody .videoExplorerContent .contentItemList.column4 .item {
  1600. height: 220px;
  1601. }
  1602. .column1 .itemMylistComment {
  1603. font-size: 85%; color: #666; display: none;
  1604. color: #400; border: 1px solid #ccc; padding: 0 4px 0px; line-height: 130%; border-radius: 4px;
  1605. }
  1606. .column1 .itemMylistComment:before {
  1607. content: 'マイリストコメント ';
  1608. background: #ccc; border-radius: 0 0 8px 0; display: inline-block; margin: 0 4px 4px -4px; padding: 2px;
  1609. min-width: 100px;
  1610. }
  1611. .log-user-video-review .column1 .itemMylistComment {
  1612. color: #004;
  1613. }
  1614. .log-user-video-review .column1 .itemMylistComment:before {
  1615. content: 'レビュー ';
  1616. }
  1617. .column1 .itemMylistComment:after {
  1618. content: '';
  1619. }
  1620. .column1 .itemMylistComment pre {
  1621. font-family: inherit;
  1622. display: inline;
  1623. white-space: pre-wrap;
  1624. }
  1625.  
  1626. .videoExplorerContent .contentItemList .column1 .nicorepoOwnerIconContainer {
  1627. display: none;
  1628. }
  1629. .videoExplorerContent .contentItemList .nicorepoResult .column1 .nicorepoOwnerIconContainer {
  1630. float: right; display: block;
  1631. padding: 24px 14px 0 4px;
  1632. }
  1633. .videoExplorerContent .contentItemList .column1 .nicorepoOwnerIconContainer img {
  1634. height: 48px;
  1635. }
  1636.  
  1637. .videoExplorerBody.dummyMylist #searchResultContainer .favMylistEditContainer,
  1638. .videoExplorerBody.dummyMylist:not(.ranking) #searchResultMylistSortOptions,
  1639. .videoExplorerBody.dummyMylist .favMylistEditContainer,
  1640. .videoExplorerBody.dummyMylist:not(.ownerNicorepo) #searchResultHeader {
  1641. display: none !important;
  1642. }
  1643.  
  1644. .videoExplorerContent .contentItemList .thumbnailHoverMenu {
  1645. position: absolute; padding: 0; z-index: 100;
  1646. display: none;
  1647. bottom: -1px; left: 0px;
  1648. }
  1649. .videoExplorerContent .contentItemList .deleteFromMyMylist {
  1650. cursor: pointer; font-size: 70%; border: 1px solid #ccc; padding: 0;
  1651. display: none;
  1652. }
  1653. .videoExplorerContent .contentItemList .showLargeThumbnail {
  1654. cursor: pointer; font-size: 70%; border: 1px solid #ccc;;
  1655. }
  1656. .videoExplorerContent .contentItemList .showLargeThumbnail {
  1657. padding: 0 4px;
  1658. }
  1659. .videoExplorerContent .contentItemList .item:hover .thumbnailHoverMenu {
  1660. display: block;
  1661. }
  1662. .videoExplorerContent .contentItemList .log-user-video-upload {
  1663. background: #ffe; border-radius: 4px;
  1664. }
  1665. .videoExplorerContent .contentItemList .nicorepoResult .itemVideoDescription, .videoExplorerContent .contentItemList .nicorepoResult .videoTitle{
  1666. }
  1667. .videoExplorerContent .contentItemList.channelGuideVideo {
  1668. background: #eff; {* 検索結果にチャンネル動画が紛れ込むようになったのでわかりやすく *}
  1669. }
  1670.  
  1671. #videoExplorer.w_deflist .videoExplorerBody.isMine.enableMylistDeleteButton .item:hover .deleteFromMyMylist,
  1672. #videoExplorer.w_mylist .videoExplorerBody.isMine.enableMylistDeleteButton .item:hover .deleteFromMyMylist
  1673. {
  1674. display: inline-block;
  1675. }
  1676.  
  1677. #playlist .generationMessage {
  1678. cursor: pointer;
  1679. }
  1680. #playlist .generationMessage:hover {
  1681. text-decoration: underline;
  1682. }
  1683. #playlist .generationMessage:after {
  1684. content: "▼";
  1685. }
  1686.  
  1687. #yukkuriPanel {
  1688. position: fixed; z-index: 1500; bottom: 0; left: 0; display: inline-block;
  1689. transition: bottom 0.2s ease;
  1690. }
  1691. #yukkuriPanel.mylistPanelLeft {
  1692. bottom: 24px;
  1693. }
  1694. body.w_noNicoru .nicoru-button{
  1695. left: -9999; display: none !important;
  1696. }
  1697. body.w_noNicoru .menuOpened #videoMenuTopList li.videoMenuListNicoru .nicoru-button{
  1698. display: block !important;
  1699. }
  1700. body.w_noNicoru #videoTagContainer .tagInner #videoHeaderTagList li {
  1701. margin: 0 18px 4px 0;
  1702. }
  1703. body.w_noNicoru #videoTagContainer .tagInner #videoHeaderTagList li .tagControlContainer, body.w_noNicoru #videoTagContainer .tagInner #videoHeaderTagList li .tagControlEditContainer {
  1704. padding: 1px 0;
  1705. }
  1706.  
  1707. .userProfile.w_touch {
  1708. font-size: 150%; line-height: 120%;
  1709. }
  1710. .resultPagination.w_touch {
  1711. font-size: 200%;
  1712. }
  1713. .resultPagination.w_touch li{
  1714. padding: 4px 16px;
  1715. }
  1716. select.w_touch {
  1717. font-size: 200%;
  1718. }
  1719. {* 真・browserFullモード *}
  1720. body.full_with_browser.hideCommentInput #nicoplayerContainerInner {
  1721. {* コメント入力欄は動画上表示にするのではなく、画面外に押し出す事によって見えなくする *}
  1722. margin-top: -10px; margin-bottom: -30px;
  1723. }
  1724. body.full_with_browser.trueBrowserFull #playerContainerWrapper {
  1725. margin: 0 !important;
  1726. }
  1727. body.full_with_browser.trueBrowserFull #playlist {
  1728. display: none;
  1729. }
  1730. body.full_with_browser.trueBrowserFull:not(.w_fullScreenMenu) .mylistPopupPanel.fixed,body.full_with_browser.trueBrowserFull .yukkuriButton { display:none; }
  1731. #trueBrowserFullShield {
  1732. -webkit-transition: opacity 0.2s ease-out;
  1733. position:absolute;
  1734. display: none;
  1735. }
  1736. body.full_with_browser #trueBrowserFullShield {
  1737. background: black;
  1738. display: block;
  1739. bottom: 100px;
  1740. right: 50px;
  1741. z-index: 10000;
  1742. min-width: 400px;
  1743. cursor: nw-resize;
  1744. opacity: 0;
  1745. color: white;
  1746. box-shadow: 2px 2px 2px silver;
  1747. border-radius: 4px;
  1748. }
  1749. body.full_with_browser #trueBrowserFullShield .title {
  1750. color: #ffc; font-size: 120%;
  1751. }
  1752. body.full_with_browser #trueBrowserFullShield .ownerIcon {
  1753. float: left; height: 55px; padding: 8px;
  1754. }
  1755. body.full_with_browser #trueBrowserFullShield:hover, body.full_with_browser #trueBrowserFullShield.active, body.w_fullScreenMenu #trueBrowserFullShield {
  1756. opacity: 1;
  1757. }
  1758. body:not(.full_with_browser) #trueBrowserFullShield { display: none; }
  1759.  
  1760. #sharedNgSettingContainer {
  1761. display: inline-block; font-size: 80%; position: absolute; top: -18px; left: 5px;
  1762. }
  1763. #sharedNgSetting {
  1764. background: #ddd; border: 1px solid silver;
  1765. }
  1766. {* ニュース消す *}
  1767. #content.noNews #textMarquee {
  1768. display: none !important;
  1769. }
  1770. body:not(.videoExplorer):not(.full_with_browser) #content.noNews #playerContainer {
  1771. min-height: 461px;
  1772. }
  1773. body:not(.videoExplorer):not(.setting_panel):not(.full_with_browser) #content.noNews #playerTabWrapper {
  1774. height: auto !important; position: absolute; bottom: 18px;
  1775. }
  1776. body:not(.videoExplorer):not(.setting_panel):not(.full_with_browser) #content.noNews #playerTabContainer {
  1777. bottom: -17px;
  1778. }
  1779. body:not(.videoExplorer):not(.setting_panel):not(.full_with_browser) #content.noNews #playerContainer.appli_panel #playerTabContainer {
  1780. bottom: 20px;
  1781. }
  1782. #playerTabWrapper.w_videoInfo #playerTabContainer, #playerTabWrapper.w_ichiba #playerTabContainer, #playerTabWrapper.w_review #playerTabContainer {
  1783. bottom: 0px !important;
  1784. }
  1785. body:not(.videoExplorer):not(.setting_panel):not(.full_with_browser) #content.noNews #playerTabWrapper.w_videoInfo,
  1786. body:not(.videoExplorer):not(.setting_panel):not(.full_with_browser) #content.noNews #playerTabWrapper.w_ichiba,
  1787. body:not(.videoExplorer):not(.setting_panel):not(.full_with_browser) #content.noNews #playerTabWrapper.w_review
  1788. {
  1789. height: auto !important; position: absolute; bottom: 2px;
  1790. }
  1791. {* body:not(.videoExplorer):not(.setting_panel):not(.full_with_browser) #content.noNews #leftPanel {
  1792. height: auto !important; position: absolute; bottom: 2px;
  1793. }*}
  1794. body:not(.videoExplorer):not(.setting_panel):not(.full_with_browser) #content.noNews #playerCommentPanel {
  1795. height: 100% !important;
  1796. }
  1797. body:not(.videoExplorer):not(.setting_panel):not(.full_with_browser) #content.noNews #playerContainer.appli_panel #appliPanel {
  1798. bottom: -18px !important;
  1799. }
  1800. body:not(.videoExplorer):not(.setting_panel):not(.full_with_browser) #content.noNews #playerContainer {
  1801. height: auto;
  1802. }
  1803. #outline.noIchiba #nicoIchiba, #outline.noReview #videoReview{
  1804. display: none;
  1805. }
  1806. #bottomContentTabContainer.noBottom .outer, #bottomContentTabContainer.noBottom #pageFooter {
  1807. display: none;
  1808. }
  1809. #bottomContentTabContainer.noBottom #outline {
  1810. background: #141414; padding-top: 0; padding-bottom: 35px;
  1811. }
  1812.  
  1813. #content.w_flat_gray #playerContainerWrapper {
  1814. background: #666;
  1815. }
  1816. #content.w_flat_white #playerContainerWrapper {
  1817. background: #f4f4f4;
  1818. }
  1819. #content.w_flat_gray #wallImageContainer, #content.w_flat_white #wallImageContainer,
  1820. #content.w_flat_gray #chipWallList, #content.w_flat_white #chipWallList {
  1821. display: none !important;
  1822. }
  1823. #content #chipWallList {
  1824. right: auto; left: -42px;
  1825. }
  1826. #content #playlist .playlistInformation {
  1827. background: #444;
  1828. }
  1829. #content #videoExplorerExpand a {
  1830. text-shadow: none;
  1831. }
  1832.  
  1833. .videoMenuToggle {
  1834. -webkit-transform-origin: 100% 100%; -webkit-transition: -webkit-transform 0.4s;
  1835. transform-origin: 100% 100%; transition: transform 0.4s;
  1836. z-index: 1000;
  1837. }
  1838. #content.w_compact .videoHeaderTitle {
  1839. letter-spacing: -1px;
  1840. }
  1841. #content.w_compact .videoDetailExpand .arrow {
  1842. position: absolute; top: 8px; right: -24px;
  1843. }
  1844. #content.w_compact .tag1Line .videoMenuToggle {
  1845. transform: scale(0.8, 0.41); -webkit-transform: scale(0.8, 0.41);
  1846. }
  1847. #content.w_compact .tag2Lines .videoMenuToggle {
  1848. transform: scale(0.8); -webkit-transform: scale(0.8);
  1849. }
  1850. #content.w_compact #topVideoInfo .parentVideoInfo {
  1851. margin-top: -9px; margin-bottom: 9x;
  1852. }
  1853. #content.w_compact #topVideoInfo .parentVideoInfo .cct{
  1854. margin-bottom: 0;
  1855. }
  1856. #content.w_compact #topVideoInfo .parentVideoInfo .videoThumb{
  1857. margin-top: 4px;
  1858. }
  1859. #content.w_compact #topVideoInfo .ch_prof, #content.w_compact #topVideoInfo .userProfile {
  1860. min-width: 297px; margin-top: -1px; border: 1px solid #e7e7e7;
  1861. }
  1862. #content.w_compact #videoHeaderDetail .videoDetailExpand{
  1863. height: auto; padding: 0;
  1864. }
  1865. #content.w_compact #topVideoInfo .videoDescription.description {
  1866. background: #fff; margin: 10px 0 0;padding: 4px ;width: 1000px;{* base - 8 *} {*font-size: 90%;*}
  1867. }
  1868.  
  1869. {* 本家の幅が変わったら変える必要がある。 変数化した方が楽かも base = 1008 *}
  1870. body:not(.full_with_browser):not(.videoExplorer).size_normal #content.w_compact.w_wide #topVideoInfo .videoDescription.description {
  1871. width: 1318px; {* base + 310 *}
  1872. }
  1873. body:not(.full_with_browser):not(.videoExplorer).size_normal #content.w_compact #topVideoInfo .videoDescription.description {
  1874. width: 1226px; {* base + 218 *}
  1875. }
  1876. body:not(.full_with_browser) #content.w_compact.w_wide #topVideoInfo .videoDescription.description {
  1877. width: 1092px; {* base + 84 *}
  1878. }
  1879. body:not(.full_with_browser).size_normal #content.w_compact.w_wide #videoTagContainer {
  1880. width: 1263px; {* base + 255 *}
  1881. }
  1882. body:not(.full_with_browser) #content.w_compact.w_wide #videoTagContainer {
  1883. width: 1040px; {* base + 32 *}
  1884. }
  1885. body:not(.full_with_browser) #content.w_compact #videoTagContainer {
  1886. width: 948px; {* base - 60 *}
  1887. }
  1888. body:not(.full_with_browser) #content.w_compact #videoHeader, #foot_inner {
  1889. width: 1008px; {* base + 48 *}
  1890. }
  1891. body:not(.full_with_browser).size_normal #content.w_compact #videoHeader, .size_normal #foot_inner {
  1892. width: 1234px; {* base + 226 *}
  1893. }
  1894. body:not(.full_with_browser) #content.w_compact.w_wide #videoHeader {
  1895. width: 1100px;
  1896. }
  1897. body:not(.full_with_browser).size_normal #content.w_compact.w_wide #videoHeader {
  1898. width: 1326px;
  1899. }
  1900.  
  1901. #content.w_compact #topVideoInfo .videoMainInfoContainer{
  1902. padding: 0;
  1903. }
  1904. #content.w_compact #videoDetailInformation{
  1905. border-top: 0;
  1906. }
  1907. #content.w_compact #videoHeaderMenu .searchContainer {
  1908. top: -16px;
  1909. }
  1910. #content.w_compact .videoInformation{
  1911. margin: -4px 0 ;
  1912. }
  1913. #content.w_compact #topVideoInfo .videoStats {
  1914. margin-bottom: 2px;
  1915. }
  1916. body:not(.full_with_browser) #content.w_compact #videoTagContainer .tagInner #videoHeaderTagList .toggleTagEdit {
  1917. width: 72px;
  1918. }
  1919. body:not(.full_with_browser) #content.w_compact #videoTagContainer .tagInner #videoHeaderTagList {
  1920. padding-left: 85px;
  1921. }
  1922. body.full_with_browser #videoHeaderTagList { background: #fafafa; }
  1923. #content.w_compact #topVideoInfo {
  1924. margin: 4px 0 4px;
  1925. }
  1926. #content.w_compact #topVideoInfo .videoShareLinks .socialLinks {
  1927. margin-top: -6px;
  1928. }
  1929. #outline.w_compact #videoInfoHead{
  1930. margin: 0 ;
  1931. }
  1932. #outline.w_compact .videoInformation #videoTitle {
  1933. margin: -4px 0 0;
  1934. }
  1935. #outline.w_compact .videoInformation #videoStats {
  1936. margin-top: -4px;
  1937. }
  1938. #outline.w_compact .videoInformation #videoStats .ranking {
  1939. margin: 0 0 4px;
  1940. }
  1941. #outline.w_compact #videoShareLinks {
  1942. margin: 0;
  1943. }
  1944. #outline.w_compact #bottomVideoDetailInformation {
  1945. margin: -18px 0 0;
  1946. }
  1947. #outline.w_compact .infoHeadOuter .videoEditMenuExpand {
  1948. position: absolute; top: 0;
  1949. }
  1950. #outline.w_compact .videoEditMenu {
  1951. margin: 0;
  1952. }
  1953. #outline.w_compact .videoDescription {
  1954. font-size: 90%; margin-top: -8px; padding: 0 0 4px 4px;
  1955. }
  1956. #outline.w_compact #videoComment {
  1957. margin: 0px; border: 1px solid silver; border-radius: 4px 4px 4px 4px; padding: 0 4px;
  1958. }
  1959. #outline.w_compact #videoComment h4{
  1960. padding-left: 4px;
  1961. }
  1962. #outline.w_compact .videoMainInfoContainer {
  1963. border-bottom: 0; margin-bottom: 0;
  1964. }
  1965. #outline.w_compact {
  1966. border-bottom: 0; margin-bottom: 0;
  1967. }
  1968.  
  1969. #outline.w_compact .sidebar { width: 300px; }
  1970.  
  1971. #outline.w_compact #ichibaMain dl.ichiba_mainitem {
  1972. margin: 0 22px 30px 0;
  1973. }
  1974. #footer { z-index: 1; }
  1975.  
  1976. body.en-us #playerAlignmentArea, body.zh-tw #playerAlignmentArea {
  1977. {*padding-right: 0;*}
  1978. }
  1979. #footer .toggleBottom {
  1980. cursor: pointer; text-align: center; width: 200px; padding: 0px 12px; margin: auto; border-radius: 16px 16px 0 0;
  1981. border: 1px solid #333; background: #666; transition: background 0.4s ease-out, box-shadow 0.4s;
  1982. }
  1983. #footer:hover .toggleBottom {
  1984. border: 1px outset; background: #ccc;
  1985. }
  1986. #footer .toggleBottom:hover {
  1987. box-shadow: 0px 0px 8px #fff;
  1988. }
  1989. #footer.noBottom .toggleBottom {
  1990. border-radius: 0 0 16px 16px;
  1991. }
  1992. #footer .toggleBottom .openBottom, #footer.noBottom .toggleBottom .closeBottom {
  1993. display: none;
  1994. }
  1995. #footer.noBottom .toggleBottom .openBottom {
  1996. display: block;
  1997. }
  1998. #footer .toggleBottom>div {
  1999. -webkit-transform: scaleX(3); transform: scaleX(3);
  2000. }
  2001. #footer .toggleBottom {
  2002. cursor: pointer; text-align: center; width: 200px; padding: 0px 12px; margin: auto; border-radius: 16px 16px 0 0;
  2003. border: 1px solid #333; background: #666; transition: background 0.4s ease-out, box-shadow 0.4s;
  2004. }
  2005. #footer:hover .toggleBottom {
  2006. border: 1px outset; background: #ccc;
  2007. }
  2008. #footer .toggleBottom:hover {
  2009. box-shadow: 0px 0px 8px #fff;
  2010. }
  2011.  
  2012. #footer.noBottom #foot_inner { padding: 0; }
  2013. #footer.noBottom a:nth-of-type(3):after, #footer.noBottom a:nth-of-type(6):after {
  2014. content: ' | '; color: white;
  2015. }
  2016. #footer.noBottom br { display: none; }
  2017. html { background: #141414; }
  2018. .videoExplorer #videoExplorer,
  2019. .videoExplorer #videoExplorer .videoExplorerBody,
  2020. .videoExplorerContentWrapper
  2021. {
  2022. background: none;
  2023. }
  2024.  
  2025. .animateBlink {
  2026. -webkit-transition: 1s ease-in; transition: 1s ease-in;
  2027. }
  2028.  
  2029. .w_compact .toggleDetailExpand, .w_compact .shortVideoInfo {
  2030. display: none;
  2031. }
  2032. .videoDetailToggleButton {
  2033. cursor: pointer;
  2034. }
  2035. #leftPanel {
  2036. {*border-radius: 4px 4px 4px 4px;*}
  2037. display: none; padding: 0; position: absolute; text-align: left; top: 0; z-index: 101;
  2038. }
  2039. body.ja-jp #leftPanel { display: none; }
  2040. body:not(.videoExplorer) #leftPanel { display: none; }
  2041.  
  2042.  
  2043. body.full_with_browser #playerTabWrapper, body.full_with_browser:not(.videoExplorer) .w_wide #playerTabWrapper {
  2044. top: auto !important; bottom: 3000px !important; right: 50px !important;
  2045. transition: bottom 0.2s ease-out; max-height: 500px;
  2046. }
  2047.  
  2048. body.full_with_browser.w_fullScreenMenu:not(.videoExplorer) #playerTabWrapper {
  2049. top: auto !important; bottom: 200px !important; right: 50px !important;
  2050. }
  2051.  
  2052. #fullScreenMenuContainer { display: none; }
  2053. body.full_with_browser #fullScreenMenuContainer {
  2054. display: block; position: absolute; bottom: 3000px; left: 50px; z-index: 10000;
  2055. background: #fff; cursor: pointer; transition: bottom 0.2s ease-out;
  2056. }
  2057. body.full_with_browser.w_fullScreenMenu #fullScreenMenuContainer {
  2058. bottom: 100px;
  2059. }
  2060.  
  2061. #fullScreenMenuContainer .button {
  2062. cursor: pointer; transition: color 0.4s ease-out;
  2063. }
  2064. #fullScreenMenuContainer .modeStatus { display: none; font-weight: bolder; }
  2065. body.trueBrowserFull #fullScreenMenuContainer .fullScreenModeSwitch { color: blue; }
  2066. body:not(.trueBrowserFull) #fullScreenMenuContainer .fullScreenModeSwitch .mode_normal,
  2067. body.trueBrowserFull #fullScreenMenuContainer .fullScreenModeSwitch .mode_noborder { display: inline; }
  2068.  
  2069. #nicoplayerContainerInner.stageVideo #fullScreenMenuContainer .stageVideoSwitch { color: blue; }
  2070. #nicoplayerContainerInner:not(.stageVideo) #fullScreenMenuContainer .stageVideoSwitch .mode_off,
  2071. #nicoplayerContainerInner.stageVideo #fullScreenMenuContainer .stageVideoSwitch .mode_on { display: inline; }
  2072.  
  2073.  
  2074. body.full_with_browser.w_fullScreenMenu .videoHeaderOuter {
  2075. position: absolute; z-index: 1000; width: 100%;
  2076. }
  2077. body.full_with_browser.w_fullScreenMenu #videoTagContainer { width: 100%; display: block; }
  2078.  
  2079. .popupMarqueeContent {
  2080. background: black;
  2081. }
  2082.  
  2083. #videoExplorer, #playlist {
  2084. transition: margin-left 0.2s ease-in-out;
  2085. }
  2086.  
  2087. .dummyMylist .editFavorite {
  2088. display: none;
  2089. }
  2090.  
  2091. {* 不要な時まで横スクロールバーが出てしまうので *}
  2092. #songrium_inline { overflow: hidden; }
  2093.  
  2094. .sideVideoInfo .nextPlayButton {
  2095. position: absolute;
  2096. margin-top: -6px;
  2097. margin-left: -30px;
  2098. width: 30px;
  2099. height: 30px;
  2100. background: url(http://res.nimg.jp/img/watch_q9/icon_nextplay.png);
  2101. {*background: url("http://res.nimg.jp/img/watch_zero/videoexplorer-s90d011f9a7.png") no-repeat scroll -37px 0 rgba(0, 0, 0, 0);*}
  2102. z-index: 100;
  2103. cursor: pointer;
  2104. text-indent: -999em;
  2105. overflow: hidden;
  2106. display: inline-block;
  2107. -webkit-transform: scale(1.0); transform: scale(1.0);
  2108. }
  2109.  
  2110. .nextPlayButton {
  2111. -webkit-transform: scale(1.5); transform: scale(1.5);
  2112. transition: transform 0.1s ease; -webkit-transition: -webkit-transform 0.1s ease;
  2113. }
  2114. .sideVideoInfo .nextPlayButton:hover {
  2115. -webkit-transform: scale(1.5); transform: scale(1.5);
  2116. }
  2117. .nextPlayButton:active, .sideVideoInfo .nextPlayButton:active {
  2118. -webkit-transform: scale(1.2); transform: scale(1.2);
  2119. }
  2120.  
  2121. .sideVideoInfo .nextPlayButton:active {
  2122. background-position-y: 30px;
  2123. }
  2124.  
  2125. body.w_disableHorizontalScroll {
  2126. overflow-x: hidden !important;
  2127. }
  2128.  
  2129. #videoTagContainerPin { display: none !important; } {* タグを固定しているか4行以上の時に現われるピン *}
  2130.  
  2131. .w_adjusted #selectionSideAdAds >* {
  2132. width: 100%; height: auto; max-width: 300px; max-height: 250px;
  2133. }
  2134.  
  2135. {* *}
  2136. .w_noHover {
  2137. pointer-events: none !important;
  2138. }
  2139. .w_noHover #playlist {
  2140. pointer-events: auto !important;
  2141. }
  2142. {* ソーシャルボタン *}
  2143. .area-JP .panel_ads_shown #playerTabContainer.w_noSocial.has_panel_ads .playerTabContent {
  2144. bottom: 80px;
  2145. }
  2146. .area-JP #playerTabContainer.w_noSocial .playerTabContent {
  2147. bottom: 4px;
  2148. }
  2149. #playerTabContainer.w_noSocial .playerTabAds {
  2150. bottom: 0;
  2151. }
  2152. #playerTabContainer.w_noSocial .socialButtons{
  2153. display: none;
  2154. }
  2155. .w_noSocial .nicoSpotAds {
  2156. bottom: 8px;
  2157. }
  2158.  
  2159.  
  2160. {* テレビちゃんメニュー スライドをやめる *}
  2161. body #videoHeader #videoMenuWrapper{
  2162. position: absolute; width: 324px; height: auto !important;
  2163. opacity: 0;
  2164. transition: opacity 0.4s ease;
  2165. right: 0px;
  2166. }
  2167. body #videoHeader.menuOpened #videoMenuWrapper{
  2168. z-index: 1000 !important;
  2169. border: 1px solid #000;
  2170. background: white;
  2171. box-shadow: 0px 0px 4px #000;
  2172. top: 110px;
  2173. bottom: auto;
  2174. opacity: 1;
  2175. }
  2176. body .tag1Line #videoHeader.menuOpened #videoMenuWrapper{
  2177. top: 62px;
  2178. }
  2179. body .tag2Lines #videoHeader.menuOpened #videoMenuWrapper{
  2180. top: 86px;
  2181. }
  2182. body #videoHeader.infoActive.menuOpened #videoMenuWrapper{
  2183. top: auto;
  2184. bottom: 48px;
  2185. }
  2186. {* body #videoHeader #videoMenuWrapper .defmylistButton, body #videoHeader #videoMenuWrapper .mylistButton {
  2187. display: none !important;
  2188. } *}
  2189. body #videoHeader #videoMenuTopList{
  2190. position: relative;
  2191. width: auto;
  2192. }
  2193. body #videoHeader.menuOpened #videoMenuWrapper .videoMenuList{
  2194. display: inline-block;
  2195. width: 60px;
  2196. min-height: 72px;
  2197. }
  2198. body #videoMenuTopList li.videoMenuListNicoru {
  2199. float: right;
  2200. min-height: 72px;
  2201. }
  2202. body #videoHeader.isAdult .videoMenuToggle, body #videoHeader.noAudioDownload .downloadButton {
  2203. display: inline-block;
  2204. opacity: 0.5;
  2205. pointer-events: none !important;
  2206. }
  2207. {* テレビちゃんメニューのスライド殺す *}
  2208. body #videoHeader.menuOpened #videoMenuWrapper {
  2209. margin-bottom: 0;
  2210. }
  2211. body #videoHeader.menuOpened #videoHeaderDetail {
  2212. margin-top: 8px;
  2213. }
  2214.  
  2215.  
  2216. .largeThumbnailPopup, .largeThumbnailPopup div{
  2217. background-color: #000;
  2218. background-size: contain;
  2219. background-repeat: no-repeat;
  2220. background-position: center center;
  2221. background-size: contain;
  2222. -moz-background-size: contain;
  2223. -webkit-background-size: contain;
  2224. -o-background-size: contain;
  2225. -ms-background-size: contain;
  2226. }
  2227.  
  2228. */}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1]
  2229. .replace(/\{\*/g, '/*').replace(/\*\}/g, '*/');
  2230. addStyle(__css__, 'watchItLaterStyle');
  2231. })(); // end of watchItLaterStyle
  2232.  
  2233.  
  2234.  
  2235.  
  2236.  
  2237. conf.load = function() {
  2238. try {
  2239. function loadStorage(key, def) {
  2240. if (window.localStorage[key] === undefined) { return def; }
  2241. return JSON.parse(window.localStorage.getItem(key));
  2242. }
  2243.  
  2244. for (var v in conf) {
  2245. if (typeof conf[v] === 'function') { continue; }
  2246. conf[v] = loadStorage('watchItLater_' + v, conf[v]);
  2247. }
  2248. } catch (e) {
  2249. }
  2250. };
  2251.  
  2252. conf.getValue = function(varName) {
  2253. return conf[varName];
  2254. };
  2255. conf.setValue = function(k, v) {
  2256. var lastValue = conf[k];
  2257. if (lastValue !== v) {
  2258. conf[k] = v;
  2259. window.localStorage.setItem('watchItLater_' + k, JSON.stringify(v));
  2260. EventDispatcher.dispatch('on.config.' + k, v, lastValue);
  2261. }
  2262. };
  2263. conf.load();
  2264.  
  2265. var console = (function(conf) {
  2266. if (conf.debugMode) { return window.console; }
  2267. var noop = function() {};
  2268. return {
  2269. log: noop,
  2270. error: noop,
  2271. trace: noop,
  2272. warn: noop,
  2273. table: noop
  2274. };
  2275. })(conf);
  2276.  
  2277. var ConfigPanel = (function($, conf, w) {
  2278. var pt = function(){};
  2279. var $panel = null, $shadow = null;
  2280. var menus = [
  2281. {title: '再生開始・終了時の設定', className: 'videoStart'},
  2282. {title: '自動で全画面モードにする', varName: 'autoBrowserFull',
  2283. values: {'する': true, 'しない': false}, addClass: true},
  2284. {title: '自動全画面化オンでも、ユーザーニコ割のある動画は', varName: 'disableAutoBrowserFullIfNicowari',
  2285. values: {'全画面化しない': true, '全画面化する': false}},
  2286. {title: '自動で検索モードにする(自動全画面化オフ時)', varName: 'autoOpenSearch',
  2287. values: {'する': true, 'しない': false}},
  2288. {title: '動画の位置に自動スクロール(自動全画面化オフ時)', varName: 'autoScrollToPlayer',
  2289. values: {'する': true, 'しない': false}},
  2290. // {title: '終了時に全画面モードを解除(原宿と同じにする)', varName: 'autoNotFull',
  2291. // values: {'する': true, 'しない': false},
  2292. // description: '連続再生中は解除しません'},
  2293. {title: 'ウィンドウがアクティブの時だけ自動再生する', varName: 'autoPlayIfWindowActive',
  2294. description: 'QWatch側の設定パネルの自動再生はオフにしてください。\n■こんな人におすすめ\n・自動再生ONにしたいけど別タブで開く時は自動再生したくない\n・複数タブ開いたままブラウザ再起動したら全部のタブで再生が始まって「うるせー!」という経験のある人',
  2295. values: {'する': 'yes', 'しない': 'no'}},
  2296. {title: '動画が切り替わる時、ポップアップでタイトルと再生数を表示', varName: 'popupViewCounter',
  2297. description: '全画面状態で連続再生している時などに便利です',
  2298. values: {'する': 'always', '全画面時のみ': 'full', 'しない': 'none'}},
  2299.  
  2300. {title: 'プレイヤーの設定', className: 'playerSetting'},
  2301. {title: 'コメントパネルを広くする', varName: 'wideCommentPanel',
  2302. values: {'する': true, 'しない': false}},
  2303. {title: 'コメントパネルにNG共有設定を表示', varName: 'enableSharedNgSetting',
  2304. values: {'する': true, 'しない': false}, addClass: true},
  2305. {title: 'コメントの表示', varName: 'commentVisibility',
  2306. values: {'オフ': 'hidden', '最後の状態を記憶': 'lastState', 'オン': 'visible'}},
  2307. {title: '右のパネルに動画情報・市場・レビューを表示', varName: 'rightPanelJack', reload: true,
  2308. values: {'する': true, 'しない': false}},
  2309. {title: 'ページのヘッダに再生数表示', varName: 'headerViewCounter', reload: true,
  2310. values: {'する': true, 'しない': false}},
  2311. {title: 'ニコニコニュースの履歴を保持する', varName: 'enableNewsHistory', reload: true,
  2312. values: {'する': true, 'しない': false}},
  2313. {title: 'ニコニコニュースを消す', varName: 'hideNicoNews',
  2314. values: {'消す': true, '消さない': false}},
  2315. {title: 'プレイヤーの背景', varName: 'playerBgStyle',
  2316. description: 'ウォール機能より優先されます',
  2317. values: {'白': 'white', 'グレー': 'gray', 'ウォール': ''}},
  2318. {title: 'コメントの盛り上がりをグラフ表示', varName: 'enableHeatMap', reload: true,
  2319. description: '動画のどのあたりが盛り上がっているのか、わかりやすくなります',
  2320. values: {'する': true, 'しない': false}},
  2321. {title: '大画面をもっと大画面にする', varName: 'customPlayerSize',
  2322. description: '※有効にするとニコニコニュースが表示できなくなります。',
  2323. values: {'フルHD': '1080p', '720p': '720p', '自動調整(推奨)': 'auto', 'しない': ''}},
  2324. {title: 'プレイリスト消えないモード(実験中)', varName: 'storagePlaylistMode', reload: true,
  2325. description: '有効にすると、リロードしてもプレイリストが消えなくなります。',
  2326. values:
  2327. (conf.debugMode ?
  2328. {'ウィンドウを閉じるまで': 'sessionStorage', 'ずっと保持': 'localStorage', 'しない': ''} :
  2329. {'有効(ウィンドウを閉じるまで)': 'sessionStorage', '無効': ''})
  2330. },
  2331. {title: '説明文中の動画IDにサムネイル表示(実験中)', varName: 'enableDescriptionThumbnail', reload: true,
  2332. values: {'有効': true, '無効': false}},
  2333.  
  2334.  
  2335. {title: '検索モードの設定', className: 'videoExplorer'},
  2336. {title: '検索モードを無効化', varName: 'disableVideoExplorer',
  2337. description: '無効にするとタグ検索などが原宿と同じになります。\nただし、自分で検索モードにしている時は検索モードで開きます',
  2338. values: {'する': true, 'しない': false}},
  2339. {title: 'プレイヤーをできるだけ大きくする (コメントやシークも可能にする)', varName: 'videoExplorerHack',
  2340. description: '便利ですがちょっと重いです。\n大きめのモニターだと快適ですが、小さいといまいちかも',
  2341. values: {'する': true, 'しない': false}},
  2342. {title: 'お気に入りタグを表示', varName: 'enableFavTags',
  2343. values: {'する': true, 'しない': false}},
  2344. {title: 'お気に入りマイリストを表示', varName: 'enableFavMylists',
  2345. description: '更新のあったリストが上に来るので、新着動画のチェックに便利です。',
  2346. values: {'する': true, 'しない': false}},
  2347. // {title: 'サムネを4:3にする', varName: 'squareThumbnail',
  2348. // description: '上下がカットされなくなり、サムネの全体が見えるようになります。',
  2349. // values: {'する': true, 'しない': false}},
  2350. {title: '「マイリストから外す」ボタンを表示', varName: 'enableMylistDeleteButton',
  2351. description: 'マイリストの整理に便利。\n ※ 消す時に確認ダイアログは出ないので注意',
  2352. values: {'する': true, 'しない': false}},
  2353. {title: '検索時に関連タグを表示する', varName: 'enableRelatedTag',
  2354. values: {'する': true, 'しない': false}},
  2355. {title: 'niconico新検索βを使う', varName: 'searchEngine',
  2356. description: '投稿期間や動画長による絞り込みができるようになります',
  2357. values: {'使う': 'sugoi', '使わない': 'normal'}},
  2358. {title: '1ページの表示件数', varName: 'searchPageItemCount',
  2359. values: {'100件': 100, '50件': 50, '32件': 32}},
  2360.  
  2361. {title: '全画面モードの設定', className: 'fullScreen'},
  2362. {title: '操作パネルとコメント入力欄を隠す', varName: 'controllerVisibilityInFull',
  2363. description: '全画面の時は少しでも動画を大きくしたい場合に便利',
  2364. values: {'隠す': 'hidden', '隠さない': ''}},
  2365. {title: '右下のマイリストメニュー', varName: 'hideMenuInFull',
  2366. values: {'完全に消す': 'hideAll', '色だけ変える': '', '目立たなくする': 'hide'}},
  2367. {title: 'ホイールを回したら動画情報を出す', varName: 'enableFullScreenMenu',
  2368. description: 'ホイールを大きく下に回すとメニューが出ます。タッチパネルも対応',
  2369. values: {'する': true, 'しない': false}},
  2370.  
  2371. {title: 'ページ下半身の設定', className: 'playerBottom'},
  2372. {title: 'ニコニコ市場の表示', varName: 'ichibaVisibility',
  2373. values: {'非表示': 'hidden', '表示': 'visible'}},
  2374. {title: 'レビューの表示', varName: 'reviewVisibility',
  2375. values: {'非表示': 'hidden', '表示': 'visible'}},
  2376.  
  2377. {title: '省スペース/軽量化設定', className: 'compact'},
  2378. {title: 'タグが2行以内の時に高さを詰める(ピン留め時のみ)', varName: 'enableAutoTagContainerHeight', reload: true,
  2379. values: {'詰める': true, '詰めない': false}},
  2380. {title: '動画情報の空きスペースを詰める', varName: 'compactVideoInfo',
  2381. description: '原宿ぐらいの密度になります。ちょっと窮屈かも',
  2382. values: {'詰める': true, '詰めない': false}},
  2383. // {title: '背景のグラデーションをなくす', varName: 'flatDesignMode',
  2384. // description: '軽い表示になります',
  2385. // values: {'なくす': 'on', 'なくさない': ''}},
  2386. {title: '「ニコる」をなくす', varName: 'noNicoru',
  2387. description: '画面上から見えなくなります。\nまた、コメントパネルの処理が軽くなります',
  2388. values: {'なくす': true, 'なくさない': false}},
  2389. {title: 'コメントパネルのマウスオーバー処理をなくす', varName: 'removeCommentPanelHoverEvent', reload: true,
  2390. description: 'マウスオーバー時のちらちらした物がなくなり、表示が軽くなります',
  2391. values: {'なくす': true, 'なくさない': false}},
  2392. {title: 'タグの自動更新を無効化', varName: 'disableTagReload',
  2393. values: {'する': true, 'しない': false}},
  2394. {title: '横スクロールバーを出なくする', varName: 'disableHorizontalScroll',
  2395. values: {'する': true, 'しない': false}},
  2396. {title: 'コメントパネル下のソーシャルボタン', varName: 'hideCommentPanelSocialButtons',
  2397. values: {'隠す': true, '隠さない': false}},
  2398. {title: 'GPUレイヤーを使用してみる(上級者用)', varName: 'enableGpuLayer', reload: true, debugOnly: true,
  2399. description: '環境によっては軽くなる かも しれません',
  2400. values: {'する': true, 'しない': false}},
  2401.  
  2402. {title: 'その他の設定', className: 'otherSetting'},
  2403. {title: '動画リンクにカーソルを重ねたらマイリストメニューを表示', varName: 'enableHoverPopup', reload: true,
  2404. description: 'マウスカーソルを重ねた時に出るのが邪魔な人はオフにしてください',
  2405. values: {'する': true, 'しない': false}},
  2406. {title: '動画リンクにカーソルを重ねてからメニューが出るまでの時間(秒)', varName: 'hoverMenuDelay',
  2407. type: 'text', description: '単位は秒。 標準は0.4です'},
  2408. {title: 'ニコレポのポップアップを置き換える', varName: 'replacePopupMarquee', reload: true,
  2409. description: '画面隅に出るポップアップの不可解な挙動を調整します',
  2410. values: {'する': true, 'しない': false}},
  2411. {title: '検索時のデフォルトパラメータ', varName: 'defaultSearchOption', type: 'text',
  2412. description: '常に指定したいパラメータ指定するのに便利です\n例: 「-グロ -例のアレ」とすると、その言葉が含まれる動画が除外されます'},
  2413. {title: '「@ジャンプ」を無効化', varName: 'ignoreJumpCommand', reload: true,
  2414. description: '勝手に他の動画に飛ばされる機能を無効化します。',
  2415. values: {'する': true, 'しない': false}},
  2416. {title: '「@ジャンプ」によるシーク無効化(無限ループなど)', varName: 'nicoSSeekCount', reload: true,
  2417. description: '完全に無効にする以外に、一動画あたりの回数を指定できます',
  2418. values: {'2回まで有効': 2, '1回まで有効': 1, '完全無効化': 0, 'しない': -1}},
  2419. {title: 'タッチパネル向けモード(画面を右フリックで開始)', varName: 'enableQTouch',
  2420. description: '指で操作しやすいように、一部のボタンやメニューが大きくなります',
  2421. values: {'使う': true, '使わない': false}},
  2422. {title: 'マイリストメニューの位置', varName: 'mylistPanelPosition',
  2423. values: {'左下': 'left', '右下': ''}},
  2424. {title: '2本目以降の動画だけ自動再生 (※プレミアム用)', varName: 'autoPlay2ndVideo', reload: true,
  2425. values: {'する': true, 'しない': false}},
  2426. {title: 'マイリストのローカルキャッシュ', varName: 'enableLocalMylistCache', reload: true,
  2427. description: '動画がどのマイリストに登録されてるかの情報をキャッシュします。\n「my」ボタンの右クリックを活用する人はおすすめ。',
  2428. values: {'有効': true, '無効': false}},
  2429.  
  2430.  
  2431. {title: 'マウスとキーボードの設定', description: '※Chromeはコメント入力中も反応してしまいます', className: 'shortcut'},
  2432. {title: '背景ダブルクリックで動画の位置にスクロール', varName: 'doubleClickScroll',
  2433. description: 'なにもない場所をダブルクリックすると、動画の位置にスクロールします。\n 市場を見てからプレイヤーに戻りたい時などに便利',
  2434. values: {'する': true, 'しない': false}},
  2435. {title: 'マウスのボタン+ホイールでどこでも音量調整', varName: 'mouseClickWheelVolume',
  2436. description: 'とっさに音量を変えたい時に便利',
  2437. values: {'左ボタン+ホイール': 1, '右ボタン+ホイール': 2, '使わない': 0}},
  2438. {title: '停止/再生', varName: 'shortcutTogglePlay', type: 'keyInput'},
  2439. {title: 'とりあえずマイリスト登録', varName: 'shortcutDefMylist', type: 'keyInput'},
  2440. {title: 'マイリスト登録', varName: 'shortcutMylist', type: 'keyInput',
  2441. description: '右下で選択中のマイリストに登録'},
  2442. {title: 'とりあえずマイリストを開く', varName: 'shortcutOpenDefMylist', type: 'keyInput'},
  2443. {title: '動画投稿者の関連動画を開く', varName: 'shortcutShowOtherVideo', type: 'keyInput'},
  2444. {title: '検索画面を開く', varName: 'shortcutOpenSearch', type: 'keyInput'},
  2445. {title: '関連動画(オススメ)を開く', varName: 'shortcutOpenRecommend', type: 'keyInput'},
  2446. {title: 'コメント表示ON/OFF', varName: 'shortcutCommentVisibility', type: 'keyInput'},
  2447. {title: 'プレイヤーの位置までスクロール', varName: 'shortcutScrollToNicoPlayer', type: 'keyInput'},
  2448. {title: 'ミュート', varName: 'shortcutMute', type: 'keyInput'},
  2449. {title: 'コメントの背面表示ON/FF', varName: 'shortcutDeepenedComment', type: 'keyInput'},
  2450. {title: 'ハードウェアアクセラレーションON/FF', varName: 'shortcutToggleStageVideo', type: 'keyInput'},
  2451.  
  2452. {title: 'その他2(一発ネタ系)', description: 'いつのまにか消えるかもしれません', className: 'shortcut'},
  2453. {title: 'テレビちゃんメニュー内にランダム画像(左上)表示', varName: 'hidariue',
  2454. values: {'する': true, 'しない': false}},
  2455. {title: 'ゆっくり再生(スロー再生)ボタンを表示', varName: 'enableYukkuriPlayButton',
  2456. values: {'する': true, 'しない': false}},
  2457.  
  2458. {title: '実験中の設定', debugOnly: true, className: 'forDebug'},
  2459. // {title: 'プレイリスト消えないモード(※実験中)', varName: 'hashPlaylistMode', debugOnly: true, reload: true,
  2460. // values: {'有効(連続再生中のみ)': 1, '有効(常時)': 2, '無効': 0}},
  2461.  
  2462.  
  2463.  
  2464. ];
  2465.  
  2466. var listener = [];
  2467. function dispatchEvent(name, value, lastValue) {
  2468. for (var i = 0; i < listener.length; i++) {
  2469. (listener[i])(name, value, lastValue);
  2470. }
  2471. }
  2472. pt.createPanelDom = function() {
  2473. if ($panel === null) {
  2474. $panel = w.jQuery([
  2475. '<div id="watchItLaterConfigPanel">',
  2476. '<div class="head"><button class="closeButton" title="閉じる">▲</button><h2>WatchItLaterの設定</h2>(※)のつく項目は、リロード後に反映されます</div>',
  2477. '<div class="inner"></div></div>'
  2478. ].join(''));
  2479. $panel.on('click', function(e) { e.stopPropagation(); });
  2480.  
  2481. var scrollTo = function() {
  2482. var $target = this;
  2483. var isOpen = $target.parent().toggleClass('open').hasClass('open');
  2484. if (isOpen) {
  2485. setTimeout(function() {
  2486. var $inner = $('#watchItLaterConfigPanel .inner');
  2487. $inner.animate({
  2488. scrollTop: $inner.scrollTop() + $target.parent().position().top - 50
  2489. }, 400);
  2490. }, 200);
  2491. }
  2492. };
  2493.  
  2494. var $ul = null, $inner = $panel.find('.inner'), $item; //$panel.find('ul'), $item;
  2495. for (var i = 0, len = menus.length; i < len; i++) {
  2496. if (menus[i].varName) {
  2497. $item = this.createMenuItem(menus[i]);
  2498. } else {
  2499. if (menus[i].description) {
  2500. $item = $('<li class="section ' +menus[i].className + '"><div><span>'+ menus[i].title + '</span><span class="description">'+ menus[i].description + '</span></div></li>');
  2501. } else {
  2502. $item = $('<li class="section ' +menus[i].className + '"><div><span>'+ menus[i].title + '</span></div></li>');
  2503. }
  2504. if ($ul) $inner.append($ul);
  2505. $ul =$('<ul class="sectionContainer"/>').addClass(menus[i].className + 'Container');
  2506. $item.click($.proxy(scrollTo, $item));
  2507. }
  2508. $item.toggleClass('debugOnly', menus[i].debugOnly === true).toggleClass('reload', menus[i].reload === true);
  2509. if ($ul) $ul.append($item);
  2510. }
  2511. if ($ul) $inner.append($ul);
  2512. $panel.toggleClass('debugMode', conf.debugMode);
  2513. var $bottom = w.jQuery('<div class="foot"></div>'), self = this;
  2514. $panel.append($bottom);
  2515. $panel.find('.closeButton').click(function() {
  2516. self.close();
  2517. });
  2518. if ($shadow === null) {
  2519. $shadow = $('<div id="watchItLaterConfigPanelShadow" /><div id="watchItLaterConfigPanelShadowTop"/><div id="watchItLaterConfigPanelOverShadow"/>');
  2520. }
  2521. }
  2522. };
  2523.  
  2524. pt.refresh = function() {
  2525. var isVisible = $panel.hasClass('open');
  2526. $panel.remove().empty();
  2527. $panel = null;
  2528. this.createPanelDom();
  2529. if (isVisible) { $panel.show(); }
  2530. };
  2531.  
  2532. pt.createMenuItem = function(menu) {
  2533. if (menu.type === 'text') {
  2534. return this.createTextMenuItem(menu);
  2535. } else
  2536. if (menu.type === 'keyInput') {
  2537. return this.createKeyInputMenuItem(menu);
  2538. } else {
  2539. return this.createRadioMenuItem(menu);
  2540. }
  2541. };
  2542. pt.createRadioMenuItem = function(menu) {
  2543. var title = menu.title, varName = menu.varName, values = menu.values;
  2544. var $menu = w.jQuery('<li><p class="title">' + title + '</p></li>');
  2545. if (menu.className) { $menu.addClass(menu.className);}
  2546. if (menu.description) { $menu.attr('title', menu.description); }
  2547. var currentValue = conf.getValue(varName);
  2548. $menu.addClass(menu.varName);
  2549. if (menu.addClass) { $panel.addClass(menu.varName + '_' + currentValue);}
  2550. for (var k in values) {
  2551. var v = values[k];
  2552. var $label = w.jQuery('<label></label>');
  2553. var $chk = w.jQuery('<input>');
  2554. $chk.attr({type: 'radio', name: varName, value: JSON.stringify(v)});
  2555.  
  2556. if (currentValue === v) {
  2557. $chk.prop('checked', 'checked');
  2558. }
  2559. $chk.click(function() {
  2560. var newValue = JSON.parse(this.value), oldValue = conf.getValue(varName);
  2561. if (oldValue !== newValue) {
  2562. if (menu.addClass) {
  2563. $panel.removeClass(menu.varName + '_' + oldValue).addClass(menu.varName + '_' + newValue);
  2564. }
  2565. conf.setValue(menu.varName, newValue);
  2566. if (typeof menu.onchange === 'function') {
  2567. menu.onchange(newValue, oldValue);
  2568. }
  2569. dispatchEvent(menu.varName, newValue, oldValue);
  2570. }
  2571. });
  2572. $label.append($chk).append(w.jQuery('<span>' + k + '</span>'));
  2573. $menu.append($label);
  2574. }
  2575. return $menu;
  2576. };
  2577. pt.createTextMenuItem = function(menu) {
  2578. var title = menu.title, varName = menu.varName;
  2579. var $menu = w.jQuery('<li><p class="title">' + title + '</p></li>');
  2580. if (menu.className) { $menu.addClass(menu.className);}
  2581. if (menu.description) { $menu.attr('title', menu.description); }
  2582. var currentValue = conf.getValue(varName);
  2583. var $input = w.jQuery('<input type="text" />');
  2584. $menu.addClass(menu.varName);
  2585. if (menu.addClass) { $panel.addClass(menu.varName + '_' + currentValue);}
  2586. $input.val(currentValue);
  2587. $input.change(function() {
  2588. var newValue = $input.val(), oldValue = conf.getValue(varName);
  2589. if (oldValue !== newValue) {
  2590. conf.setValue(varName, newValue);
  2591. if (typeof menu.onchange === 'function') {
  2592. menu.onchange(newValue, oldValue);
  2593. }
  2594. dispatchEvent(menu.varName, newValue, oldValue);
  2595. }
  2596. });
  2597. $menu.append($input);
  2598. return $menu;
  2599. };
  2600.  
  2601. pt.createKeyInputMenuItem = function(menu) {
  2602. var title = menu.title, varName = menu.varName;
  2603. var currentValue = conf.getValue(varName), currentKey = currentValue.char;
  2604.  
  2605. function update() {
  2606. var newValue = {char: $sel.val(), ctrl: $menu.hasClass('ctrl'), alt: $menu.hasClass('alt'), shift: $menu.hasClass('shift'), enable: $menu.hasClass('enable')};
  2607. conf.setValue(varName, newValue);
  2608. if (typeof menu.onchange === 'function') {
  2609. menu.onchange(newValue);
  2610. }
  2611. dispatchEvent(menu.varName, newValue, conf.getValue(varName));
  2612. }
  2613.  
  2614. var $menu = w.jQuery('<li class="shortcutSetting"><p class="title">' + title + '</p></li>');
  2615. var sel = ['<select>'], $sel;
  2616. for (var v = 48; v <= 90; v++) {
  2617. if (v >= 0x3c && v <= 0x3f) continue;
  2618. var c = String.fromCharCode(v);
  2619. var op = ['<option value="', c, '">', c, '</option>' ].join('');
  2620. sel.push(op);
  2621. }
  2622. sel.push('</select>');
  2623. $sel = w.jQuery(sel.join(''));
  2624. var $meta = w.jQuery('<span class="enable" data-meta="enable">有効</span><span class="ctrl" data-meta="ctrl">ctrl</span><span class="alt" data-meta="alt">alt</span><span class="shift" data-meta="shift">shift</span>').on('click', function(e) {
  2625. var meta = w.jQuery(e.target).attr('data-meta');
  2626. $menu.toggleClass(meta);
  2627. update();
  2628. });
  2629. $sel.change(update);
  2630.  
  2631. $menu.toggleClass('enable', currentValue.enable).toggleClass('ctrl', currentValue.ctrl).toggleClass('alt', currentValue.alt).toggleClass('shift', currentValue.shift);
  2632. $sel.val(currentKey);
  2633.  
  2634. if (menu.className) { $menu.addClass(menu.className);}
  2635. if (menu.description) { $menu.attr('title', menu.description); }
  2636.  
  2637. $menu.append(w.jQuery('<span/>').append($meta).append($sel));
  2638.  
  2639. return $menu;
  2640. };
  2641.  
  2642. pt.toggleOpenSection = function(sectionName, toggle) {
  2643. $('#watchItLaterConfigPanel .'+ sectionName + 'Container').toggleClass('open', toggle);
  2644. $('#watchItLaterConfigPanel .inner').scrollTop($('#watchItLaterConfigPanel .' + sectionName).position().top - 50);
  2645. };
  2646.  
  2647. pt.addChangeEventListener = function(callback) {
  2648. listener.push(callback);
  2649. };
  2650. pt.open = function() {
  2651. $('body').append($shadow).append($panel);
  2652. setTimeout(function() {
  2653. $shadow.addClass('open'); $panel.addClass('open');
  2654. }, 50);
  2655. setTimeout(function() {
  2656. if (WatchController.isFullScreen()) {
  2657. pt.toggleOpenSection('fullScreen', true);
  2658. } else
  2659. if (WatchController.isSearchMode()) {
  2660. pt.toggleOpenSection('videoExplorer', true);
  2661. }
  2662. }, 1000);
  2663. };
  2664. pt.close = function() {
  2665. $shadow.removeClass('open'); $panel.removeClass('open');
  2666. setTimeout(function() {
  2667. $shadow.detach(); $panel.detach();
  2668. }, 800);
  2669. };
  2670. pt.toggle = function() {
  2671. this.createPanelDom();
  2672. if ($panel.hasClass('open')) {
  2673. this.close();
  2674. } else {
  2675. this.open();
  2676. }
  2677. };
  2678.  
  2679. return pt;
  2680. })(w.jQuery, conf, w);
  2681.  
  2682.  
  2683. /**
  2684. * 通信用
  2685. */
  2686. window.WatchItLater = {
  2687. config: {
  2688. get: function(varName) {
  2689. return conf.getValue(varName);
  2690. },
  2691. set: function(varName, value) {
  2692. conf.setValue(varName, value);
  2693. },
  2694. open: function() {
  2695. ConfigPanel.open();
  2696. }
  2697. },
  2698. loader: {},
  2699. debug: {},
  2700. init: {},
  2701. test: {
  2702. assert: function(v, m) {
  2703. if (v === true) {
  2704. window.console.log('%c OK: ', 'color: black; background: lime;', m);
  2705. } else {
  2706. window.console.log('%cFail: ', 'color: white; background: red;', m);
  2707. throw {message: 'Fail'};
  2708. }
  2709. },
  2710. expect: function(a) {
  2711. try {
  2712. var assert = window.WatchItLater.test.assert, exp = {
  2713. toBeTrue: function( desc) { assert(a === true , desc); },
  2714. toBeFalse: function( desc) { assert(a === false , desc); },
  2715. toEqual: function(b, desc) { assert(a === b , desc); },
  2716. toBeNull: function( desc) { assert(a === null , desc); },
  2717. toBeNotNull: function( desc) { assert(a !== null , desc); },
  2718. toBeDefined: function( desc) { assert(a !== undefined , desc); },
  2719. toBeTruthy: function( desc) { assert(a ? true : false, desc); }
  2720. };
  2721. return exp;
  2722. } catch(e) {
  2723. window.console.log('%c', a);
  2724. }
  2725. },
  2726. spec: {},
  2727. run: function(name) {
  2728. var def = (new $.Deferred()), promise = def.promise();
  2729. var con = function(name) {
  2730. return function() {
  2731. var d = new $.Deferred();
  2732. setTimeout(function() {
  2733. window.console.log('%c RUN: ' + name, 'background: #8ff;');
  2734. d.resolve();
  2735. }, 100);
  2736. return d.promise();
  2737. };
  2738. };
  2739. var wrap = function(self, name) {
  2740. return function() {
  2741. var d = new $.Deferred();
  2742. setTimeout(function() {
  2743. try {
  2744. $.proxy(self.spec[name], self)(d);
  2745. } catch (e) {
  2746. window.console.log(e);
  2747. d.reject();
  2748. }
  2749. }, 0);
  2750. return d.promise();
  2751. };
  2752. };
  2753. var onFail = function(e) {
  2754. window.console.log('%c fail : ','background: red;', e);
  2755. };
  2756.  
  2757. if (name) {
  2758. promise = promise.then(con(name)).then(wrap(this, name), onFail);
  2759. } else {
  2760. for(var v in this.spec) {
  2761. if (!v.match(/^test/)) continue;
  2762. promise = promise.then(con(v)) .then(wrap(this, v), onFail);
  2763. }
  2764. }
  2765. promise.then(
  2766. function() { window.console.log('%cテスト完了', 'background: #8ff'); },
  2767. function() { window.console.log('%cテスト失敗', 'background: #f00'); }
  2768. );
  2769. def.resolve();
  2770. }
  2771. }
  2772. };
  2773. // w.WatchItLater = window.WatchItLater;
  2774.  
  2775.  
  2776. var EventDispatcher = (function(conf) {
  2777. var events = {};
  2778.  
  2779. function addEventListener(name, callback) {
  2780. name = name.toLowerCase();
  2781. if (!events[name]) {
  2782. events[name] = [];
  2783. }
  2784. events[name].push(callback);
  2785. }
  2786.  
  2787. function _dispatch(name) {
  2788. name = name.toLowerCase();
  2789. if (!events[name]) { return; }
  2790. var e = events[name];
  2791. for (var i =0, len = e.length; i < len; i++) {
  2792. try {
  2793. e[i].apply(null, Array.prototype.slice.call(arguments, 1));
  2794. } catch (ex) {
  2795. console.log('%c' + name, 'background:red; color: white;', i, e[i], ex);
  2796. }
  2797. }
  2798. }
  2799. function dispatch(name) {
  2800. console.log('%cevent:', 'background: blue; color: white;', name);//, arguments);
  2801. _dispatch.apply(null, arguments);
  2802. }
  2803. return {
  2804. addEventListener: addEventListener,
  2805. dispatch: dispatch,
  2806. _dispatch: _dispatch // コンソール汚したくない用
  2807. };
  2808. })(conf);
  2809. window.WatchItLater.event = EventDispatcher;
  2810.  
  2811. /*
  2812. * 通算視聴回数をカウント。 カウントしても意味はないけど、どれだけ無駄な時間を費やしたかを知りたくて実装。
  2813. */
  2814. var WatchCounter = (function(conf, w) {
  2815. var key = 'watchItLater_watchCounter';
  2816. function get() {
  2817. return JSON.parse(w.localStorage.getItem(key));
  2818. }
  2819. function add() {
  2820. var v = get() + 1;
  2821. w.localStorage.setItem(key, JSON.stringify(v));
  2822. console.log('%cwatchCounter: %c%d', 'color: orange;', 'font-weight: bolder;', v);
  2823. return v;
  2824. }
  2825. var self = {
  2826. get: get,
  2827. add: add
  2828. };
  2829. return self;
  2830. })(conf, w);
  2831. window.WatchItLater.counter = WatchCounter;
  2832.  
  2833. /**
  2834. * 動画タグ取得とポップアップ
  2835. *
  2836. */
  2837. var VideoTags = (function(conf, w){
  2838.  
  2839. var host = location.host.replace(/^([\w\d]+)\./, 'www.');
  2840. var pt = function(){};
  2841. var lastPopup = null;
  2842.  
  2843. pt.get = function(watchId, callback) {
  2844. var _get = function(watchId, callback) {
  2845. var url = 'http://' + host + '/tag_edit/' + watchId + '/?res_type=json&cmd=tags';
  2846. //http://www.nicovideo.jp/tag_edit/sm9/?res_type=json&cmd=tags
  2847. var req = {
  2848. method: 'GET',
  2849. url: url,
  2850. onload: function(resp) {
  2851. var result = JSON.parse(resp.responseText);
  2852. if (typeof callback === 'function') callback(result.status, result);
  2853. }
  2854. };
  2855. GM_xmlhttpRequest(req);
  2856. };
  2857.  
  2858. WatchController.getTid2Vid(watchId, function(videoId) {
  2859. _get(videoId, callback);
  2860. });
  2861. };
  2862.  
  2863. pt.hidePopup = function() {
  2864. if (lastPopup) {
  2865. lastPopup.style.display = 'none';
  2866. }
  2867. };
  2868.  
  2869. var uniq = null, $history = null, popupContainer = null;
  2870. pt.popupItems = function(watchId, baseX, baseY) {
  2871. var self = this;
  2872. popupContainer.innerHTML = '';
  2873. this.get(watchId, function(status, resp) {
  2874. if (status === 'ok') {
  2875. var tags = resp.tags;
  2876. self.hidePopup();
  2877. if (tags.length > 0) {
  2878. lastPopup = createPopup(tags, baseX, baseY);
  2879. } else {
  2880. Popup.show('この動画のタグはありません');
  2881. }
  2882. } else {
  2883. Popup.alert(resp.error_message);
  2884. }
  2885. });
  2886.  
  2887. function createPopup(tags, baseX, baseY) {
  2888. var popup = createDOM(tags, baseX, baseY);
  2889. popupContainer.appendChild(popup);
  2890. popup.style.right = null;
  2891. popup.style.left = baseX + 'px';
  2892. popup.style.top = Math.max(baseY - popup.offsetHeight, 0, document.body.scrollTop, document.documentElement.scrollTop) + 'px';
  2893. if (popup.offsetLeft + popup.offsetWidth > document.body.clientWidth) {
  2894. popup.style.left = null;
  2895. popup.style.right = 0;
  2896. }
  2897.  
  2898. return popup;
  2899. }
  2900.  
  2901. function createDOM(tags) {
  2902. var items = document.createElement('ul');
  2903. for (var i = 0, len = tags.length; i < len; i++) {
  2904. items.appendChild(createItemDOM(tags[i]));
  2905. }
  2906. var popup = createPopupDOM();
  2907.  
  2908. popup.appendChild(items);
  2909. return popup;
  2910. }
  2911.  
  2912. function createPopupDOM() {
  2913. var popup = document.createElement('div');
  2914. popup.className = 'tagItemsPopup popupMenu';
  2915. popup.addEventListener('click', createPopupOnClick(), false);
  2916. return popup;
  2917. }
  2918.  
  2919. function createPopupOnClick() {
  2920. return function(e) {
  2921. if (e.button !== 0 || e.shiftKey || e.ctrlKey || e.altKey || e.target.className === 'icon' || e.target.tagName === 'A') {
  2922. return;
  2923. }
  2924. this.style.display = 'none';
  2925. e.preventDefault();
  2926. e.stopPropagation();
  2927. };
  2928. }
  2929.  
  2930. function appendTagHistory(dom, text, dic) {
  2931. var $ = w.$;
  2932. if (uniq === null) {
  2933. uniq = {};
  2934. $history = $('<div class="tagSearchHistory"><h3 class="title">タグ検索履歴</h3></div>');
  2935. $history.css({width: $('.videoExplorerMenu').width() - 8, maxHeight: '300px', overflowY: 'auto'});
  2936. $('.videoExplorerMenu').append($history);
  2937. }
  2938. if (!uniq[text]) {
  2939. var a = $(dom).clone().css({marginRight: '8px', fontSize: '80%'}).click(Util.Closure.openNicoSearch(text));
  2940. dic.style.marginRight = '0';
  2941. $history.find('.title').after(a).after(dic);
  2942. }
  2943. uniq[text] = 1;
  2944. }
  2945.  
  2946. function createItemDOM(tag) {
  2947. var text = tag.tag;
  2948. var li = document.createElement('li');
  2949. li.className = 'popupTagItem';
  2950.  
  2951. // 大百科アイコン
  2952. var dic = createDicIconDOM(tag, text);
  2953. li.appendChild(dic);
  2954.  
  2955. // 新検索(search.nicovideo.jp)へのリンク
  2956. var newSearchIcon = createNewSearchIconDOM(tag, text);
  2957. li.appendChild(newSearchIcon);
  2958.  
  2959. // 本文リンク
  2960. var a = document.createElement('a');
  2961. a.appendChild(document.createTextNode(text));
  2962.  
  2963. var href = text;
  2964. if (conf.defaultSearchOption && conf.defaultSearchOption !== '' && !text.match(/(sm|nm|so)\d+/)) {
  2965. href += ' ' + conf.defaultSearchOption;
  2966. }
  2967. var sortOrder = '?sort=' + conf.searchSortType + '&order=' + conf.searchSortOrder;
  2968. a.href = 'http://' + host + '/tag/' + encodeURIComponent(href) + sortOrder;
  2969. a.addEventListener('click', createItemOnClick(text, dic), false);
  2970. li.appendChild(a);
  2971.  
  2972. return li;
  2973. }
  2974.  
  2975. function createItemOnClick(text, dic) {
  2976. return function(e) {
  2977. if (e.button !== 0 || e.metaKey) return;
  2978. if (w.WatchApp) {
  2979. WatchController.nicoSearch(text, 'tag');
  2980. e.preventDefault();
  2981. appendTagHistory(this, text, dic);
  2982. }
  2983. return false;
  2984. };
  2985. }
  2986.  
  2987. function createNewSearchIconDOM(tag, text) {
  2988. var link = document.createElement('a');
  2989. link.className = 'newsearch';
  2990. link.title = 'niconico新検索で開く';
  2991.  
  2992. // TODO: パラメータの対応表作ってあわせる
  2993. var newSortOrder = '';
  2994. link.href = 'http://search.nicovideo.jp/video/search/' + encodeURIComponent(text) + newSortOrder;
  2995. if (location.host !== 'search.nicovdieo.jp') {
  2996. link.target = '_blank';
  2997. }
  2998.  
  2999. var icon = document.createElement('img');
  3000. icon.className = 'icon';
  3001. icon.src = 'http://uni.res.nimg.jp/img/favicon.ico';
  3002. link.appendChild(icon);
  3003.  
  3004. return link;
  3005. }
  3006. function createDicIconDOM(tag, text) {
  3007. var dic = document.createElement('a');
  3008. dic.className = 'nicodic';
  3009. dic.href = 'http://dic.nicovideo.jp/a/' + encodeURIComponent(text);
  3010. dic.target = '_blank';
  3011. var icon = document.createElement('img');
  3012. icon.className = 'icon';
  3013. icon.src = tag.dic ? 'http://live.nicovideo.jp/img/2012/watch/tag_icon002.png' : 'http://live.nicovideo.jp/img/2012/watch/tag_icon003.png';
  3014. dic.appendChild(icon);
  3015. return dic;
  3016. }
  3017. };
  3018. popupContainer = document.createElement('div');
  3019. popupContainer.id = 'videoTagPopupContainer';
  3020. document.body.appendChild(popupContainer);
  3021.  
  3022. return pt;
  3023. })(conf, w);
  3024.  
  3025.  
  3026.  
  3027.  
  3028.  
  3029.  
  3030.  
  3031.  
  3032.  
  3033. /**
  3034. * マイリスト登録API
  3035. *
  3036. * (9)の頃は、iframeを作ってその中にマイリスト登録のポップアップウィンドウを開くという手抜きを行っていたが、
  3037. * ポップアップウィンドウは評判が悪いし、そのうち廃止されるだろうなと思うので、
  3038. * 真面目にAPIを叩くようにした。 (マイリストの新規作成機能は省略)
  3039. *
  3040. * …と思っていたのだが、(9)からQになった今でもポップアップウィンドウは廃止されないようだ。
  3041. */
  3042. var Mylist = window.WatchItLater.mylist = (function(){
  3043. var mylistlist = [];
  3044. var initialized = false;
  3045. var defListItems = [], mylistItems = {};
  3046. var host = location.host.replace(/^([\w\d]+)\./, 'www.');
  3047. var token = '';//
  3048.  
  3049. function Mylist() {
  3050. this.initialize();
  3051. }
  3052.  
  3053. function getToken() {
  3054. if (!isNativeGM && host !== location.host) return null; //
  3055.  
  3056. var _token = (w.NicoAPI) ? w.NicoAPI.token : '';
  3057. if (w.NicoAPI) {
  3058. return w.NicoAPI.token;
  3059. } else
  3060. if (w.WatchApp && w.WatchJsApi) {
  3061. var watchInfoModel = WatchApp.ns.model.WatchInfoModel.getInstance();
  3062. watchInfoModel.addEventListener('reset', function(watchInfoModel) {
  3063. token = watchInfoModel.csrfToken;
  3064. });
  3065. if (watchInfoModel.initialized) {
  3066. return watchInfoModel.csrfToken;
  3067. } else {
  3068. var dc = JSON.parse($("#watchAPIDataContainer").text());
  3069. return dc.flashvars.csrfToken;
  3070. }
  3071. } else
  3072. if (_token === null && w.FavMylist && w.FavMylist.csrf_token) {
  3073. _token = w.FavMylist.csrf_token;
  3074. }
  3075.  
  3076. if (_token !== '') {
  3077. return _token;
  3078. }
  3079. var url = 'http://' + host + '/mylist_add/video/sm9'; // マイリスト登録ウィンドウから強引にtoken取得
  3080. // var url = 'http://' + host + '/my/mylist'; // マイリスト登録ウィンドウから強引にtoken取得
  3081. GM_xmlhttpRequest({
  3082. url: url,
  3083. onload: function(resp) {
  3084. var result = resp.responseText;
  3085. if (result.match(/NicoAPI\.token = "([a-z0-9\-]+)";/)) {
  3086. token = RegExp.$1;
  3087. }
  3088. }
  3089. });
  3090. return _token;
  3091. }
  3092.  
  3093. var pt = Mylist.prototype, events = {defMylistUpdate: [], mylistUpdate: []};
  3094.  
  3095. function dispatchEvent(name) {
  3096. var e = events[name];
  3097. for (var i =0, len = e.length; i < len; i++) {
  3098. e[i].apply(null, Array.prototype.slice.call(arguments, 1));
  3099. }
  3100. }
  3101.  
  3102. pt.onDefMylistUpdate = function(callback) {
  3103. events.defMylistUpdate.push(callback);
  3104. };
  3105.  
  3106. pt.onMylistUpdate = function(callback) {
  3107. events.mylistUpdate.push(callback);
  3108. };
  3109.  
  3110. pt.getUserId = function() {
  3111. if (document.cookie.match(/user_session_(\d+)/)) {
  3112. return RegExp.$1;
  3113. } else {
  3114. return false;
  3115. }
  3116. };
  3117.  
  3118. var onInitialized = [];
  3119. pt.initialize = function() {
  3120. if (initialized) return;
  3121. var uid = this.getUserId();
  3122. if (!uid) {
  3123. return;
  3124. }
  3125. if (!isNativeGM && host !== location.host) {
  3126. initialized = true;
  3127. return;
  3128. }
  3129. token = getToken();
  3130. //var url = 'http://' + host + '/api/watch/uservideo?user_id=' + uid;
  3131. var url = 'http://' + host + '/api/mylistgroup/list';
  3132. GM_xmlhttpRequest({
  3133. url: url,
  3134. onload: function(resp) {
  3135. var result = JSON.parse(resp.responseText);
  3136. if (result.status === "ok" && result.mylistgroup) {
  3137. mylistlist = result.mylistgroup;
  3138. initialized = true;
  3139. for (var i = 0; i < onInitialized.length; i++) {
  3140. onInitialized[i](mylistlist.concat());
  3141. }
  3142. }
  3143. }
  3144. });
  3145. this.reloadDefList();
  3146. };
  3147.  
  3148. pt.loadMylistList = function(callback) {
  3149. if (initialized) {
  3150. setTimeout(function() { callback(mylistlist.concat()); }, 0);
  3151. } else {
  3152. onInitialized.push(callback);
  3153. }
  3154. };
  3155.  
  3156. pt.isMine = function(id) {
  3157. if (!initialized) { return false; }
  3158. for (var i = 0, len = mylistlist.length; i < len; i++) {
  3159. if (mylistlist[i].id == id) { return true; }
  3160. }
  3161. return false;
  3162. };
  3163.  
  3164. pt.reloadDefList = function(callback) {
  3165. var url = 'http://' + host + '/api/deflist/list';
  3166. GM_xmlhttpRequest({
  3167. url: url,
  3168. onload: function(resp) {
  3169. try {
  3170. JSON.parse(resp.responseText);
  3171. } catch (e) {
  3172. window.console.log(e);
  3173. window.console.log(resp.responseText);
  3174. }
  3175. if (!resp.responseText) return;
  3176. var result = JSON.parse(resp.responseText);
  3177. if (result.status === "ok" && result.mylistitem) {
  3178. defListItems = result.mylistitem;
  3179. if (typeof callback === "function") callback(defListItems);
  3180. }
  3181. }
  3182. });
  3183. };
  3184.  
  3185. pt.loadMylist = function(groupId, callback) {
  3186. if (mylistItems[groupId]) {
  3187. setTimeout(function() {callback(mylistItems[groupId]); }, 0);
  3188. return;
  3189. }
  3190. var url = 'http://' + host + '/api/mylist/list?group_id=' + groupId;
  3191. GM_xmlhttpRequest({
  3192. url: url,
  3193. onload: function(resp) {
  3194. var result = JSON.parse(resp.responseText);
  3195. if (result.status === "ok" && result.mylistitem) {
  3196. mylistItems[groupId] = result.mylistitem;
  3197. if (typeof callback === "function") callback(result.mylistitem);
  3198. }
  3199. }
  3200. });
  3201. };
  3202.  
  3203. pt.clearMylistCache = function(groupId) {
  3204. delete mylistItems[groupId];
  3205. };
  3206.  
  3207. pt.reloadMylist = function(groupId, callback) {
  3208. this.clearMylistCache(groupId);
  3209. return this.loadMylist(groupId, callback);
  3210. };
  3211.  
  3212.  
  3213. pt.findDeflistByWatchId = function(watchId) {
  3214. // if (/^[0-9]+$/.test(watchId)) return watchId; // スレッドIDが来た
  3215.  
  3216. for (var i = 0, len = defListItems.length; i < len; i++) {
  3217. var item = defListItems[i], wid = item.item_data.watch_id;
  3218. if (wid == watchId) return item;
  3219. }
  3220. return null;
  3221. };
  3222.  
  3223. pt.findMylistByWatchId = function(watchId, groupId) {
  3224. // if (/^[0-9]+$/.test(watchId)) return watchId; // スレッドIDが来た
  3225. var items = mylistItems[groupId];
  3226. if (!items) { return null; }
  3227. for (var i = 0, len = items.length; i < len; i++) {
  3228. var item = items[i], wid = item.item_data.watch_id;
  3229. if (wid == watchId) return item;
  3230. }
  3231. return null;
  3232. };
  3233.  
  3234. // おもに参考にしたページ
  3235. // http://uni.res.nimg.jp/js/nicoapi.js
  3236. // http://d.hatena.ne.jp/lolloo-htn/20110115/1295105845
  3237. // http://d.hatena.ne.jp/aTaGo/20100811/1281552243
  3238. pt.deleteDefListItem = function(watchId, callback) {
  3239. var item = this.findDeflistByWatchId(watchId);
  3240. if (!item) return false;
  3241. var item_id = item.item_id;
  3242. var url = 'http://' + host + '/api/deflist/delete';
  3243. var data = 'id_list[0][]=' + item_id + '&token=' + token;
  3244. var req = {
  3245. method: 'POST',
  3246. data: data,
  3247. headers: {'Content-Type': 'application/x-www-form-urlencoded'}, // これを忘れて小一時間はまった
  3248. url: url,
  3249. onload: function(resp) {
  3250. var result = JSON.parse(resp.responseText);
  3251. if (typeof callback === "function") callback(result.status, result);
  3252. if (window.jQuery) {
  3253. defListItems = window.jQuery.grep(defListItems, function(item) {
  3254. return item.item_data.watch_id !== watchId;
  3255. });
  3256. }
  3257. dispatchEvent('defMylistUpdate');
  3258. }
  3259. };
  3260. GM_xmlhttpRequest(req);
  3261. return true;
  3262. };
  3263.  
  3264. pt.addDefListItem = function(watchId, callback, description) {
  3265. var url = 'http://' + host + '/api/deflist/add';
  3266.  
  3267. // 例えば、とりマイの300番目に登録済みだった場合に「登録済みです」と言われても探すのがダルいし、
  3268. // 他の動画を追加していけば、そのうち押し出されて消えてしまう。
  3269. // なので、重複時にエラーを出すのではなく、「消してから追加」することによって先頭に持ってくる。
  3270. // 「重複してたら先頭に持ってきて欲しいな~」って要望掲示板にこっそり書いたりしたけど相手にされないので自分で実装した。
  3271. var data = "item_id=" + watchId + "&token=" + token, replaced = true;
  3272. if (description) {
  3273. data += '&description='+ encodeURIComponent(description);
  3274. }
  3275.  
  3276. var _add = function(status, resp) {
  3277. var req = {
  3278. method: 'POST',
  3279. data: data,
  3280. url: url,
  3281. headers: {'Content-Type': 'application/x-www-form-urlencoded' }, // これを忘れて小一時間はまった
  3282. onload: function(resp) {
  3283. var result = JSON.parse(resp.responseText);
  3284. if (typeof callback === "function") callback(result.status, result, replaced);
  3285. }
  3286. };
  3287. GM_xmlhttpRequest(req);
  3288. };
  3289. // とりあえずマイリストにある場合はdeleteDefListItem()のcallbackで追加、ない場合は即時追加
  3290. if (!this.deleteDefListItem(watchId, _add)) {
  3291. replaced = false;
  3292. _add();
  3293. dispatchEvent('defMylistUpdate');
  3294. }
  3295. };
  3296.  
  3297. pt.addMylistItem = function(watchId, groupId, callback, description) {
  3298. var self = this;
  3299. var url = 'http://' + host + '/api/mylist/add';
  3300. var data = ['item_id=', watchId,
  3301. '&group_id=', groupId,
  3302. '&item_type=', 0, // video=0 seiga=5
  3303. '&description=', (typeof description === 'string') ? encodeURIComponent(description) : '',
  3304. '&token=', token
  3305. ].join('');
  3306. // 普通のマイリストのほうは重複しても「消してから追加」という処理を行っていない。
  3307. // とりあえずマイリストと違って登録の順番に意味があるのと、
  3308. // 古いのが押し出される心配がないため。
  3309. var _add = function() {
  3310. var req = {
  3311. method: 'POST',
  3312. data: data,
  3313. url: url,
  3314. headers: {'Content-Type': 'application/x-www-form-urlencoded' },
  3315. onload: function(resp) {
  3316. var result = JSON.parse(resp.responseText);
  3317. if (typeof callback === "function") callback(result.status, result);
  3318. if (result.status === 'ok') {
  3319. dispatchEvent('mylistUpdate', {action: 'add', groupId: groupId, watchId: watchId});
  3320. EventDispatcher.dispatch('onMylistItemAdded', groupId, watchId);
  3321. self.clearMylistCache(groupId);
  3322. }
  3323. },
  3324. error: function() {
  3325. Popup.alert('ネットワークエラー');
  3326. }
  3327. };
  3328. GM_xmlhttpRequest(req);
  3329. };
  3330. // 普通のマイリストに入れたら、とりあえずマイリストからは削除(≒移動)
  3331. if (!this.deleteDefListItem(watchId, _add)) _add();
  3332. };
  3333.  
  3334. pt.updateMylistItem = function(watchId, groupId, callback, description) {
  3335. var self = this;
  3336. this.loadMylist(groupId, function() {
  3337. var item = self.findMylistByWatchId(watchId, groupId);
  3338. if (!item) {
  3339. Popup.alert('マイリスト中に該当する動画がみつかりませんでした');
  3340. return;
  3341. }
  3342. var
  3343. itemId = item.item_id,
  3344. url = 'http://' + host + '/api/mylist/update',
  3345. data = ['item_id=', itemId,
  3346. '&group_id=', groupId,
  3347. '&item_type=', 0, // video=0 seiga=5
  3348. '&description=', (typeof description === 'string') ? encodeURIComponent(description) : '',
  3349. '&token=', token
  3350. ].join(''),
  3351. req = {
  3352. method: 'POST',
  3353. data: data,
  3354. headers: {'Content-Type': 'application/x-www-form-urlencoded'},
  3355. url: url,
  3356. onload: function(resp) {
  3357. var result = JSON.parse(resp.responseText);
  3358. if (result.status === 'ok') {
  3359. if (typeof callback === "function") callback(result.status, result);
  3360. dispatchEvent('mylistUpdate', {action: 'update', groupId: groupId, watchId: watchId});
  3361. EventDispatcher.dispatch('onMylistItemUpdated', groupId, watchId);
  3362. }
  3363. },
  3364. error: function() {
  3365. Popup.alert('ネットワークエラー');
  3366. }
  3367. };
  3368.  
  3369. GM_xmlhttpRequest(req);
  3370. });
  3371. };
  3372.  
  3373.  
  3374. pt.deleteMylistItem = function(watchId, groupId, callback) {
  3375. var self = this;
  3376. this.loadMylist(groupId, function() {
  3377. var item = self.findMylistByWatchId(watchId, groupId);
  3378. if (!item) {
  3379. Popup.alert('マイリスト中に該当する動画がみつかりませんでした');
  3380. return;
  3381. }
  3382. var
  3383. item_id = item.item_id,
  3384. url = 'http://' + host + '/api/mylist/delete',
  3385. data = [
  3386. 'id_list[0][]=', item_id,
  3387. '&group_id=', groupId,
  3388. '&token=', token
  3389. ].join(''),
  3390. req = {
  3391. method: 'POST',
  3392. data: data,
  3393. headers: {'Content-Type': 'application/x-www-form-urlencoded'},
  3394. url: url,
  3395. onload: function(resp) {
  3396. var result = JSON.parse(resp.responseText);
  3397. if (result.status === 'ok') {
  3398. if (typeof callback === "function") callback(result.status, result);
  3399. dispatchEvent('mylistUpdate', {action: 'delete', groupId: groupId, watchId: watchId});
  3400. EventDispatcher.dispatch('onMylistItemDeleted', groupId, watchId);
  3401. }
  3402. },
  3403. error: function() {
  3404. Popup.alert('ネットワークエラー');
  3405. }
  3406. };
  3407.  
  3408. GM_xmlhttpRequest(req);
  3409. });
  3410. };
  3411.  
  3412.  
  3413. /**
  3414. * マイリスト登録パネルを返す
  3415. */
  3416. pt.getPanel = function(watchId, videoId) {
  3417. if (isNativeGM || host === location.host) {
  3418. return this.getNativePanel(watchId, videoId);
  3419. } else {
  3420. return this.getIframePanel(watchId, videoId);
  3421. }
  3422. };
  3423.  
  3424. pt.getNativePanel = function(watchId, videoId) {
  3425. var self = this;
  3426. var _watchId = watchId, _videoId = videoId || watchId;
  3427. var body = document.createElement('div');
  3428. var mylistListPopup = null;
  3429. body.className = 'mylistPopupPanel deflistSelected';
  3430. var nobr = document.createElement('nobr');
  3431. body.appendChild(nobr);
  3432.  
  3433. var extArea = document.createElement('span');
  3434.  
  3435.  
  3436. var isWatchPage = (window.PlayerApp) ? true : false;
  3437.  
  3438. var addDeflist = function(watchId, description) {
  3439. self.addDefListItem(watchId, function(status, result, replaced) {
  3440. self.reloadDefList();
  3441. if (status !== 'ok') {
  3442. Popup.alert('とりあえずマイリストへの登録に失敗: ' + result.error.description);
  3443. } else {
  3444. var torimai = '<a href="/my/mylist">とりあえずマイリスト</a>';
  3445. Popup.show(
  3446. torimai +
  3447. (replaced ? 'の先頭に移動しました' : 'に登録しました')
  3448. );
  3449. }
  3450. }, description);
  3451. };
  3452. var addMylist = function(watchId, mylistId, mylistName, description) {
  3453. self.addMylistItem(watchId, mylistId, function(status, result) {
  3454. self.reloadDefList();
  3455. if (status === 'ok') {
  3456. Popup.show( '<a href="/my/mylist/#/' + mylistId + '">' + mylistName + '</a>に登録しました');
  3457. } else {
  3458. Popup.alert(mylistName + 'への登録に失敗: ' + result.error.description);
  3459. }
  3460. }, description);
  3461. };
  3462. var setButtonStyleUpdating = function(btn) {
  3463. btn.style.opacity = 0.5;
  3464. btn.style.cursor = 'pointer';
  3465. btn.disabled = true;
  3466.  
  3467. window.setTimeout(function() {
  3468. btn.disabled = false;
  3469. btn.style.opacity = 1;
  3470. btn.style.cursor = 'pointer';
  3471. btn = null;
  3472. }, 1000);
  3473. };
  3474. var onMylistListClick = function(mylistId, mylistName, type) {
  3475. if (type === 'icon') {
  3476. if (window.WatchApp) {
  3477. if (mylistId === 'default') {
  3478. WatchController.showDeflist();
  3479. } else {
  3480. WatchController.showMylist(mylistId);
  3481. }
  3482. } else {
  3483. location.href = 'http://' + host + '/my/mylist/#/' + mylistId.replace('default','home');
  3484. }
  3485. return;
  3486. }
  3487. if (mylistId === 'default') {
  3488. addDeflist(_watchId);
  3489. } else {
  3490. addMylist(_watchId, mylistId, mylistName);
  3491. }
  3492. };
  3493.  
  3494. body.watchId = function(w, v) {
  3495. if (w) {
  3496. _watchId = w;
  3497. _videoId = v || w;
  3498. var isThreadId = (/^[0-9]+$/.test(w));
  3499.  
  3500. deleteDef.disabled = false;
  3501. if (self.findDeflistByWatchId(w)) {
  3502. deleteDef.style.display = '';
  3503. } else {
  3504. deleteDef.style.display = 'none';
  3505. }
  3506. if (!isWatchPage && isThreadId) {
  3507. tagBtn.style.display = 'none'; // スレッドIDから動画IDを取る手段がないためタグ取得が難しい
  3508. } else {
  3509. tagBtn.style.display = '';
  3510. }
  3511. if (newTabLink) {
  3512. newTabLink.href = 'http://nico.ms/' + _watchId; // QWatchに乗っ取られないようにnico.msをかます(せこい)
  3513. }
  3514. if (mylistListPopup) {
  3515. mylistListPopup.hide();
  3516. }
  3517. return body;
  3518. }
  3519. return _watchId;
  3520. };
  3521.  
  3522. body.show = function() {
  3523. body.style.display = '';
  3524. if (mylistListPopup) {
  3525. mylistListPopup.hide();
  3526. }
  3527. };
  3528. body.hide = function() {
  3529. body.style.display = 'none';
  3530. if (mylistListPopup) {
  3531. mylistListPopup.hide();
  3532. }
  3533. };
  3534.  
  3535. function createSelector() {
  3536. var sel = document.createElement('select');
  3537. var lastSelect = 0;
  3538.  
  3539. sel.className = 'mylistSelect';
  3540. var appendO = function(sel, text, value) {
  3541. var opt = document.createElement('option');
  3542. opt.appendChild(document.createTextNode(text));
  3543. opt.value = value;
  3544. sel.appendChild(opt);
  3545. return opt;
  3546. },
  3547. createOptions = function() {
  3548. for (var i = 0, len = mylistlist.length; i < len; i++) {
  3549. var mylist = mylistlist[i];
  3550. appendO(sel, (i + 1).toString(36) + ':' + mylist.name, mylist.id);
  3551. }
  3552. },
  3553. onSelect = function() {
  3554. // jQueryは全てのページにあるわけではないので気をつける。忘れると原宿が死ぬ
  3555. if (sel.selectedIndex === 0) {
  3556. body.className = body.className.replace('mylistSelected', 'deflistSelected');
  3557. } else {
  3558. lastSelect = sel.selectedIndex;
  3559. body.className = body.className.replace('deflistSelected', 'mylistSelected');
  3560. }
  3561. },
  3562. selectDeflist = function() {
  3563. sel.selectedIndex = 0;
  3564. onSelect();
  3565. },
  3566. onContextMenu = function(e) {
  3567. e.preventDefault();
  3568. e.stopPropagation();
  3569.  
  3570. if (lastSelect === 0) return;
  3571. if (sel.selectedIndex === 0) {
  3572. sel.selectedIndex = lastSelect;
  3573. } else {
  3574. sel.selectedIndex = 0;
  3575. }
  3576. onSelect();
  3577. };
  3578.  
  3579. appendO(sel, '0:とりマイ', 'default');
  3580. sel.selectedIndex = 0;
  3581. window.setTimeout(createOptions, initialized ? 0 : 3000);
  3582.  
  3583. sel.addEventListener('change', onSelect, false);
  3584. sel.addEventListener('contextmenu', onContextMenu, false);
  3585.  
  3586.  
  3587. body.addEventListener('dblclick', selectDeflist, false);
  3588. return sel;
  3589. }
  3590.  
  3591. function createSubmitButton() {
  3592. var btn = document.createElement('button');
  3593. btn.appendChild(document.createTextNode('my'));
  3594. btn.className = 'mylistAdd';
  3595. btn.title = 'マイリストに追加\n(ボタンを右クリックで詳細メニュー)';
  3596.  
  3597. var callMylistListPopup = function() {
  3598. if (!mylistListPopup) {
  3599. mylistListPopup = new MylistListPopup(mylistlist, onMylistListClick);
  3600. }
  3601. mylistListPopup.toggle(btn, _watchId);
  3602. };
  3603.  
  3604. btn.addEventListener('contextmenu', function(e) {
  3605. if (window.jQuery) {
  3606. e.preventDefault();
  3607. e.stopPropagation();
  3608. callMylistListPopup();
  3609. }
  3610. });
  3611.  
  3612. btn.addEventListener('click', function(e) {
  3613. var description = null;
  3614. if (e.shiftKey) {
  3615. description = prompt('マイリストコメントの入力');
  3616. if (!description) return;
  3617. }
  3618. setButtonStyleUpdating(btn);
  3619.  
  3620. var mylistId = sel.value, name = sel.options[sel.selectedIndex].textContent;
  3621. if (mylistId === 'default') {
  3622. addDeflist(_watchId, description);
  3623. } else {
  3624. addMylist(_watchId, mylistId, name, description);
  3625. }
  3626. } ,false);
  3627. return btn;
  3628. }
  3629.  
  3630. function createDeleteDeflistItemButton() {
  3631. var btn = document.createElement('button');
  3632. btn.appendChild(document.createTextNode('×'));
  3633. btn.className = 'deflistRemove';
  3634. btn.title = 'とりあえずマイリストから外す';
  3635.  
  3636. btn.addEventListener('click', function() {
  3637.  
  3638. setButtonStyleUpdating(btn);
  3639.  
  3640. self.deleteDefListItem(_watchId, function(status, result) {
  3641. self.reloadDefList();
  3642. btn.style.display = 'none';
  3643. if (status !== "ok") {
  3644. Popup.alert('とりあえずマイリストから削除に失敗: ' + result.error.description);
  3645. } else {
  3646. Popup.show('とりあえずマイリストから外しました');
  3647. }
  3648. });
  3649. } ,false);
  3650. return btn;
  3651. }
  3652.  
  3653. function createTagListButton() {
  3654. var btn = document.createElement('button');
  3655. btn.appendChild(document.createTextNode('tag'));
  3656. btn.className = 'tagGet';
  3657. btn.title = 'タグ取得';
  3658. btn.addEventListener('click', function(e) {
  3659. btn.disabled = true;
  3660.  
  3661. setButtonStyleUpdating(btn);
  3662.  
  3663. if (w.jQuery) {
  3664. var $btn = w.jQuery(btn), o = $btn.offset();
  3665. VideoTags.popupItems(_videoId, o.left, o.top + $btn.outerHeight());
  3666. } else {
  3667. VideoTags.popupItems(_videoId, e.pageX, e.pageY);
  3668. }
  3669. } ,false);
  3670. return btn;
  3671. }
  3672.  
  3673. function createNewTabLink() {
  3674. var a = document.createElement('a');
  3675. a.className = 'newTabLink';
  3676. a.target = '_blank';
  3677. a.title = 'この動画を新しいウィンドウで開く';
  3678. a.innerHTML = '▲';
  3679. return a;
  3680. }
  3681.  
  3682. var newTabLink = createNewTabLink();
  3683. if (w.WatchApp) {
  3684. nobr.appendChild(newTabLink);
  3685. }
  3686.  
  3687.  
  3688. var sel = createSelector();
  3689. var submit = createSubmitButton(sel);
  3690. nobr.appendChild(sel);
  3691. nobr.appendChild(submit);
  3692. if (w.jQuery) {
  3693. w.jQuery(sel).keydown(function(e) {
  3694. e.stopPropagation();
  3695. if (e.keyCode === 13) { // ENTER
  3696. submit.click();
  3697. }
  3698. });
  3699. }
  3700.  
  3701. var tagBtn = createTagListButton();
  3702. nobr.appendChild(tagBtn);
  3703.  
  3704. var deleteDef = createDeleteDeflistItemButton();
  3705. nobr.appendChild(deleteDef);
  3706.  
  3707.  
  3708.  
  3709. nobr.appendChild(extArea);
  3710.  
  3711. body.watchId(_watchId, _videoId);
  3712. return body;
  3713. };
  3714.  
  3715. // XHRでクロスドメインを超えられない場合はこちら
  3716. // 将来マイリストのポップアップウィンドウが廃止されたら使えない
  3717. // (マイページから強引に生成するか?)
  3718. pt.getIframePanel = function(watchId) {
  3719. var _watchId = watchId;
  3720. var body = document.createElement('iframe');
  3721. body.name = 'nicomylistaddDummy';
  3722. body.className = 'mylistPopupPanel';
  3723. body.setAttribute('style', 'width: 130px; height: 24px; z-index: 10000; border: 1px solid silver; padding: 0; margin: 0; overflow: hidden');
  3724.  
  3725. body.watchId = function(w) {
  3726. if (w) {
  3727. _watchId = w;
  3728. body.contentWindow.location.replace("http:/" + "/www.nicovideo.jp/mylist_add/video/" + w);
  3729. return body;
  3730. }
  3731. return _watchId;
  3732. };
  3733. if (watchId !== '') {
  3734. body.src = "http:/" + "/www.nicovideo.jp/mylist_add/video/" + _watchId;
  3735. }
  3736.  
  3737. // ダミーメソッド
  3738. body.show = function() {
  3739. body.style.display = '';
  3740. };
  3741. body.hide = function() {
  3742. body.style.display = 'none';
  3743. };
  3744.  
  3745.  
  3746. return body;
  3747. };
  3748.  
  3749. return new Mylist();
  3750. })();
  3751.  
  3752. var MylistListPopup = function() { this.initialize.apply(this, arguments); };
  3753. MylistListPopup.prototype = {
  3754. initialize: function(mylistList, onItemClick) {
  3755. this._mylistList = mylistList.concat();
  3756. this.initializeView(mylistList);
  3757. this._onItemClick = onItemClick;
  3758. },
  3759. initializeView: function() {
  3760. var $ = window.jQuery;
  3761. this._$view = $([
  3762. '<div class="mylistListPopup popupMenu">',
  3763. '<div class="listInner">',
  3764. '<ul></ul>',
  3765. '</div>',
  3766. '</div>',
  3767. ''].join(''));
  3768. this._$list = this._$view.find('ul');
  3769. this._$inner = this._$view.find('.listInner');
  3770.  
  3771.  
  3772. $('body').append(this._$view);
  3773.  
  3774. this.refresh();
  3775.  
  3776. this.initializeEvent(this._$view);
  3777. },
  3778. initializeEvent: function($view) {
  3779. $view.on('click', window.jQuery.proxy(function(e) {
  3780. if (e.button !== 0 || e.metaKey || e.shiftKey || e.altKey || e.ctrlKey) { return; }
  3781. var target = e.target, $target = window.jQuery(target);
  3782. this.hide();
  3783. e.preventDefault();
  3784.  
  3785. var mylistId = $target.attr('data-mylist-id');
  3786. var mylistName = $target.attr('data-mylist-name');
  3787. if (!mylistId) { return; }
  3788. var type = target.className;
  3789.  
  3790. if (typeof this._onItemClick === 'function') {
  3791. this._onItemClick(mylistId, mylistName, type);
  3792. }
  3793. }, this));
  3794. var self = this, closeTimer = null;
  3795. $view.hover(
  3796. function() {
  3797. if (closeTimer) { window.clearTimeout(closeTimer); }
  3798. },
  3799. function() {
  3800. closeTimer = window.setTimeout(function() { self.hide(); }, 1000);
  3801. });
  3802.  
  3803. $view = null;
  3804. },
  3805. adjustColumnCount: function() {
  3806. this._$inner.css({
  3807. 'column-count': '',
  3808. 'max-height': ''
  3809. });
  3810. var height = this._$view.outerHeight(),
  3811. clientHeight = window.jQuery(window).innerHeight(),
  3812. threshold = clientHeight * 0.4;
  3813. if (threshold < height) {
  3814. var columns = parseInt( height / threshold, 10) + 1;
  3815. this._$inner.css({
  3816. 'column-count': columns,
  3817. 'max-height': clientHeight * 0.8
  3818. });
  3819. }
  3820. },
  3821. updateList: function(mylistList) {
  3822. this._mylistList = mylistList.concat();
  3823. this.refresh();
  3824. },
  3825. refresh: function() {
  3826. var mylistList = this._mylistList;
  3827. this._$list.empty();
  3828. for (var i = 0, len = mylistList.length; i < len; i++) {
  3829. var mylist = mylistList[i];
  3830. this.appendItem(mylist.id, mylist.name, mylist.icon_id);
  3831. }
  3832. this.appendItem('default', 'とりあえずマイリスト');
  3833.  
  3834. this.adjustColumnCount();
  3835. },
  3836. appendItem: function(id, name, icon_id) {
  3837. var $mylist = window.jQuery([
  3838. '<li class="folder', icon_id, '">',
  3839. '<span class="icon"></span>',
  3840. '<a href="my/mylist/#/', id.replace('default', 'home'), '" class="name">',
  3841. name,
  3842. '</a>',
  3843. '</li>',
  3844. ''].join(''));
  3845. $mylist.find('.icon, .name').attr({
  3846. 'data-mylist-id': id,
  3847. 'data-mylist-name': name
  3848. });
  3849.  
  3850. if (id === 'default') {
  3851. $mylist.addClass('deflist');
  3852. } else
  3853. if (id.indexOf('ext') === 0) {
  3854. $mylist.addClass('ext');
  3855. }
  3856. this._$list.append($mylist);
  3857. },
  3858. updateExist: function(watchId) {
  3859. if (!watchId) {
  3860. return;
  3861. }
  3862. this._$view.find('.exist').removeClass('exist');
  3863. this._$view.find('.name').each(function() {
  3864. var $this = window.jQuery(this), mylistId = $this.attr('data-mylist-id');
  3865. if (mylistId === 'default') { return; }
  3866. $this
  3867. .toggleClass('exist', window.WatchItLater.mylist.cache.hasItem(mylistId, watchId));
  3868. });
  3869.  
  3870. this._$view.find('.deflist .name')
  3871. .toggleClass('exist', window.WatchItLater.mylist.findDeflistByWatchId(watchId) !== null);
  3872. },
  3873. show: function(elm, watchId) {
  3874. this.adjustColumnCount();
  3875. this._$view.addClass('show active');
  3876.  
  3877. if (!elm) { return; }
  3878. var
  3879. $ = window.jQuery,
  3880. $elm = $(elm),
  3881. o = $elm.offset(),
  3882. $view = this._$view,
  3883. $window = $(window),
  3884. scrollLeft = $window.scrollLeft(),
  3885. scrollTop = $window.scrollTop();
  3886.  
  3887. $view.css({
  3888. top: Math.max(o.top - $view.outerHeight(), 0, scrollTop),
  3889. left: o.left
  3890. });
  3891. if ($view.offset().left + $view.outerWidth() > $window.innerWidth() + scrollLeft) {
  3892. $view.css({
  3893. left: Math.max(0, $window.innerWidth() + scrollLeft - $view.outerWidth())
  3894. });
  3895. }
  3896.  
  3897. this.updateExist(watchId);
  3898. window.jQuery('body').on('click', $.proxy(this._onBodyClick, this));
  3899. },
  3900. hide: function() {
  3901. var $view = this._$view
  3902. .removeClass('show');
  3903. window.setTimeout(function() {
  3904. $view.css({top: '', left: '', right: ''}).removeClass('active');
  3905. }, 500);
  3906. window.jQuery('body').off('click', this._onBodyClick);
  3907. },
  3908. toggle: function(elm, watchId) {
  3909. if (this._$view.hasClass('avtive')) {
  3910. this.hide();
  3911. } else {
  3912. this.show(elm, watchId);
  3913. }
  3914. },
  3915. _onBodyClick: function() {
  3916. this.hide();
  3917. }
  3918. };
  3919.  
  3920. window.WatchItLater.mylist.cache = (function() {
  3921. var CacheList = function() { this.initialize.apply(this, arguments); };
  3922. CacheList.prototype = {
  3923. initialize: function() {
  3924. this.reset();
  3925. },
  3926. reset: function() {
  3927. this._cacheList = {};
  3928. },
  3929. setCache: function(mylistId, items) {
  3930. if (!this.hasCache(mylistId)) {
  3931. this._cacheList[mylistId] = new MylistCache();
  3932. }
  3933. this._cacheList[mylistId].update(items);
  3934. },
  3935. hasCache: function(mylistId) {
  3936. if (this._cacheList[mylistId]) {
  3937. return true;
  3938. }
  3939. return false;
  3940. },
  3941. hasItem: function(mylistId, watchId) {
  3942. if (!this.hasCache(mylistId)) {
  3943. return false;
  3944. }
  3945. return this._cacheList[mylistId].hasItem(watchId);
  3946. },
  3947. addItem: function(mylistId, watchId) {
  3948. if (!this.hasCache(mylistId)) {
  3949. return false;
  3950. }
  3951. this._cacheList[mylistId].addItem(watchId);
  3952. },
  3953. removeItem: function(mylistId, watchId) {
  3954. if (!this.hasCache(mylistId)) {
  3955. return false;
  3956. }
  3957. this._cacheList[mylistId].removeItem(watchId);
  3958. },
  3959. count: function(mylistId) {
  3960. if (!this.hasCache(mylistId)) {
  3961. return NaN;
  3962. }
  3963. this._cacheList[mylistId].count();
  3964. },
  3965. toJSON: function() {
  3966. var cacheList = this._cacheList;
  3967. return this._cacheList;
  3968. },
  3969. parse: function(jsonString) {
  3970. var data;
  3971. try {
  3972. data = JSON.parse(jsonString);
  3973. } catch (e) {
  3974. data = {};
  3975. }
  3976. this.reset();
  3977. for (var mylistId in data) {
  3978. var mylistCache = data[mylistId];
  3979. this._cacheList[mylistId] = new MylistCache(mylistCache);
  3980. }
  3981. }
  3982. };
  3983.  
  3984. var MylistCache = function() { this.initialize.apply(this, arguments); };
  3985. MylistCache.prototype = {
  3986. initialize: function(mylistData) {
  3987. this._name = '';
  3988. if (mylistData) {
  3989. this.update(mylistData);
  3990. }
  3991. },
  3992. update: function(mylistData) {
  3993. this._cache = [];
  3994. this._hash = {};
  3995. var items = mylistData.items ? mylistData.items : mylistData;
  3996. for (var i = 0, len = items.length; i < len; i++) {
  3997. var
  3998. item = items[i],
  3999. watchId = typeof item.getId === 'function' ? item.getId() : item.id;
  4000. this._cache.push({id: watchId});
  4001. this._hash[watchId] = true;
  4002. }
  4003. if (mylistData.name) {
  4004. this._name = mylistData.name;
  4005. }
  4006. },
  4007. hasItem: function(watchId) {
  4008. return this._hash[watchId] === true;
  4009. },
  4010. addItem: function(watchId) {
  4011. if (this.hasItem(watchId)) {
  4012. return;
  4013. }
  4014. this._hash[watchId] = true;
  4015. this._cache.push({id: watchId});
  4016. },
  4017. removeItem: function(watchId) {
  4018. if (!this.hasItem(watchId)) {
  4019. return;
  4020. }
  4021. delete this._hash[watchId];
  4022. this._cache = $.grep(this._cache, function(item) {
  4023. return item.id !== watchId;
  4024. });
  4025. },
  4026. count: function() {
  4027. return this._cache.length;
  4028. },
  4029. toJSON: function() {
  4030. return {
  4031. name: this._name,
  4032. items: this._cache
  4033. };
  4034. },
  4035. parse: function(jsonString) {
  4036. var items;
  4037. try {
  4038. items = JSON.parse(jsonString);
  4039. } catch (e) {
  4040. items = [];
  4041. }
  4042. this.update(items);
  4043. }
  4044. };
  4045.  
  4046. var cacheList, noop = function() {};
  4047. var initialize = function() {
  4048. initialize = noop;
  4049. console.log('%cinitialize mylistCache', 'background: lightgreen;');
  4050. cacheList = new CacheList();
  4051.  
  4052. if (conf.enableLocalMylistCache) {
  4053. if (window.PlayerApp) {
  4054. $(window).on('beforeunload.watchItLater', function(e) {
  4055. window.localStorage.setItem('watchItLater_mylistCache', serialize());
  4056. });
  4057. }
  4058. var cacheData = window.localStorage.getItem('watchItLater_mylistCache');
  4059. if (cacheData) {
  4060. cacheList.parse(cacheData);
  4061. }
  4062. }
  4063. };
  4064. var hasCache = function(mylistId, watchId) {
  4065. initialize();
  4066. return cacheList.hasItem(mylistId, watchId);
  4067. };
  4068. var hasItem = function(mylistId, watchId) {
  4069. initialize();
  4070. return cacheList.hasItem(mylistId, watchId);
  4071. };
  4072. var addItem = function(mylistId, watchId) {
  4073. initialize();
  4074. cacheList.addItem(mylistId, watchId);
  4075. };
  4076. var removeItem = function(mylistId, watchId) {
  4077. initialize();
  4078. cacheList.removeItem(mylistId, watchId);
  4079. };
  4080. var setCache = function(mylistId, items) {
  4081. initialize();
  4082. cacheList.setCache(mylistId, items);
  4083. };
  4084. var serialize = function() {
  4085. initialize();
  4086. return JSON.stringify(cacheList);
  4087. };
  4088. var unserialize = function(json) {
  4089. initialize();
  4090. cacheList.parse(json);
  4091. };
  4092. var clearCache = function() {
  4093. window.localStorage.removeItem('watchItLater_mylistCache');
  4094. };
  4095.  
  4096.  
  4097. EventDispatcher.addEventListener('onMyMylistLoad', function(mylistId, list) {
  4098. setCache(mylistId, list || []);
  4099. });
  4100. EventDispatcher.addEventListener('onMylistItemAdded', function(mylistId, watchId) {
  4101. initialize();
  4102. cacheList.addItem(mylistId, watchId);
  4103. });
  4104. EventDispatcher.addEventListener('onMylistItemDeleted', function(mylistId, watchId) {
  4105. initialize();
  4106. cacheList.removeItem(mylistId, watchId);
  4107. });
  4108.  
  4109.  
  4110. return {
  4111. initialize: initialize,
  4112. hasItem: hasItem,
  4113. addItem: addItem,
  4114. setCache: setCache,
  4115. serialize: serialize,
  4116. unserialize: unserialize,
  4117. clearCache: clearCache
  4118. };
  4119. })();
  4120.  
  4121.  
  4122. var LocationHashParser = (function(conf, w) {
  4123. var self, dat = {};
  4124.  
  4125. function initialize() {
  4126. var hash = w.location.hash.toString();
  4127. var redirectedHash = window.sessionStorage.getItem('watchItLater_redirectedHash');
  4128. if (redirectedHash) {
  4129. console.log('%cNiconicodo redirect', 'background: lightgreen;');
  4130. hash = redirectedHash;
  4131. window.sessionStorage.removeItem('watchItLater_redirectedHash');
  4132. }
  4133. try {
  4134. if (hash.indexOf('#json={') === 0) {
  4135. dat = JSON.parse(hash.substr(6));
  4136. }
  4137. } catch (e) {
  4138. try {
  4139. dat = JSON.parse(decodeURIComponent(hash.substr(6)));
  4140. } catch(ex) {
  4141. console.log(ex);
  4142. }
  4143. console.log(e);
  4144. }
  4145. }
  4146. function setValue(key, value) {
  4147. dat[key] = value;
  4148. }
  4149. function getValue(key) {
  4150. return dat[key];
  4151. }
  4152. function deleteValue(key) {
  4153. delete dat[key];
  4154. }
  4155. function updateHash() {
  4156. var loc = window.location.href.split('#')[0];
  4157. location.replace(loc + getHash());
  4158. }
  4159. function removeHash() {
  4160. if (location.hash.length <= 1) { return; }
  4161. var scrollTop = $(window).scrollTop();
  4162. var loc = window.location.href.split('#')[0];
  4163. location.replace(loc + '#');
  4164. $(window).scrollTop(scrollTop);
  4165. }
  4166. function getHash() {
  4167. var json = JSON.stringify(dat);
  4168. if (json === '{}') { return ''; }
  4169. return '#json=' + json;
  4170. }
  4171. function getUrl() {
  4172. var loc = window.location.href.split('#')[0];
  4173. return loc + getHash();
  4174. }
  4175. function clear() {
  4176. dat = {};
  4177. removeHash();
  4178. }
  4179.  
  4180. self = {
  4181. initialize: initialize,
  4182. setValue: setValue,
  4183. getValue: getValue,
  4184. deleteValue: deleteValue,
  4185. updateHash: updateHash,
  4186. removeHash: removeHash,
  4187. getHash: getHash,
  4188. getUrl: getUrl,
  4189. clear: clear
  4190. };
  4191. return self;
  4192. })(conf, w);
  4193.  
  4194. window.WatchItLater.loader.favMylists = (function() {
  4195. var lastUpdate = 0;
  4196. var favMylistList = [];
  4197. var host = location.host.replace(/^([\w\d]+)\./, 'www.');
  4198. var $ = w.$;
  4199. /**
  4200. * お気に入りマイリストの取得。 jQueryのあるページでしか使えない
  4201. * マイページを無理矢理パースしてるので突然使えなくなるかも
  4202. */
  4203. var self = {
  4204. load: function(callback) {
  4205. if (!w.jQuery) return; //
  4206.  
  4207. function request(page) {
  4208. url = baseUrl + '?page=' + page;
  4209. GM_xmlhttpRequest({
  4210. url: url,
  4211. onload: function(resp) {
  4212. var $result = $(resp.responseText).find('#favMylist');
  4213.  
  4214. if ($result.length >= 1) {
  4215. updateMaxPage($result);
  4216.  
  4217. if (page === 1) { favMylistList = []; }
  4218.  
  4219. $result.find('.outer').each(function() {
  4220. favMylistList.push(readBlock(this));
  4221. });
  4222. }
  4223.  
  4224. if (page < maxPage) {
  4225. setTimeout(function() {
  4226. page++;
  4227. request(page);
  4228. }, 500);
  4229. } else {
  4230. sort();
  4231. do_callback();
  4232. }
  4233. }
  4234. });
  4235. }
  4236. function readBlock(elm) {
  4237. var
  4238. $elm = $(elm),
  4239. $a = $elm.find('h5 a'), $desc = $elm.find('.mylistDescription'),
  4240. iconType = $elm.find('.folderIcon').attr('class').split(' ')[1],
  4241. id = ($a.attr('href').split('/').reverse())[0],
  4242. $postTime = $elm.find('.postTime span'),
  4243. postTime = $.trim($postTime.text()),
  4244. postTimeData = $postTime.data(),
  4245. $videoLink = $elm.find('.videoTitle a'),
  4246. videoTitle = $videoLink.text(),
  4247. videoHref = $videoLink.attr('href'),
  4248. videoId = videoHref ? (videoHref.split('/').reverse()[0]) : '';
  4249. return {
  4250. id: id,
  4251. name: $a.text(),
  4252. description: $desc.text(),
  4253. iconType: iconType,
  4254. lastVideo: {
  4255. title: videoTitle,
  4256. videoId: videoId,
  4257. postedAt: postTime,
  4258. postTimeData: postTimeData
  4259. }
  4260. };
  4261. }
  4262.  
  4263. function updateMaxPage($result) {
  4264. var $paging = $result.find('.pagerWrap:first .pager:first a');
  4265. maxPage = Math.min(Math.max($paging.length, 1), 3);
  4266. }
  4267. function sort() {
  4268. favMylistList.sort(function(a, b) {
  4269. return (a.lastVideo.postedAt < b.lastVideo.postedAt) ? 1 : -1;
  4270. });
  4271. }
  4272. function do_callback() {
  4273. if (typeof callback === 'function') { callback(favMylistList); }
  4274. }
  4275.  
  4276. var now = Date.now();
  4277. if (now - lastUpdate < 3 * 60 * 1000) {
  4278. do_callback();
  4279. return;
  4280. }
  4281. lastUpdate = now;
  4282.  
  4283. var
  4284. baseUrl = 'http://' + host + '/my/fav/mylist',
  4285. url = baseUrl,
  4286. maxPage = 1;
  4287.  
  4288. request(1);
  4289.  
  4290. }
  4291. };
  4292. return self;
  4293. })();
  4294.  
  4295.  
  4296. window.WatchItLater.loader.favTags = (function(w) {
  4297. var lastUpdate = 0;
  4298. var favTagList = [], favTagTextList = [];
  4299. var host = location.host.replace(/^([\w\d]+)\./, 'www.');
  4300. var $ = w.$;
  4301. var pt = function(){};
  4302.  
  4303. var load = function(callback) {
  4304. if (!w.jQuery) return; //
  4305. var now = Date.now();
  4306. if (now - lastUpdate < 60 * 1000) {
  4307. if (typeof callback === 'function') { callback(favTagList); }
  4308. return;
  4309. }
  4310. lastUpdate = now;
  4311. var api = 'http://' + host + '/api/favtag/list?t=' + now;
  4312. $.ajax({
  4313. url: api,
  4314. timeout: 30000,
  4315. complete: function(result) {
  4316. if (result.status !== 200) {
  4317. return;
  4318. }
  4319. try {
  4320. var json = JSON.parse(result.responseText), items = json.favtag_items;
  4321. for (var i = 0, len = items.length; i < len; i++) {
  4322. var text = items[i]['tag'];
  4323. favTagList.push({href: '/tag/' + encodeURIComponent(text), name: items[i]['tag']});
  4324. favTagTextList.push(text);
  4325. }
  4326. EventDispatcher.dispatch('onFavTagsLoad', favTagTextList.concat());
  4327. if (typeof callback === 'function') { callback(favTagList); }
  4328. } catch (e) {
  4329. console.log('tag parse error!', e);
  4330. }
  4331. }
  4332. });
  4333. };
  4334.  
  4335. pt.load = load;
  4336. return pt;
  4337. })(w);
  4338.  
  4339.  
  4340. /**
  4341. * 左下に出るポップアップメッセージ
  4342. *
  4343. */
  4344. var Popup = (function(){
  4345. function Popup() {}
  4346.  
  4347. Popup.show = function(text) {
  4348. console.log('%c' + text, 'background: cyan;');
  4349. if (w.WatchApp) {
  4350. text = text.replace(/[\n]/, '<br />');
  4351. w.WatchApp.ns.init.PopupMarqueeInitializer.popupMarqueeViewController.onData(
  4352. // Firefoxではflashの上に半透明要素を重ねられないのでとりあえず黒で塗りつぶす
  4353. '<span style="background: black;">' + text + '</span>'
  4354. );
  4355. }
  4356. };
  4357.  
  4358. Popup.alert = function(text) {
  4359. console.log('%c' + text, 'background: yellow;');
  4360. if (w.WatchApp) {
  4361. text = text.replace(/[\n]/, '<br />');
  4362. w.WatchApp.ns.init.PopupMarqueeInitializer.popupMarqueeViewController.onData(
  4363. '<span style="background: black; color: red;">' + text + '</span>'
  4364. );
  4365. } else {
  4366. w.alert(text);
  4367. }
  4368. };
  4369.  
  4370. Popup.hide = function() {
  4371. if (w.WatchApp) {
  4372. w.WatchApp.ns.init.PopupMarqueeInitializer.popupMarqueeViewController.stop();
  4373. }
  4374. };
  4375. return Popup;
  4376. })();
  4377.  
  4378.  
  4379. var KeyMatch = (function() {
  4380. var self;
  4381.  
  4382. function create(def) {
  4383. var ch = def.char[0].toUpperCase();
  4384. return {
  4385. prop: {
  4386. char: ch,
  4387. code: typeof def.code === 'number' ? def.code : ch.charCodeAt(0),
  4388. shift: !!def.shift,
  4389. ctrl: !!def.ctrl,
  4390. alt: !!def.alt,
  4391. enable: !!def.enable
  4392. },
  4393. test: function(event) {
  4394. if (
  4395. this.prop.enable === true &&
  4396. this.prop.shift === event.shiftKey &&
  4397. this.prop.ctrl === event.ctrlKey &&
  4398. this.prop.alt === event.altKey &&
  4399. this.prop.code === event.which
  4400. ) {
  4401. event.preventDefault();
  4402. return true;
  4403. }
  4404. return false;
  4405. },
  4406. json: function() {
  4407. return JSON.stringify(this.prop);
  4408. }
  4409. };
  4410. }
  4411.  
  4412. self = {
  4413. create: create
  4414. };
  4415. return self;
  4416. })();
  4417.  
  4418. var TouchEventDispatcher = (function(target) {
  4419. var
  4420. self,
  4421. touchStartEvent = null,
  4422. touchEndEvent = null,
  4423. events = {
  4424. onflick: []
  4425. };
  4426. function dispatchEvent(name) {
  4427. var e = events[name];
  4428. for (var i =0, len = e.length; i < len; i++) {
  4429. e[i].apply(null, Array.prototype.slice.call(arguments, 1));
  4430. }
  4431. }
  4432.  
  4433. target.addEventListener('touchstart', function(e) {
  4434. touchStartEvent = e;
  4435. }, false);
  4436. target.addEventListener('touchcancel', function(e) {
  4437. touchStartEvent = null;
  4438. }, false);
  4439. target.addEventListener('touchend', function(e) {
  4440. touchEndEvent = e;
  4441. if (touchStartEvent !== null) {
  4442. var
  4443. sx = touchStartEvent.changedTouches[0].pageX, sy = touchStartEvent.changedTouches[0].pageY,
  4444. ex = touchEndEvent.changedTouches[0].pageX, ey = touchEndEvent.changedTouches[0].pageY,
  4445. dx = (sx - ex), dy = (sy - ey), len = Math.sqrt(dx * dx + dy * dy), s;
  4446. if (len > 150) {
  4447. s = dy / len;
  4448. var a = Math.abs(s), ss = Math.round(s);
  4449. if (a <= 0.3 || a >= 0.7) {
  4450. var d;
  4451. if (ss < 0) { d = 'down'; } else if (ss > 0) { d = 'up'; }
  4452. else if (dx < 0) { d = 'right';} else { d = 'left'; }
  4453. dispatchEvent('onflick', {
  4454. direction: d,
  4455. distance: len,
  4456. x: dx, y: dy,
  4457. startEvent: touchStartEvent,
  4458. endEvent: touchEndEvent
  4459. });
  4460. }
  4461. }
  4462. }
  4463. touchStartEvent = touchEndEvent = null;
  4464. }, false);
  4465.  
  4466. function onflick(callback) {
  4467. events.onflick.push(callback);
  4468. }
  4469.  
  4470. self = {
  4471. onflick: onflick
  4472. };
  4473. return self;
  4474. })(w.document);
  4475.  
  4476.  
  4477.  
  4478. /**
  4479. * リンクのマウスオーバーに仕込む処理
  4480. * ここの表示は再考の余地あり
  4481. */
  4482. var AnchorHoverPopup = (function(w, conf,EventDispatcher) {
  4483. var mylistPanel = Mylist.getPanel(''), hoverMenuDelay = conf.hoverMenuDelay * 1000;
  4484. mylistPanel.className += ' popup';
  4485. mylistPanel.style.display = 'none';
  4486. document.body.appendChild(mylistPanel);
  4487.  
  4488. EventDispatcher.addEventListener('on.config.hoverMenuDelay', function(delay) {
  4489. delay = parseFloat(delay, 10);
  4490. if (isNaN(delay)) { return; }
  4491. hoverMenuDelay = Math.abs(delay * 1000);
  4492. });
  4493.  
  4494. function showPanel(watchId, baseX, baseY, w_touch) {
  4495.  
  4496. var cn = mylistPanel.className.toString();
  4497. if (w_touch === true) {
  4498. cn = cn.replace(' w_touch', '') + ' w_touch';
  4499. } else {
  4500. if (cn.indexOf('w_touch') >= 0 && mylistPanel.style.display !== 'none') {
  4501. // フリック操作で表示したパネルが出ている間はそちらを優先し、なにもしない
  4502. return;
  4503. }
  4504. cn = cn.replace(' w_touch', '');
  4505.  
  4506. }
  4507. VideoTags.hidePopup();
  4508. if (mylistPanel.className !== cn) mylistPanel.className = cn;
  4509.  
  4510. mylistPanel.style.display = '';
  4511. mylistPanel.watchId(watchId);
  4512. mylistPanel.style.right = null;
  4513. mylistPanel.style.left = (baseX) + 'px';
  4514. mylistPanel.style.top = Math.max(baseY - mylistPanel.offsetHeight, 0, document.body.scrollTop, document.documentElement.scrollTop) + 'px';
  4515.  
  4516. if (mylistPanel.offsetLeft + mylistPanel.offsetWidth > document.body.clientWidth) {
  4517. mylistPanel.style.left = null;
  4518. mylistPanel.style.right = 0;
  4519. }
  4520.  
  4521. }
  4522.  
  4523.  
  4524. var videoReg = /(\?cc_video_id=|\?cc_id=|watch\/)([a-z0-9]+)/;
  4525. var excludeReg = /(news|live|seiga)\..*?nicovideo\.jp/;
  4526.  
  4527. function each(w, watchId) {
  4528.  
  4529. this.w_eventInit = false;
  4530.  
  4531. this.addEventListener('mouseover', function() {
  4532. var mx = 0, my = 0, self = this;
  4533. if (this.href) this.setAttribute('href', this.href.split('?')[0]);
  4534.  
  4535. self.w_mouse_in = true;
  4536. self.w_mouse_timer = null;
  4537. self.w_mouse_timer = setTimeout(function() {
  4538. self.w_mouse_timer = null;
  4539. if (!self.w_mouse_in) {
  4540. return;
  4541. }
  4542. var o;
  4543. if (w.jQuery) {
  4544. var $e = w.jQuery(self);
  4545. var t = $e.text();
  4546. o = t !== "" ? $e.offset() : $e.find('*').offset();
  4547. showPanel(watchId, o.left, o.top);
  4548. } else
  4549. if (self.getBoundingClientRect) {
  4550. o = (self.firstChild && self.firstChild.tagName === 'IMG') ? self.firstChild.getBoundingClientRect() : self.getBoundingClientRect();
  4551. var top = Math.max(w.document.documentElement.scrollTop, w.document.body.scrollTop),
  4552. left = Math.max(w.document.documentElement.scrollLeft, w.document.body.scrollLeft);
  4553. showPanel(watchId, left + o.left, top + o.top);
  4554. } else {
  4555. showPanel(watchId, mx + 8, my + 8);
  4556. }
  4557. EventDispatcher.dispatch('mylistPanelOpen', mylistPanel, self, watchId);
  4558. }, hoverMenuDelay);
  4559.  
  4560. if (!this.w_eventInit) {
  4561. this.addEventListener('mouseout', function() {
  4562. self.w_mouse_in = false;
  4563. if (self.w_mouse_timer) {
  4564. clearTimeout(self.w_mouse_timer);
  4565. self.w_mouse_timer = null;
  4566. }
  4567. }, false);
  4568. if (!w.jQuery) {
  4569. this.addEventListener('mousemove', function(ev) {
  4570. mx = ev.pageX;
  4571. my = ev.pageY;
  4572. }, false);
  4573. }
  4574. this.w_eventInit = true;
  4575. }
  4576. }, false);
  4577. this.added = 1;
  4578. }
  4579.  
  4580. function bind(force, target) {
  4581. if (!conf.enableHoverPopup) { return; }
  4582.  
  4583. var a = Array.prototype.slice.apply(document.links), vreg = videoReg, ereg = excludeReg;
  4584. for (var i = 0, len = a.length; i < len; i++) {
  4585. var e = a[i];
  4586. var m, href= e.href;
  4587. if (
  4588. href &&
  4589. !e.added &&
  4590. (m = vreg.exec(href)) !== null &&
  4591. !ereg.test(href) &&
  4592. // e.className !== "itemEcoLink" &&
  4593. e.className !== "playlistSaveLink"
  4594. ) {
  4595. each.apply(e, [w, m[2]]);
  4596. }
  4597. }
  4598. }
  4599. function bindTouch() {
  4600. TouchEventDispatcher.onflick(function(e) {
  4601. var se = e.startEvent;
  4602. if (e.direction === 'right' && (se.target.tagName === 'A' || se.target.parentElement.tagName === 'A')) {
  4603. var
  4604. a = (se.target.tagName === 'A') ? e.startEvent.target : e.startEvent.target.parentElement,
  4605. href = a.href, vreg = videoReg, ereg = excludeReg, m, watchId;
  4606. if (
  4607. href &&
  4608. (m = vreg.exec(href)) !== null &&
  4609. !ereg.test(href) &&
  4610. // e.className !== "itemEcoLink" &&
  4611. true
  4612. ) {
  4613. watchId = m[2];
  4614. var o;
  4615. if (w.jQuery) {
  4616. var $a = w.jQuery(a);
  4617. var t = $a.text();
  4618. o = t !== "" ? $a.offset() : $a.find('*').offset();
  4619. showPanel(watchId, o.left, o.top, true);
  4620. } else {
  4621. o = (a.firstChild && a.firstChild.tagName === 'IMG') ? a.firstChild.getBoundingClientRect() : a.getBoundingClientRect();
  4622. var top = Math.max(w.document.documentElement.scrollTop, w.document.body.scrollTop),
  4623. left = Math.max(w.document.documentElement.scrollLeft, w.document.body.scrollLeft);
  4624. showPanel(watchId, left + o.left, top + o.top, true);
  4625. }
  4626. }
  4627. }
  4628. });
  4629. }
  4630.  
  4631. var lastUpdate = 0, linksCount = document.links.length,
  4632. bindLoop = function(nextTime) {
  4633. var now = Date.now();
  4634. var updateInterval = w.document.hasFocus() ? 3000 : 15000;
  4635. if (now - lastUpdate < updateInterval) {
  4636. var len = document.links.length;
  4637. if (linksCount === len) {
  4638. return;
  4639. }
  4640. linksCount = len;
  4641. }
  4642. bind();
  4643. lastUpdate = now;
  4644. };
  4645.  
  4646. var self = {
  4647. hidePopup: function() {
  4648. VideoTags.hidePopup();
  4649. mylistPanel.hide();
  4650. return this;
  4651. },
  4652. updateNow: function() {
  4653. bind();
  4654. lastUpdate -= 1500;
  4655. return this;
  4656. }
  4657. };
  4658.  
  4659.  
  4660. if (location.host === "ext.nicovideo.jp") {
  4661. bind();
  4662. } else {
  4663. var thumbnailReg = /\.smilevideo\.jp\/smile\?i=(\d+)/;
  4664. if (location.host === 'ch.nicovideo.jp' && w.jQuery) {
  4665. w.jQuery('.lazyimage, .thumb_video.thumb_114.wide img, .itemset li .image a .item').each(function() {
  4666. var $e = w.jQuery(this).text(' ');
  4667. var src = $e.attr('data-original') || $e.attr('src');
  4668. if (typeof src === 'string' && thumbnailReg.test(src)) {
  4669. each.apply(this, [w, 'so' + RegExp.$1]);
  4670. }
  4671. });
  4672. }
  4673. bindTouch();
  4674. bind();
  4675. setInterval(bindLoop, 500);
  4676. }
  4677. return self;
  4678. })(w, conf, EventDispatcher);
  4679. window.WatchItLater.popup = AnchorHoverPopup;
  4680.  
  4681.  
  4682. //===================================================
  4683. //===================================================
  4684. //===================================================
  4685.  
  4686.  
  4687. /**
  4688. * マイリスト登録のポップアップウィンドウを乗っ取る処理
  4689. *
  4690. * iframeの子ウィンドウ内に開かれた時に実行される
  4691. * クロスドメインを越えられない環境ではこっちを使うしかない
  4692. */
  4693. (function(){ // mylist window
  4694. if (w.location.href.indexOf('/mylist_add/') < 0 || w.name === 'nicomylistadd') return;
  4695.  
  4696. var $ = w.jQuery;
  4697. $('body,table,img,td').css({border:0, margin:0, padding:0, background: "transparent", overflow: 'hidden'});
  4698. $('#main_frm').css({background: '#fff', paddig: 0, borderRadius: 0}).addClass('mylistPopupPanel');
  4699.  
  4700. if ($('#edit_description').length < 1) {
  4701. $('#main_frm .font12:first').css({position: 'absolute', margin: 0, top: 0, left: 0, padding: 0, color: 'red', fontSize: '8pt'});
  4702. // ログインしてないぽい
  4703. return;
  4704. }
  4705.  
  4706. $('#box_success').css({position: 'absolute', top: 0, left: 0});
  4707. $('#box_success h1').css({color: 'black', fontSize: '8pt', padding: 0});
  4708. $('td').css({padding: 0});
  4709.  
  4710. // 「マイリストに登録しました」
  4711. // $('.mb8p4:last').show();
  4712. // $('.mb8p4:last h1').css({fontSize : "8pt"});
  4713.  
  4714. $('table:first').css({width: '200px'});
  4715. $('table:first td.main_frm_bg').css({height: '20px'});
  4716. $('table:first table:first').hide();
  4717.  
  4718. $('select')
  4719. .css({width: '64px',position: 'absolute', top:0, left:0, margin: 0})
  4720. .addClass('mylistSelect');
  4721. $('select')[0].selectedIndex = $('select')[0].options.length - 1;
  4722. $('#select_group option:last')[0].innerHTML = 'とりマイ';
  4723.  
  4724. // var submit = document.createElement("input");
  4725. // submit.className = 'mylistAdd';
  4726. // submit.type = "submit";
  4727. // submit.value = "マ";
  4728. // $(submit).css({position: 'absolute', top: 0, left: '64px'});
  4729. // $('select')[0].parentNode.appendChild(submit);
  4730.  
  4731.  
  4732. $('#edit_description').hide();
  4733.  
  4734. w.document.documentElement.scrollTop = 0;
  4735. w.document.documentElement.scrollLeft = 0;
  4736.  
  4737.  
  4738. $($.browser.safari ? 'body' : 'html').scrollTop(0);
  4739.  
  4740. w.window.close = function()
  4741. {
  4742. return;
  4743. };
  4744. w.window.alert = function()
  4745. {
  4746. w.document.write('<span style="position:absolute;top:0;left:0;font-size:8pt;color:red;">' +
  4747. arguments[0] + '</span>');
  4748. };
  4749. })();
  4750.  
  4751.  
  4752. //===================================================
  4753. //===================================================
  4754. //===================================================
  4755.  
  4756. window.WatchItLater.loader.videoArrayAPILoader = (function() {
  4757. var sessionId = 0;
  4758. var deferredList = {};
  4759. var cacheData = {};
  4760. var currentRequestIds = '', currentPromise;
  4761. var loaderFrame, loaderWindow;
  4762. var BASE_URL = 'http://i.nicovideo.jp/v3/video.array?v=';
  4763.  
  4764. //WatchItLater.loader.videoArrayAPILoader.load('sm9').then(function(info) { console.log(info); });
  4765.  
  4766. var load = function(watchId) {
  4767. var ids = [], result = {}, def = new $.Deferred(), timeoutTimer = null;
  4768.  
  4769. initialize();
  4770.  
  4771. $(typeof watchId !== 'string' ? watchId : [watchId]).each(function(i, id) {
  4772. if (cacheData[id]) {
  4773. result[id] = cacheData[id];
  4774. } else {
  4775. ids.push(id);
  4776. }
  4777. });
  4778. ids = window._.unique(ids);
  4779. if (ids.length < 1) {
  4780. window.setTimeout(function() { def.resolve(result); }, 0);
  4781. return def;
  4782. }
  4783. var _ids = JSON.stringify(ids);
  4784. var onSuccess = function(infoList) {
  4785. $(ids).each(function(i, id) {
  4786. result[id] = result[id] || infoList[id] || cacheData[id];
  4787. });
  4788.  
  4789. window.clearTimeout(timeoutTimer);
  4790. currentRequestIds = ''; currentPromise = null;
  4791. def.resolve(result);
  4792. def = null;
  4793. };
  4794. var onFail = function() {
  4795. window.clearTimeout(timeoutTimer);
  4796. console.log('%cVideoArrayAPILoader.onFail', 'color: red;');
  4797. currentRequestIds = ''; currentPromise = null;
  4798. if (def) {
  4799. def.reject();
  4800. }
  4801. def = null;
  4802. };
  4803.  
  4804. sessionId++;
  4805.  
  4806. timeoutTimer = window.setTimeout(onFail, 30 * 1000);
  4807.  
  4808. if (_ids === currentRequestIds) {
  4809. currentPromise.then(onSuccess, onFail);
  4810. return def;
  4811. }
  4812.  
  4813. currentRequestIds = _ids;
  4814. sendRequest(ids, sessionId).then(onSuccess, onFail);
  4815.  
  4816. return def.promise();
  4817. };
  4818.  
  4819. var sendRequest = function(ids, sessionId) {
  4820. var def = new $.Deferred();
  4821. currentPromise = def;
  4822. deferredList[sessionId] = def;
  4823. loaderWindow.location.replace(BASE_URL + ids.join(',') + '#' + sessionId);
  4824. return def.promise();
  4825. };
  4826.  
  4827. var parseVideoArray = function(xml) {
  4828. var $xml = $(xml), infoList = {};
  4829. $xml.find('video_info').each(function() {
  4830. var info = new parseVideoInfo($(this));
  4831. infoList[info.id] = cacheData[info.id] = cacheData[info.default_thread] = info;
  4832. if (info.threadIds && info.threadIds.length > 1) {
  4833. $(info.threadIds).each(function(i, threadId) {
  4834. infoList[threadId] = cacheData[threadId] = info;
  4835. });
  4836. }
  4837. });
  4838. return infoList;
  4839. };
  4840.  
  4841. var parseVideoInfo = function($xml) {
  4842. var info = {threadIds: []};
  4843. var elements = [
  4844. 'id', 'user_id', 'title', 'description', 'length_in_seconds',
  4845. 'thumbnail_url', 'first_retrieve', 'default_thread',
  4846. 'view_counter', 'mylist_counter'];
  4847.  
  4848. $(elements).each(function(i, elm) {
  4849. info[elm] = $xml.find(elm + ':first').text();
  4850. });
  4851.  
  4852. info['num_res'] = $xml.find('thread:first num_res').text();
  4853.  
  4854. var duration = parseInt(info['length_in_seconds'], 10);
  4855. info['length'] = parseInt(duration / 60, 10) + ':' + (100 + (duration % 60)).toString().substr(1);
  4856.  
  4857. info['first_retrieve_t'] = info['first_retrieve'];
  4858. info['first_retrieve'] =
  4859. window.WatchApp.ns.util.DateFormat.strftime(
  4860. '%Y-%m-%d %H:%M:%S',
  4861. new Date(info['first_retrieve'])
  4862. );
  4863.  
  4864. $xml.find('thread id, channel_thread id').each(function() {
  4865. info.threadIds.push(this.innerHTML);
  4866. });
  4867. return info;
  4868. };
  4869.  
  4870.  
  4871. var initialize = function() {
  4872. initialize = window._.noop;
  4873.  
  4874. loaderFrame = document.createElement('iframe');
  4875. loaderFrame.name = 'watchItLaterAPILoader';
  4876. loaderFrame.className = 'watchItLaterAPILoaderFrame';
  4877. document.body.appendChild(loaderFrame);
  4878.  
  4879. loaderWindow = loaderFrame.contentWindow;
  4880.  
  4881. EventDispatcher.addEventListener('onMessage', function(data, type) {
  4882. if (type !== 'VideoArrayAPILoader') { return; }
  4883. try {
  4884. var session = data.session, xml = data.xml;
  4885. //console.log('VideoArrayAPILoader.onMessage', data.session, data.xml);
  4886. if (deferredList[session]) {
  4887. deferredList[session].resolve(parseVideoArray(xml));
  4888. delete deferredList[session];
  4889. currentPromise = null;
  4890. }
  4891. } catch (e) {
  4892. console.log('message parse error', e);
  4893. if (deferredList[session]) {
  4894. deferredList[session].reject();
  4895. delete deferredList[session];
  4896. currentPromise = null;
  4897. }
  4898. }
  4899. });
  4900.  
  4901. };
  4902.  
  4903. // sample URL
  4904. // http://i.nicovideo.jp/v3/video.array?v=sm9,sm3393520,sm5909863,so23023492,1394173596
  4905. //initialize();
  4906. return {
  4907. load: load
  4908. };
  4909. })();
  4910.  
  4911.  
  4912.  
  4913.  
  4914. //===================================================
  4915. //===================================================
  4916. //===================================================
  4917.  
  4918. var _watchController = function(w) {
  4919. var WatchApp = w.WatchApp, _false = function() { return false; };
  4920. var
  4921. watch = (WatchApp && WatchApp.ns.init) || {},
  4922. watchInfoModel = (watch.CommonModelInitializer && WatchApp.ns.model.WatchInfoModel.getInstance()) || {},
  4923. nicoPlayer = (watch.PlayerInitializer && watch.PlayerInitializer.nicoPlayerConnector) || {},
  4924. videoExplorerController = watch.VideoExplorerInitializer.videoExplorerController,
  4925. videoExplorer = videoExplorerController.getVideoExplorer(),
  4926. videoExplorerContentType = WatchApp.ns.components.videoexplorer.model.ContentType,
  4927. $ = w.$, WatchJsApi = w.WatchJsApi;
  4928. return {
  4929. isZeroWatch: function() {
  4930. return (window.PlayerApp) ? true : false;
  4931. },
  4932. getWatchInfoModel: function() {
  4933. return watchInfoModel;
  4934. },
  4935. nicoSearch: function(word, search_type) {
  4936. if (!search_type) {
  4937. try {
  4938. var type = videoExplorerContentType.SEARCH;
  4939. search_type = videoExplorer.getContentList().getContent(type).getSearchType();
  4940. } catch(e) {
  4941. search_type = search_type || 'tag';
  4942. }
  4943. }
  4944. videoExplorerController.searchVideo(word, search_type);
  4945. AnchorHoverPopup.hidePopup();
  4946. },
  4947. showMylist: function(mylistId) {
  4948. videoExplorerController.showMylist(mylistId.toString());
  4949. },
  4950. clearDeflistCache: function() {
  4951. watch.VideoExplorerInitializer.deflistVideoAPILoader._cache.clear();
  4952. },
  4953. clearMylistCache: function(id) {
  4954. if (id) {
  4955. watch.VideoExplorerInitializer.mylistVideoAPILoader._cache.deleteElement(
  4956. 'http://riapi.nicovideo.jp/api/watch/mylistvideo?id=' + id.toString()
  4957. );
  4958. } else {
  4959. watch.VideoExplorerInitializer.mylistVideoAPILoader._cache.clear();
  4960. }
  4961. },
  4962. showDeflist: function() {
  4963. videoExplorerController.showDeflist();
  4964. },
  4965. changeScreenMode: function(mode) {
  4966. WatchJsApi.player.changePlayerScreenMode(mode);
  4967. setTimeout(function(){$(window).resize();}, 3000);
  4968. },
  4969. isFixedHeader: function() {
  4970. return !$('body').hasClass('nofix');
  4971. },
  4972. // ヘッダー追従かどうかを考慮したscrollTop
  4973. scrollTop: function(top, dur) {
  4974. var header = (this.isFixedHeader() ? $("#siteHeader").outerHeight() : 0);
  4975.  
  4976. if (top !== undefined) {
  4977. return $(window).scrollTop(top - header, dur);
  4978. } else {
  4979. return $(window).scrollTop() + header;
  4980. }
  4981. },
  4982. scrollToVideoPlayer: function(force) {
  4983. // 縦解像度がタグ+プレイヤーより大きいならタグの開始位置、そうでないならプレイヤーの位置にスクロール
  4984. // ただし、該当部分が画面内に納まっている場合は、勝手にスクロールするとかえってうざいのでなにもしない
  4985. var $body = $('body'), isContentFix = $body.hasClass('content-fix');
  4986. $body.addClass('w_noHover').removeClass('content-fix');
  4987. var h = $('#playerContainer').outerHeight() + $('#videoTagContainer').outerHeight();
  4988. var top = $(window).height() >= h ? '#videoTagContainer, #playerContainer' : '#playerContainer';
  4989.  
  4990.  
  4991. if (force) {
  4992. // 要素が画面内に納まっている場合でも、その要素の位置までスクロール
  4993. WatchApp.ns.util.WindowUtil.scrollFit(top, 600);
  4994. } else {
  4995. // 要素が画面内に収まっている場合はスクロールしない
  4996. WatchApp.ns.util.WindowUtil.scrollFitMinimum(top, 600);
  4997. }
  4998. $(window).scrollLeft(0);
  4999. $body.toggleClass('content-fix', isContentFix);
  5000. setTimeout(function() {
  5001. $body.removeClass('w_noHover');
  5002. }, 800);
  5003. },
  5004. play: function() {
  5005. nicoPlayer.playVideo();
  5006. },
  5007. pause: function() {
  5008. nicoPlayer.stopVideo();
  5009. },
  5010. togglePlay: function() {
  5011. var status = $("#external_nicoplayer")[0].ext_getStatus();
  5012. if (status === 'playing') {
  5013. this.pause();
  5014. } else {
  5015. this.play();
  5016. }
  5017. },
  5018. isPlaying: function() {
  5019. var status = $("#external_nicoplayer")[0].ext_getStatus();
  5020. return status === 'playing';
  5021. },
  5022. nextVideo: function() {
  5023. return nicoPlayer.playNextVideo();
  5024. },
  5025. prevVideo: function() {
  5026. return nicoPlayer.playPreviousVideo();
  5027. },
  5028. vpos: function(v) {
  5029. if (typeof v === 'number') {
  5030. return nicoPlayer.seekVideo(v);
  5031. } else {
  5032. return nicoPlayer.getVpos();
  5033. }
  5034. },
  5035. openSearch: function() {
  5036. WatchApp.ns.init.VideoExplorerInitializer.expandButtonView.open();
  5037. // videoExplorer.openByCurrentCondition();
  5038. // videoExplorer.changeState(true);
  5039. },
  5040. closeSearch: function() {
  5041. videoExplorer.changeState(false);
  5042. videoExplorer.close();
  5043. },
  5044. openVideoOwnersVideo: function() {
  5045. if (this.isChannelVideo()) {
  5046. this.openChannelOwnersVideo();
  5047. } else {
  5048. this.openUpNushiVideo();
  5049. }
  5050. },
  5051. openUpNushiVideo: function() {
  5052. videoExplorerController.showOwnerVideo();
  5053. },
  5054. openChannelOwnersVideo: function() {
  5055. videoExplorerController.showMylist('-3');
  5056. },
  5057. openUserVideo: function(userId, userNick) {
  5058. videoExplorerController.showOtherUserVideos(userId, userNick);
  5059. },
  5060. openRecommend: function() {
  5061. var
  5062. type = videoExplorerContentType.RELATED_VIDEO,
  5063. open = function() {
  5064. var rel = WatchApp.ns.init.VideoExplorerInitializer.videoExplorer._menu.getItemByContentType(type);
  5065. rel.select();
  5066. };
  5067. if (videoExplorer.isOpen()) {
  5068. open();
  5069. } else {
  5070. this.openSearch();
  5071. setTimeout(open, 500);
  5072. }
  5073. },
  5074. getVideoExplorerCurrentItems: function(format) {
  5075. var ac = videoExplorer._contentList.getActiveContent();
  5076. if (!ac || !ac.getItems) return [];
  5077. var items = ac.getItems();
  5078.  
  5079. if (!format) {
  5080. return items;
  5081. } else
  5082. if (format === 'playlist') {
  5083. var result = [];
  5084. for (var i = items.length - 1; i >= 0; i--) {
  5085. result.unshift(
  5086. videoExplorerController._item2playlistItem(items[i])
  5087. );
  5088. }
  5089. return result;
  5090. }
  5091. },
  5092. getWatchId: function() {// スレッドIDだったりsmXXXXだったり
  5093. return watchInfoModel.v;
  5094. },
  5095. getVideoId: function() {// smXXXXXX, soXXXXX など
  5096. return watchInfoModel.id;
  5097. },
  5098. getMyNick: function() {
  5099. return watch.CommonModelInitializer.viewerInfoModel.nickname;
  5100. },
  5101. getMyUserId: function() {
  5102. return watch.CommonModelInitializer.viewerInfoModel.userId;
  5103. },
  5104. getPlaylistItems: function() {
  5105. return watch.PlaylistInitializer.playlist.items || watch.PlaylistInitializer.playlist.currentItems;
  5106. },
  5107. setPlaylistItems: function(items, currentItem) {
  5108. var playlist = watch.PlaylistInitializer.playlist;
  5109. // var isAutoPlay = playlist.isContinuous();//.isAutoPlay();
  5110. playlist.reset(
  5111. items,
  5112. 'WatchItLater',
  5113. playlist.type,
  5114. playlist.option
  5115. );
  5116. if (currentItem) { playlist.playingItem = currentItem; }
  5117. else { playlist.playingItem = items[0]; }
  5118. // if (!isAutoPlay) { // 本家側の更新でリセット時に勝手に自動再生がONになるようになったので、リセット前の状態を復元する
  5119. // playlist.disableContinuous();
  5120. // }
  5121. },
  5122. shufflePlaylist: function(target) {
  5123. var x = this.getPlaylistItems(), items = [], i, currentIndex = -1, currentItem = null;
  5124. if (target === 'right') {
  5125. for (i = 0; i < x.length;) {
  5126. if (x[0]._isPlaying) {
  5127. currentIndex = i;
  5128. currentItem = x.shift();
  5129. items.push(currentItem);
  5130. break;
  5131. } else {
  5132. items.push(x.shift());
  5133. }
  5134. }
  5135. }
  5136.  
  5137. x = x.map(function(a){return {weight:Math.random(), value:a};})
  5138. .sort(function(a, b){return a.weight - b.weight;})
  5139. .map(function(a){return a.value;});
  5140. for (i = 0; i < x.length; i++) {
  5141. if (x[i]._isPlaying) {
  5142. items.unshift(x[i]);
  5143. } else {
  5144. items.push(x[i]);
  5145. }
  5146. }
  5147. var pm = WatchApp.ns.view.playlist.PlaylistManager, pv = watch.PlaylistInitializer.playlistView;
  5148. var left = pm.getLeftSideIndex();
  5149. this.setPlaylistItems(items, currentItem);
  5150. pv.scroll(left);
  5151. },
  5152. clearPlaylist: function(target) {
  5153. var x = this.getPlaylistItems(), items = [], i, currentItem = null;
  5154. if (target === 'left') {
  5155. for (i = x.length - 1; i >= 0; i--) {
  5156. items.unshift(x[i]);
  5157. if (x[i]._isPlaying) {
  5158. currentItem = x[i];
  5159. break;
  5160. }
  5161. }
  5162. } else
  5163. if (target === 'right') {
  5164. for (i = 0; i < x.length ; i++) {
  5165. items.push(x[i]);
  5166. if (x[i]._isPlaying) {
  5167. currentItem = x[i];
  5168. break;
  5169. }
  5170. }
  5171. }
  5172. else {
  5173. for (i = 0; i < x.length; i++) {
  5174. if (x[i]._isPlaying) {
  5175. currentItem = x[i];
  5176. items.unshift(x[i]);
  5177. }
  5178. }
  5179. }
  5180. this.setPlaylistItems(items, currentItem);
  5181. },
  5182. appendSearchResultToPlaylist: function(mode) {
  5183. var
  5184. items = this.getPlaylistItems(),
  5185. searchItems = this.getVideoExplorerCurrentItems('playlist'),
  5186. uniq = {}, i, playingIndex = 0, c, len, currentItem = null;
  5187. if (!searchItems || searchItems.length < 1) {
  5188. return;
  5189. }
  5190. for (i = 0, len = items.length; i < len; i++) {
  5191. uniq[items[i].id] = true;
  5192. if (items[i]._isPlaying) { playingIndex = i; currentItem = items[i]; }
  5193. }
  5194. if (mode === 'next') {
  5195. for (i = searchItems.length - 1; i >= 0; i--) {
  5196. c = searchItems[i];
  5197. ("undefined" === typeof c.type || "video" === c.type) && uniq[c.id] === undefined && items.splice(playingIndex + 1, 0, c);
  5198. }
  5199. } else {
  5200. for (i = 0, len = searchItems.length; i < len; i++) {
  5201. c = searchItems[i];
  5202. ("undefined" === typeof c.type || "video" === c.type) && uniq[c.id] === undefined && items.push(c);
  5203. }
  5204. }
  5205. this.setPlaylistItems(items, currentItem);
  5206. },
  5207. insertVideoToPlaylist: function(id) {
  5208. WatchItLater.VideoInfoLoader.load(id).then(function(info) {
  5209. var item = new WatchApp.ns.model.playlist.PlaylistItem(info);
  5210. watch.PlaylistInitializer.playlist.insertNextPlayingItem(item);
  5211. }, function(err) {
  5212. Popup.alert(err.message);
  5213. });
  5214. },
  5215. addDefMylist: function(description) {
  5216. var watchId = watchInfoModel.id;
  5217. setTimeout(function() {
  5218. Mylist.addDefListItem(watchId, function(status, result, replaced) {
  5219. Mylist.reloadDefList();
  5220. if (status !== "ok") {
  5221. Popup.alert('とりあえずマイリストの登録に失敗: ' + result.error.description);
  5222. } else {
  5223. var torimai = '<a href="/my/mylist">とりあえずマイリスト</a>';
  5224. Popup.show(
  5225. torimai +
  5226. (replaced ? 'の先頭に移動しました' : 'に登録しました')
  5227. );
  5228. }
  5229. }, description);
  5230. }, 0);
  5231. },
  5232. commentVisibility: function(v) {
  5233. if (v === 'toggle') {
  5234. return this.commentVisibility(!this.commentVisibility());
  5235. } else
  5236. if (typeof v === 'boolean') {
  5237. nicoPlayer.playerConfig.set({commentVisible: v});
  5238. return this;
  5239. } else {
  5240. var pc = nicoPlayer.playerConfig.get();
  5241. return pc.commentVisible;
  5242. }
  5243. },
  5244. deepenedComment: function(v) {
  5245. if (v === 'toggle') {
  5246. return this.deepenedComment(!this.deepenedComment());
  5247. } else
  5248. if (typeof v === 'boolean') {
  5249. nicoPlayer.playerConfig.set({deepenedComment: v});
  5250. return this;
  5251. } else {
  5252. var pc = nicoPlayer.playerConfig.get();
  5253. return pc.deepenedComment;
  5254. }
  5255. },
  5256. allowStageVideo: function(v) {
  5257. if (v === 'toggle') {
  5258. return this.allowStageVideo(!this.allowStageVideo());
  5259. } else
  5260. if (typeof v === 'boolean') {
  5261. nicoPlayer.playerConfig.set({allowStageVideo: v});
  5262. return this;
  5263. } else {
  5264. var pc = nicoPlayer.playerConfig.get();
  5265. return pc.allowStageVideo;
  5266. }
  5267. },
  5268. isStageVideoSupported: function() {
  5269. try {
  5270. var exp = w.document.getElementById('external_nicoplayer');
  5271. return exp.isStageVideoSupported();
  5272. } catch(e) {
  5273. console.log(e);
  5274. return false;
  5275. }
  5276. },
  5277. isStageVideoAvailable: function() {
  5278. try {
  5279. var exp = w.document.getElementById('external_nicoplayer');
  5280. return exp.isStageVideoAvailable();
  5281. } catch(e) {
  5282. console.log(e);
  5283. return false;
  5284. }
  5285. },
  5286. toggleStageVideo: function() {
  5287. if (!this.isStageVideoSupported()) {
  5288. Popup.alert('ハードウェアアクセラレーションを使用できない状態か、未対応の環境です');
  5289. return;
  5290. }
  5291. var isAllowed = this.allowStageVideo(), exp = $('#external_nicoplayer')[0];
  5292. exp.setIsForceUsingStageVideo(!isAllowed && conf.forceEnableStageVideo);
  5293. this.allowStageVideo(!isAllowed);
  5294. setTimeout($.proxy(function() {
  5295. isAllowed = this.allowStageVideo();
  5296. var isAvailable = this.isStageVideoAvailable();
  5297. Popup.show('ハードウェアアクセラレーション:' +
  5298. (isAllowed ? '設定ON' : '設定OFF') + ' / ' +
  5299. (isAvailable ? '使用可能' : '使用不能')
  5300. );
  5301. }, this), 100);
  5302. },
  5303. mute: function(v) {
  5304. var exp = w.document.getElementById('external_nicoplayer');
  5305.  
  5306. if (v === 'toggle') {
  5307. return this.mute(!this.mute());
  5308. } else
  5309. if (typeof v === 'boolean') {
  5310. exp.ext_setMute(v);
  5311. return this;
  5312. } else {
  5313. return exp.ext_isMute();
  5314. }
  5315. },
  5316. volume: function(v) {
  5317. var exp = w.document.getElementById('external_nicoplayer');
  5318. if (typeof v === 'string' && v.match(/^[+-]\d+$/)) {
  5319. this.volume(this.volume() + v * 1);
  5320. } else
  5321. if (typeof v === 'number' || (typeof v === 'string' && v.match(/^\d+$/))) {
  5322. exp.ext_setVolume(Math.max(0, Math.min(v * 1, 100)));
  5323. }
  5324. return exp.ext_getVolume();
  5325. },
  5326. isWide: function() {
  5327. var exp = w.document.getElementById('external_nicoplayer');
  5328. return exp.ext_isWide();
  5329. },
  5330. isPlaylistActive: function() {
  5331. return watch.PlaylistInitializer.playlist.getPlaybackMode() !== 'normal';
  5332. },
  5333. isPlaylistRandom: function() {
  5334. return watch.PlaylistInitializer.playlist.isShuffle();
  5335. },
  5336. isPlaylistContinuous: function() {
  5337. return watch.PlaylistInitializer.playlist.getPlaybackMode() === 'continuous';
  5338. },
  5339. getOwnerIcon: function() {
  5340. try {
  5341. return this.isChannelVideo() ? watchInfoModel.channelInfo.iconUrl : watchInfoModel.uploaderInfo.iconUrl;
  5342. } catch (e) {
  5343. return 'http://uni.res.nimg.jp/img/user/thumb/blank_s.jpg';
  5344. }
  5345. },
  5346. getOwnerName: function() {
  5347. try {
  5348. return this.isChannelVideo() ? watchInfoModel.channelInfo.name : watchInfoModel.uploaderInfo.nickname;
  5349. } catch (e) {
  5350. return '';
  5351. }
  5352. },
  5353. getOwnerId: function() {
  5354. try {
  5355. return this.isChannelVideo() ? watchInfoModel.channelInfo.id : watchInfoModel.uploaderInfo.id;
  5356. } catch (e) {
  5357. return '0';
  5358. }
  5359. },
  5360. getOwnerType: function() {
  5361. try {
  5362. return this.isChannelVideo() ? 'channel' : 'user';
  5363. } catch (e) {
  5364. return 'channel';
  5365. }
  5366. },
  5367. getOwnerPage: function() {
  5368. try {
  5369. if (this.isChannelVideo()) {
  5370. return $('#ch_prof').find('.symbol').attr('href');
  5371. } else {
  5372. return '/user/' + this.getOwnerId();
  5373. }
  5374. } catch (e) {
  5375. return '';
  5376. }
  5377. },
  5378. isFavoriteOwner: function() {
  5379. try {
  5380. return this.isChannelVideo() ?
  5381. !!(watchInfoModel.channelInfo && watchInfoModel.channelInfo.isFavorited) :
  5382. watchInfoModel.uploaderInfo.isFavorited;
  5383. } catch (e) {
  5384. return false;
  5385. }
  5386. },
  5387. isVideoPublic: function() { // 投稿動画一覧を公開しているか? 公開マイリストがあるかどうかとは別なのでややこしい
  5388. return this.isChannelVideo() ? true : watchInfoModel.uploaderInfo.isUserVideoPublic;
  5389. },
  5390. isChannelVideo: function() {
  5391. return watchInfoModel.isChannelVideo();
  5392. },
  5393. getOwnerInfo: function() {
  5394. return {
  5395. type: this.getOwnerType(),
  5396. name: this.getOwnerName(),
  5397. icon: this.getOwnerIcon(),
  5398. id: this.getOwnerId(),
  5399. page: this.getOwnerPage(),
  5400. isFavorite: this.isFavoriteOwner(),
  5401. isVideoPublic: this.isVideoPublic()
  5402. };
  5403. },
  5404. isSearchMode: function() {
  5405. return videoExplorer.isOpen(); ////return $('body').hasClass('videoExplorer');
  5406. },
  5407. isFullScreen: function() {
  5408. return $('body').hasClass('full_with_browser');
  5409. },
  5410. // フルスクリーンの時にタグとかプレイリストを表示する設定かどうか
  5411. isFullScreenContentAll: function() {
  5412. try {
  5413. var content = localStorage.BROWSER_FULL_OPTIONS;
  5414. if (typeof content !== 'string') return false;
  5415. var isAll = JSON.parse(content).content === 'all';
  5416. return isAll;
  5417. } catch(e) {
  5418. console.log('%cexception', 'background: red; color: white;', e);
  5419. return false;
  5420. }
  5421. },
  5422. isIchibaEmpty: function() {
  5423. return $('#ichibaMain') .find('.ichiba_mainitem').length < 1;
  5424. },
  5425. postComment: function(comment, command) {
  5426. comment = $.trim(comment);
  5427. if (comment.length <= 0) { return; }
  5428. if (!command) { command = ''; }
  5429.  
  5430. setTimeout(function() {
  5431. try {
  5432. var exp = w.document.getElementById('external_nicoplayer');
  5433. console.log('postComment: ', [comment, command]);
  5434. if (!exp.externalPostChat(comment, command)) {
  5435. Popup.alert('コメント投稿に失敗しました');
  5436. }
  5437. } catch(e) {
  5438. Popup.alert('コメント投稿に失敗しました');
  5439. }
  5440. }, 0);
  5441. },
  5442. // スレッドIDから動画IDに変換。出来なかった時はそのまま返す
  5443. getTid2Vid: function(watchId, callback) {
  5444. if (!watchId.match(/^[0-9]+$/)) {
  5445. return callback(watchId);
  5446. }
  5447. WatchItLater.VideoInfoLoader.load(watchId).then(function(info) {
  5448. callback(info.id);
  5449. },
  5450. function() {
  5451. callback(watchId);
  5452. });
  5453. }
  5454. };
  5455. }; // end _watchController
  5456.  
  5457. (function() {
  5458. window.WatchItLater.WatchController =
  5459. window.WatchController = {
  5460. isZeroWatch: function() { return window.PlayerApp ? true : false; },
  5461. isFullScreen: function() { return false; },
  5462. isSearchMode: function() { return false; },
  5463. getTid2Vid: function(threadId, callback) { return callback(threadId);}
  5464. };
  5465. })();
  5466.  
  5467.  
  5468.  
  5469. var Util = (function() {
  5470. var Cache = {
  5471. storage: {},
  5472. get: function(key) {
  5473. if (!this.storage[key]) {
  5474. console.log('no cache');
  5475. return false;
  5476. } else
  5477. if (this.storage[key].cachedUntil <= Date.now()){
  5478. console.log('cache timeout');
  5479. delete this.storage[key];
  5480. return false;
  5481. } else {
  5482. console.log('cache exist');
  5483. return this.storage[key].data;
  5484. }
  5485. },
  5486. set: function(key, data, cacheTimeMs) {
  5487. cacheTimeMs = cacheTimeMs || 1000 * 60 * 10;
  5488. console.log('set cache', key, cacheTimeMs);
  5489. this.storage[key] = {
  5490. data: data,
  5491. cachedUntil: Date.now() + cacheTimeMs
  5492. };
  5493. return data;
  5494. }
  5495. };
  5496. var Browser = {
  5497. isWebkit: function() {
  5498. return navigator.userAgent.toLowerCase().indexOf('webkit') >= 0;
  5499. },
  5500. isFx: function() {
  5501. return navigator.userAgent.toLowerCase().indexOf('firefox') >= 0;
  5502. }
  5503. };
  5504. var
  5505. isMetaKey = function(e) {
  5506. if (e.button !== 0 || e.metaKey || e.shiftKey || e.altKey || e.ctrlKey) { return true; }
  5507. return false;
  5508. },
  5509. prevent = function(e) {
  5510. e.preventDefault(); e.stopPropagation();
  5511. },
  5512. scrollToVideoExplorer = function() {
  5513. if (!WatchController.isSearchMode()) { return; }
  5514. window.setTimeout(function() {
  5515. window.WatchApp.ns.util.WindowUtil.scrollFit($('#videoExplorer'));
  5516. }, 100);
  5517. };
  5518.  
  5519. var Closure = {
  5520. outScope: function(func) {
  5521. return new Function([
  5522. '(' + func.toString() + ').apply(this, arguments);'
  5523. ].join(''));
  5524. },
  5525. openVideoOwnersVideo: function() {
  5526. return function(e) {
  5527. if (isMetaKey(e)) { return; }
  5528. prevent(e);
  5529. WatchController.openVideoOwnersVideo();
  5530. scrollToVideoExplorer();
  5531. };
  5532. },
  5533. openVideoOwnersNicorepo: function() {
  5534. return function(e) {
  5535. if (isMetaKey(e)) { return; }
  5536. if (conf.disableVideoExplorer && !WatchController.isSearchMode()) {
  5537. return;
  5538. }
  5539. prevent(e);
  5540. //WatchController.showMylist(NicorepoVideo.REPO_OWNER);
  5541. WatchController.showMylist('repo-owner-' + WatchController.getOwnerId());
  5542. scrollToVideoExplorer();
  5543. };
  5544. },
  5545. openDefMylist: function() {
  5546. return function(e) {
  5547. if (isMetaKey(e)) { return; }
  5548. prevent(e);
  5549. WatchController.showDeflist();
  5550. scrollToVideoExplorer();
  5551. };
  5552. },
  5553. openMylist: function(id) {
  5554. return function(e) {
  5555. if (isMetaKey(e)) { return; }
  5556. prevent(e);
  5557. WatchController.showMylist(id);
  5558. scrollToVideoExplorer();
  5559. };
  5560. },
  5561. openNicoSearch: function(word, type) {
  5562. return function(e) {
  5563. if (isMetaKey(e)) { return; }
  5564. if (WatchController.isZeroWatch()) {
  5565. prevent(e);
  5566. WatchController.nicoSearch(word, type);
  5567. scrollToVideoExplorer();
  5568. }
  5569. };
  5570. },
  5571. seekVideo: function(vpos) {
  5572. return function(e) {
  5573. if (isMetaKey(e)) { return; }
  5574. prevent(e);
  5575. WatchController.vpos(vpos);
  5576. };
  5577. },
  5578. showLargeThumbnail: function(url) {
  5579. return function() {
  5580. WatchController.showLargeThumbnail(url);
  5581. };
  5582. },
  5583. commentPanelContextMenu: function() {
  5584. return function(a) {
  5585. a.preventDefault(); a.stopPropagation();
  5586. var c = this.commentListModel.getComment(this.parseResNo(jQuery(a.currentTarget)));
  5587. var WatchApp = null, WatchController = null;
  5588. if (c) {
  5589. var
  5590. $d = this.CommentContextMenu.$contextMenuContainer,
  5591. $e = jQuery("#playerCommentPanel"),
  5592. left = this.$commentTableHeaderOuter.position().left,
  5593. top = a.pageY - $e.offset().top,
  5594. f = Math.min($e.offset().top + $e.outerHeight(), jQuery(window).scrollTop() + jQuery(window).outerHeight());
  5595. if (f < a.pageY + $d.outerHeight()) top -= a.pageY + $d.outerHeight() - f;
  5596. this.CommentContextMenu.show(c, left, top);
  5597. }
  5598. };
  5599. }
  5600. };
  5601. var Deferred = {
  5602. wait: function(msec) {
  5603. return function() {
  5604. var args = Array.prototype.slice.call(arguments, 0);
  5605. var d = new $.Deferred();
  5606. setTimeout(function() {
  5607. d.resolve.apply(d, args);
  5608. }, msec);
  5609. return d.promise();
  5610. };
  5611. }
  5612. };
  5613.  
  5614.  
  5615. var self = {
  5616. Cache: Cache,
  5617. Closure: Closure,
  5618. Deferred: Deferred,
  5619. Browser: Browser,
  5620. here: function(func) { // えせヒアドキュメント
  5621. return func.toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1].replace(/\{\*/g, '/*').replace(/\*\}/g, '*/');
  5622. }
  5623. };
  5624. return self;
  5625. })();
  5626. window.WatchItLater.util = Util;
  5627.  
  5628. var NicoNews = (function() {
  5629. var WatchApp = null, watch = null, $ = null, WatchJsApi = null, initialized = false;
  5630. var $button = null, $history = null, $ul = null, deteru = {}, $textMarquee, $textMarqueeInner;
  5631. var isHover = false;
  5632.  
  5633. function onNewsUpdate(news) {
  5634. var type = news.data.type, $current = null,
  5635. newsText = $textMarqueeInner.find('.categoryOuter:last').text() +
  5636. $textMarqueeInner.find('.item .title, .item .header, .item .bannertext, .item .text').text(),
  5637. newsHref = $textMarqueeInner.find('a').attr('href');
  5638. if (deteru[newsHref]) {
  5639. $current = deteru[newsHref].remove();
  5640. } else {
  5641. $current = deteru[newsHref] = makeTopic(newsText, newsHref, type);
  5642. }
  5643. $ul.append($current);
  5644. $current.show(200, scrollToBottom);
  5645. }
  5646. function makeTopic(title, url, type) {
  5647. return $([
  5648. '<li style="display: none;">',
  5649. '<a href="', url , '" target="_blank" class="', type, ' title="', escape(title),'">', title, '</a>',
  5650. '</li>',
  5651. ''].join(''));
  5652. }
  5653. function scrollToBottom() {
  5654. if (!isHover) {
  5655. $history.animate({scrollTop: $('.newsHistory ul').innerHeight()}, 200);
  5656. }
  5657. }
  5658.  
  5659. var self = {
  5660. initialize: function(w) {
  5661. WatchApp = w.WatchApp;
  5662. if (!WatchApp || initialized) { return; }
  5663. watch = WatchApp.ns.init;
  5664. $ = w.$;
  5665. WatchJsApi = w.WatchJsApi;
  5666. $textMarquee = $('#textMarquee');
  5667. $textMarqueeInner = $textMarquee.find('.textMarqueeInner');
  5668.  
  5669. watch.TextMarqueeInitializer.textMarqueeViewController.scheduler.addEventListener(
  5670. 'schedule',
  5671. onNewsUpdate);
  5672.  
  5673. $button = $('<button class="openNewsHistory" title="ニコニコニュースの履歴を開く">▲</button>');
  5674. $history = $('<div class="newsHistory" style="display: none;"><ul></ul></div>');
  5675. $history.hover(
  5676. function() { isHover = true; },
  5677. function() { isHover = false; }
  5678. );
  5679. $ul = $history.find('ul');
  5680. $button.click(function() { self.toggle(); });
  5681.  
  5682. $textMarquee.append($button).append($history);
  5683. initialized = true;
  5684. },
  5685. open: function() {
  5686. $history.show(200, function() {
  5687. scrollToBottom();
  5688. WatchApp.ns.util.WindowUtil.scrollFitMinimum('.newsHistory', 200);
  5689. });
  5690. },
  5691. close: function() {
  5692. $history.hide(200);
  5693. isHover = false;
  5694. },
  5695. toggle: function() {
  5696. if ($history.is(':visible')) {
  5697. $button.text('▲');
  5698. this.close();
  5699. } else {
  5700. $button.text('▼');
  5701. this.open();
  5702. }
  5703. }
  5704. };
  5705. return self;
  5706. })();
  5707.  
  5708.  
  5709.  
  5710. /**
  5711. * マイリストや検索API互換形式のデータを返すやつ
  5712. */
  5713. var DummyMylist = function() { this.initialize.apply(this, arguments); };
  5714. DummyMylist.prototype = {
  5715. banner: '',
  5716. id: '-100',
  5717. sort: '4',
  5718. isDeflist: -1,
  5719. isWatchngCountFull: false,
  5720. isWatchngThisMylist: false,
  5721. itemCount: 0,
  5722. items: [],
  5723. rawData: {},
  5724. page: 1,
  5725. perPage: 32,
  5726. type: 2, // 2: MYLIST_VIDEO
  5727. //
  5728. // ver130726より新規追加 defineGetterのほうがいいかも
  5729.  
  5730. status: 'ok',
  5731. name: '',
  5732. description: '',
  5733. user_id: '',
  5734. user_nickname: 'ニコニコ動画',
  5735. default_sort: '1',
  5736. is_watching_this_mylist: false,
  5737. is_watching_count_full: false,
  5738. list: [],
  5739. // ここまで
  5740. initialize: function(param) {
  5741. this.rawData = {
  5742. status: 'ok',
  5743. list: [],
  5744. name: '総合ランキング',
  5745. description: '',
  5746. is_watching_count_full: false,
  5747. is_watching_this_mylist: false,
  5748. user_nickname: '',
  5749. user_id: '',
  5750. sort: '1'
  5751. };
  5752. this._baseCreateTime = Date.now();//new Date();
  5753. this.rawData.user_nickname = param.user_nickname || WatchController.getMyNick();
  5754. this.rawData.user_id = param.user_id || WatchController.getMyUserId();
  5755. this.rawData.name = param.name || this.rawData.name;
  5756. this.rawData.description = param.description || '';
  5757.  
  5758. this.type = param.type || WatchApp.ns.components.videoexplorer.model.ContentType.MYLIST_VIDEO;
  5759. this.sort = this.rawData.sort = param.sort || this.sort;
  5760. this.id = param.id || '-100';
  5761.  
  5762. this.status = this.rawData.status;
  5763. this.list = this.rawData.list;
  5764. this.name = this.rawData.name;
  5765. this.description = this.rawData.description;
  5766. this.default_sort = this.rawData.sort || this.sort;
  5767. this.user_nickname = this.rawData.user_nickname || this.user_nickname;
  5768. this.user_id = this.rawData.user_id;
  5769.  
  5770.  
  5771. },
  5772. setName: function(name) {
  5773. this.rawData.name = name;
  5774. },
  5775. getName: function() {
  5776. return this.rawData.name || '';
  5777. },
  5778. setPage: function(page) {
  5779. this.page = page;
  5780. this.items = this.rawData.list.slice(page * this.perPage - this.perPage, page * this.perPage);
  5781. },
  5782. push: function(item) {
  5783. if (!item.create_time) {
  5784. var tm = this._baseCreateTime - 60000 * this.itemCount;
  5785. item.create_time = tm;
  5786. }
  5787. this.rawData.list.push(item);
  5788. this.itemCount = this.rawData.list.length;
  5789. this.setPage(this.page);
  5790. },
  5791. unshift: function(item) {
  5792. if (!item.create_time) {
  5793. var tm = this._baseCreateTime + 60000 * this.itemCount;
  5794. item.create_time = tm;
  5795. }
  5796. this.rawData.list.unshift(item);
  5797. this.itemCount = this.rawData.list.length;
  5798. this.setPage(this.page);
  5799. },
  5800. slice: function(b, e) {
  5801. this.rawData.list = this.rawData.list.slice(b, e);
  5802. this.itemCount = this.rawData.list.length;
  5803. this.setPage(this.page);
  5804. },
  5805. sortItem: function(sortId, force) {
  5806. sortId = parseInt(sortId, 10);
  5807. if (!!!force && (sortId < 0 || sortId === parseInt(this.sort, 10)) ) { return; }
  5808. var sortKey = ([
  5809. 'create_time', 'create_time',
  5810. 'mylist_comment', 'mylist_comment',
  5811. 'title', 'title',
  5812. 'first_retrieve', 'first_retrieve',
  5813. 'view_counter', 'view_counter',
  5814. 'thread_update_time', 'thread_update_time',
  5815. 'num_res', 'num_res',
  5816. 'mylist_counter', 'mylist_counter',
  5817. 'length_seconds', 'length_seconds'
  5818. ])[sortId],
  5819. order = (sortId % 2 === 0) ? 'asc' : 'desc';
  5820.  
  5821. if (!sortKey) { return; }
  5822. var compare= {
  5823. asc: function(a, b) { return (a[sortKey] > b[sortKey] ) ? 1 : -1; },
  5824. desc: function(a, b) { return (a[sortKey] < b[sortKey] ) ? 1 : -1; },
  5825. iasc: function(a, b) { return (a[sortKey]*1 > b[sortKey]*1) ? 1 : -1; },
  5826. idesc: function(a, b) { return (a[sortKey]*1 < b[sortKey]*1) ? 1 : -1; }
  5827. };
  5828. // 偶数がascで奇数がdescかと思ったら特に統一されてなかった
  5829. if (
  5830. sortKey === 'first_retrieve' ||
  5831. sortKey === 'thread_update_time'
  5832. ) {
  5833. order = (sortId % 2 === 1) ? 'asc' : 'desc';
  5834. } else
  5835. // 数値系は偶数がdesc
  5836. if (sortKey === 'view_counter' ||
  5837. sortKey === 'num_res' ||
  5838. sortKey === 'mylist_counter' ||
  5839. sortKey === 'length_seconds'
  5840. ) {
  5841. order = (sortId % 2 === 1) ? 'iasc' : 'idesc';
  5842. }
  5843. this.sort = this.rawData.sort = sortId.toString();
  5844. this.rawData.list.sort(compare[order]);
  5845. this.items = this.rawData.list.slice(0, 32);
  5846. this.list = this.rawData.list.slice(0);
  5847. }
  5848. };
  5849.  
  5850. var DummyMylistVideo = function() { this.initialize.apply(this, arguments); };
  5851. DummyMylistVideo.prototype = {
  5852. id: 0,
  5853. title: '',
  5854. length: 0,
  5855. view_counter: 0,
  5856. num_res: 0,
  5857. mylist_counter: 0,
  5858. description_short: '',
  5859. first_retrieve: null,
  5860. thumbnail_url: null,
  5861. mylist_comment: '',
  5862. create_time: null,
  5863. type: 0, //'video',
  5864. _info: {},
  5865.  
  5866.  
  5867. initialize: function(info) {
  5868. this._info = info._info || this;
  5869. this.id = info.id;
  5870. this.length = info.length;
  5871. this.mylist_counter = info.mylist_counter || 0;
  5872. this.view_counter = info.view_counter || 0;
  5873. this.num_res = info.num_res || 0;
  5874. this.first_retrieve = info.first_retrieve || '2000-01-01 00:00:00';
  5875. this.create_time = info.create_time || null;
  5876. this.thumbnail_url = info.thumbnail_url || 'http://res.nimg.jp/img/common/video_deleted_ja-jp.jpg' /* 「視聴できません」 */;
  5877. this.title = info.title || '';
  5878. this.type = info.type || 'video';
  5879. this.description_short = info.description_short;
  5880. this.length = info.length || '00:00';
  5881. this.length_seconds = parseInt(info.length_seconds || 0, 10);
  5882. this.mylist_comment = info.mylist_comment || '';
  5883. this.type = info.type || WatchApp.ns.components.videoexplorer.model.ContentItemType.VIDEO;
  5884.  
  5885. if (this.length_seconds === 0 && this.length && this.length.indexOf(':') >= 0) {
  5886. var sp = this.length.split(':');
  5887. this.length_seconds = sp[0] * 60 + sp[1] * 1;
  5888. } else
  5889. if (this.length === '00:00' && this.length_seconds > 0) {
  5890. this.length = parseInt(this.length_seconds / 60, 10) + ':' + (this.length_seconds % 60);
  5891. }
  5892.  
  5893. if (typeof info.is_middle_thumbnail !== 'boolean') {
  5894. if (this.thumbnail_url.indexOf('.M') >= 0) {
  5895. this.thumbnail_url = this.thumbnail_url.replace(/\.M$/, '');
  5896. this.is_middle_thumbnail = true;
  5897. } else
  5898. if (this.thumbnail_url.indexOf('.M') < 0 &&
  5899. this.id.indexOf('sm') === 0) {
  5900. var threshold = 23608629, // .Mのついた最小ID?
  5901. _id = _.parseInt(this.id.substr(2));
  5902. if (_id >= threshold) {
  5903. this.is_middle_thumbnail = true;
  5904. }
  5905. }
  5906. }
  5907. },
  5908. getType: function() { return this.type; },
  5909. getInfo: function() { return this; }, // 手抜き
  5910. getName: function() { return this.title; },
  5911. getId: function() { return this.id; },
  5912. getDescription: function() { return this.description_short; },
  5913.  
  5914.  
  5915. length_seconds: 0, // TODO:
  5916. thread_update_time: '2000-01-01 00:00:00' // TODO: 「コメントが新しい順でソート」に必要?
  5917. };
  5918.  
  5919. // 参考:
  5920. // http://looooooooop.blog35.fc2.com/blog-entry-1146.html
  5921. // http://toxy.hatenablog.jp/entry/2013/07/25/200645
  5922. // http://ch.nicovideo.jp/pita/blomaga/ar297860
  5923. // http://search.nicovideo.jp/docs/api/ma9.html
  5924. var NewNicoSearch = function() { this.initialize.apply(this, arguments); };
  5925. NewNicoSearch.API_BASE_URL = 'http://api.search.nicovideo.jp/api/';
  5926. NewNicoSearch.PAGE_BASE_URL = 'http://search.nicovideo.jp/video/';
  5927. NewNicoSearch.prototype = {
  5928. _u: '', // 24h, 1w, 1m, ft 期間指定
  5929. _ftfrom: '', // YYYY-MM-DD
  5930. _ftto: '', // YYYY-MM-DD
  5931. _l: '', // short long
  5932. _m: false, // true=音楽ダウンロード
  5933. _sort: '', // last_comment_time, last_comment_time_asc,
  5934. // view_counter, view_counter_asc,
  5935. // comment_counter, comment_counter_asc,
  5936. // mylist_counter, mylist_counter_asc,
  5937. // upload_time, upload_time_asc,
  5938. // length_seconds, length_seconds_asc
  5939. _size: 32, // 一ページの件数 maxは100
  5940. _issuer: 'watch-it-later',
  5941. _base_url: NewNicoSearch.API_BASE_URL,
  5942. initialize: function(params) {
  5943.  
  5944. },
  5945. load: function(params, callback) {
  5946. var url = this._base_url;
  5947. var data = {};
  5948. data.query = params.query || 'Qwatch';
  5949. data.service = params.service || ['video']; // video video_tag
  5950. data.search = params.search || ['title', 'tags', 'description'];
  5951. data.join = params.join || [
  5952. // TODO:投稿者IDを取得する方法がないか?
  5953. 'cmsid', 'title', 'description', 'thumbnail_url', 'start_time',
  5954. 'view_counter', 'comment_counter', 'mylist_counter', 'length_seconds', 'last_res_body'
  5955. // 'user_id', 'channel_id', 'main_community_id', 'ss_adlut'
  5956. ];
  5957. data.filters = params.filters || [{}];
  5958. data.sort_by = params.sort_by || 'start_time';
  5959. data.order = params.order || 'desc';
  5960. data.timeout = params.timeout || 10000;
  5961. data.issuer = params.issuer || 'watch-it-later';
  5962. data.reason = params.reason || 'video-explorer'; // 'watchItLater';
  5963. data.size = params.size || 32;
  5964. data.from = params.from || 0;
  5965.  
  5966. if (params.sort_by === '_hot') { // 人気順ソートのパラメータ
  5967. data.hot_field = params.hot_field;
  5968. data.hot_from = params.hot_from;
  5969. data.hot_to = params.hot_to;
  5970. }
  5971.  
  5972. var cache_key = JSON.stringify({url: url, data: data}), cache = Util.Cache.get(cache_key);
  5973. if (cache) {
  5974. setTimeout(function() { callback(null, cache); }, 0);
  5975. return;
  5976. }
  5977.  
  5978. $.ajax({
  5979. url: url,
  5980. type: 'POST',
  5981. data: JSON.stringify(data),
  5982. timeout: 30000,
  5983. complete: function(result) {
  5984. console.log('result', result);
  5985. if (result.status !== 200) {
  5986. callback('fail', 'HTTP status:' + result.status);
  5987. return;
  5988. }
  5989. var data;
  5990. try {
  5991. var lines = result.responseText.split('\n'), head = JSON.parse(lines[0]);
  5992. if (head.values[0].total > 0) {
  5993. data = [head];
  5994. for (var i = 1, len = lines.length; i < len - 1; i++) {
  5995. data.push(JSON.parse(lines[i]));
  5996. }
  5997. } else {
  5998. data = [head, JSON.parse(lines[1]), {type: 'hits', values: []}, JSON.parse(lines[2])];
  5999. }
  6000. Util.Cache.set(cache_key, data);
  6001. } catch(e) {
  6002. console.log('Exception: ', e, result);
  6003. callback('fail', 'JSON syntax');
  6004. return;
  6005. }
  6006. callback(null, data);
  6007. },
  6008. error: function(req, status, thrown) {
  6009. if (status === 'parsererror') {
  6010. return;
  6011. }
  6012. console.log('%c ajax error: ' + status, 'background: red', arguments);
  6013. callback('fail', status);
  6014. }
  6015. });
  6016. }
  6017. };
  6018.  
  6019.  
  6020.  
  6021. /**
  6022. * niconico新検索の検索結果を既存の検索API互換形式に変換して返すやつ
  6023. */
  6024. var NewNicoSearchWrapper = function() { this.initialize.apply(this, arguments); };
  6025. NewNicoSearchWrapper.prototype = {
  6026. _search: null,
  6027. sortTable: {f: 'start_time', v: 'view_counter', r: 'comment_counter', m: 'mylist_counter', l: 'length_seconds',
  6028. '_hot': '_hot', // 人気が高い順
  6029. '_explore': '_explore', // 新着優先
  6030. '_popular': '_popular' // 並び順指定なし
  6031. },
  6032. initialize: function(params) {
  6033. this._search = params.search;
  6034. },
  6035. _buildSearchQuery: function(params) {
  6036. var query = {filters: []};
  6037. var sortTable = this.sortTable;
  6038. query.query = params.searchWord;
  6039. query.search = params.searchType === 'tag' ? ['tags'] : ['tags', 'title', 'description'];
  6040. query.sort_by = params.sort && sortTable[params.sort] ? sortTable[params.sort] : 'last_comment_time';
  6041. query.order = params.order === 'd' ? 'desc' : 'asc';
  6042. query.size = params.size || 32;
  6043. query.from = params.page ? Math.max(parseInt(params.page, 10) - 1, 0) * query.size : 0;
  6044.  
  6045. var n = new Date();
  6046. var now = n.getTime();
  6047. switch (params.u) {
  6048. case '1h':
  6049. query.filters.push(this._buildStartTimeRangeFilter(new Date(now - 1 * 1 * 60 * 60 * 1000)));
  6050. break;
  6051. case '24h': case '1d':
  6052. query.filters.push(this._buildStartTimeRangeFilter(new Date(now - 1 * 24 * 60 * 60 * 1000)));
  6053. break;
  6054. case '1w': case '7d':
  6055. query.filters.push(this._buildStartTimeRangeFilter(new Date(now - 7 * 24 * 60 * 60 * 1000)));
  6056. break;
  6057. case '1m':
  6058. query.filters.push(this._buildStartTimeRangeFilter(new Date(now - 30 * 24 * 60 * 60 * 1000)));
  6059. break;
  6060. case '3m':
  6061. query.filters.push(this._buildStartTimeRangeFilter(new Date(now - 90 * 24 * 60 * 60 * 1000)));
  6062. break;
  6063. case '6m':
  6064. query.filters.push(this._buildStartTimeRangeFilter(new Date(now - 180 * 24 * 60 * 60 * 1000)));
  6065. break;
  6066. default:
  6067. break;
  6068. }
  6069.  
  6070. if (query.sort_by === '_hot') {
  6071. // 人気が高い順ソート
  6072. (function() {
  6073. var format = function(date) { return WatchApp.ns.util.DateFormat.strftime('%Y-%m-%d %H:%M:%S', date); };
  6074. query.hot_field = 'mylist_counter';
  6075. query.hot_from = format(new Date(now - 1 * 24 * 60 * 60 * 1000));
  6076. query.hot_to = format(n);
  6077.  
  6078. query.order = 'desc';
  6079. })();
  6080. }
  6081.  
  6082. if (typeof params.userId === 'string' && params.userId.match(/^\d+$/)) {
  6083. query.filters.push({type: 'equal', field: 'user_id', value: params.userId});
  6084. }
  6085. if (typeof params.channelId === 'string' && params.channelId.match(/^\d+$/)) {
  6086. query.filters.push({type: 'equal', field: 'channel_id', value: params.channelId});
  6087. }
  6088.  
  6089. if (params.l === 'short') { // 5分以内
  6090. query.filters.push(this._buildLengthSecondsRangeFilter(0, 60 * 5));
  6091. } else
  6092. if (params.l === 'long' ) { // 20分以上
  6093. query.filters.push(this._buildLengthSecondsRangeFilter(60 * 20));
  6094. }
  6095.  
  6096. if (params.m === true) { // 音楽ダウンロード
  6097. query.filters.push({type: 'equal', field: 'music_download', value: true});
  6098. }
  6099.  
  6100. // TODO: これの調査 → {field: 'ss_adult', type: 'equal', value: false}
  6101.  
  6102. return query;
  6103. },
  6104. _buildStartTimeRangeFilter: function(from, to) {
  6105. var format = function(date) { return WatchApp.ns.util.DateFormat.strftime('%Y-%m-%d %H:%M:%S', date); };
  6106. var range = {field: 'start_time', type: 'range', include_lower: true, };
  6107. range.from = format(from);
  6108. if (to) range.to = format(to);
  6109. return range;
  6110. },
  6111. _buildLengthSecondsRangeFilter: function(from, to) {
  6112. var range = {field: 'length_seconds', type: 'range'};
  6113. if (to) { // xxx ~ xxx
  6114. range.from = Math.min(from, to);
  6115. range.to = Math.max(from, to);
  6116. range.include_lower = range.include_upper = true;
  6117. } else { // xxx以上
  6118. range.from = from;
  6119. range.include_lower = true;
  6120. }
  6121. return range;
  6122. },
  6123. load: function(params, callback) {
  6124. var query = this._buildSearchQuery(params);
  6125. this._search.load(query, $.proxy(function(err, result) {
  6126. this.onLoad(err, result, params, query, callback);
  6127. }, this));
  6128. },
  6129. onLoad: function(err, result, params, query, callback) {
  6130. if (err) {
  6131. console.log('load fail', err, result);
  6132. callback('fail', {message: '通信に失敗しました1'});
  6133. return;
  6134. }
  6135. var searchResult;
  6136. searchResult = {
  6137. status: 'ok',
  6138. count: result[0].values[0].total,
  6139. page: params.page,
  6140. list: []
  6141. };
  6142. var pushItems = function(items) {
  6143. var len = items.length;
  6144. for (var i = 0; i < len; i++) {
  6145. var item = items[i], description = item.description ? item.description.replace(/<.*?>/g, '') : '';
  6146.  
  6147. item.id = item.cmsid;
  6148. if (item.thumbnail_url.indexOf('.M') >= 0) {
  6149. item.thumbnail_url = item.thumbnail_url.replace(/\.M$/, '');
  6150. item.is_middle_thumbnail = true;
  6151. } else
  6152. if (item.thumbnail_url.indexOf('.M') < 0 &&
  6153. item.id.indexOf('sm') === 0) {
  6154. var threshold = 23608629, // .Mのついた最小ID?
  6155. _id = _.parseInt(item.id.substr(2));
  6156. if (_id >= threshold) {
  6157. item.is_middle_thumbnail = true;
  6158. }
  6159. }
  6160.  
  6161. searchResult.list.push({
  6162. id: item.cmsid,
  6163. type: 0, // 0 = VIDEO,
  6164. length: item.length_seconds ?
  6165. Math.floor(item.length_seconds / 60) + ':' + (item.length_seconds % 60 + 100).toString().substr(1) : '',
  6166. mylist_counter: item.mylist_counter,
  6167. view_counter: item.view_counter,
  6168. num_res: item.comment_counter,
  6169. first_retrieve: item.start_time,
  6170. create_time: item.start_time,
  6171. thumbnail_url: item.thumbnail_url,
  6172. title: item.title,
  6173. description_short: description.substr(0, 150),
  6174. description_full: description,
  6175. length_seconds: item.length_seconds,
  6176. last_res_body: item.last_res_body,
  6177. is_middle_thumbnail: item.is_middle_thumbnail
  6178. // channel_id: item.channel_id,
  6179. // main_community_id: item.main_community_id
  6180. });
  6181. }
  6182. };
  6183. for (var i = 1; i < result.length; i++) {
  6184. if (result[i].type === 'hits' && result[i].endofstream) { break; }
  6185. if (result[i].type === 'hits' && result[i].values) {
  6186. pushItems(result[i].values);
  6187. }
  6188. }
  6189. callback(null, searchResult);
  6190. }
  6191. };
  6192.  
  6193. // sug.search.nicovideo.jpはリアルタイムの入力補完用? で、関連タグはhttp://api.search.nicovideo.jp/api/tag/ っぽい
  6194. var NicoSearchSuggest = function() { this.initialize.apply(this, arguments); };
  6195. NicoSearchSuggest.API_BASE_URL = 'http://sug.search.nicovideo.jp/'; //'/suggestion/complete';
  6196. NicoSearchSuggest.prototype = {
  6197. _base_url: NicoSearchSuggest.API_BASE_URL,
  6198. initialize: function(params) {
  6199. },
  6200. load: function(word, callback) {
  6201. if (typeof word !== 'string' || word.length <= 0) {
  6202. throw new Error('wordが設定されてない!');
  6203. }
  6204. var url = this._base_url + 'suggestion/complete',
  6205. cache_key = JSON.stringify({url: url, word: word}),
  6206. cache_time = 60 * 1000 * 1,
  6207. cache = Util.Cache.get(cache_key);
  6208. if (cache) {
  6209. setTimeout(function() { callback(null, cache); }, 0);
  6210. return;
  6211. }
  6212. $.ajax({
  6213. url: url,
  6214. type: 'POST',
  6215. data: word,
  6216. timeout: 30000,
  6217. complete: function(result) {
  6218. if (result.status !== 200) {
  6219. callback('fail', 'HTTP status:' + result.status);
  6220. return;
  6221. }
  6222. var data;
  6223. try {
  6224. data = JSON.parse(result.responseText);
  6225. } catch(e) {
  6226. console.log('Exception: ', e, result);
  6227. callback('fail', 'JSON syntax');
  6228. }
  6229. Util.Cache.set(cache_key, data, cache_time);
  6230. callback(null, data);
  6231. },
  6232. error: function(req, status, thrown) {
  6233. if (status === 'parsererror') {
  6234. return;
  6235. }
  6236. callback('fail', status);
  6237. }
  6238. });
  6239. }
  6240. };
  6241.  
  6242. var NicoSearchRelatedTag = function() { this.initialize.apply(this, arguments); };
  6243. NicoSearchRelatedTag.API_BASE_URL = 'http://api.search.nicovideo.jp/';
  6244. NicoSearchRelatedTag.prototype = {
  6245. _base_url: NicoSearchRelatedTag.API_BASE_URL,
  6246. initialize: function(params) {
  6247. },
  6248. load: function(word, callback) {
  6249. var url = this._base_url + 'api/tag/',
  6250. cache_key = JSON.stringify({url: url, word: word}),
  6251. cache_time = 60 * 1000 * 10,
  6252. cache = Util.Cache.get(cache_key);
  6253. if (cache) {
  6254. setTimeout(function() { callback(null, cache); }, 0);
  6255. return;
  6256. }
  6257. var query = {query: word, service: ['tag_video'], from: 0, size: 100, timeout: 10000, reason: 'user'};
  6258. $.ajax({
  6259. url: url,
  6260. type: 'POST',
  6261. data: JSON.stringify(query),
  6262. timeout: 30000,
  6263. complete: function(result) {
  6264. if (result.status !== 200) {
  6265. callback('fail', 'HTTP status:' + result.status);
  6266. return;
  6267. }
  6268. var data;
  6269. try {
  6270. var lines = result.responseText.split('\n');
  6271. data = JSON.parse(lines[0]);
  6272. } catch(e) {
  6273. console.log('Exception: ', e, result);
  6274. callback('fail', 'JSON syntax');
  6275. return;
  6276. }
  6277. Util.Cache.set(cache_key, data, cache_time);
  6278. callback(null, data);
  6279. },
  6280. error: function(req, status, thrown) {
  6281. if (status === 'parsererror') {
  6282. return;
  6283. }
  6284. callback('fail', status);
  6285. }
  6286. });
  6287. }
  6288. };
  6289.  
  6290.  
  6291. var VideoInfoLoader = function() { this.initialize.apply(this, arguments); };
  6292. VideoInfoLoader.BASE_URL = "http://riapi.nicovideo.jp/api/search/tag";
  6293. VideoInfoLoader.prototype = {
  6294. initialize: function(params) {
  6295. },
  6296. load: function(id, callback) {
  6297. var def = new $.Deferred;
  6298.  
  6299. var cache_key = JSON.stringify({'VideoInfoLoaderCache': id}), cacheData = Util.Cache.get(cache_key);
  6300. if (cacheData) {
  6301. return def.resolve(cacheData);
  6302. }
  6303.  
  6304. if (id.toString().match(/^\d+$/)) { // watchId
  6305. WatchApp.ns.init.PlaylistInitializer.videoInfoAPILoader.load(
  6306. [id],
  6307. function(err, resp) {
  6308. if (err !== null) {
  6309. return def.reject({message: '通信に失敗しました(1)', status: 'fail'});
  6310. }
  6311. if (resp.items && resp.items[id] && resp.items[id].id) {
  6312. if (typeof callback === 'function') { callback(null, resp.items[id]); }
  6313. return def.resolve(Util.Cache.set(cache_key, resp.items[id]));
  6314. }
  6315. var err = {message: '動画が見つかりませんでした(1): ' + id, status: 'fail'};
  6316. if (typeof callback === 'function') { callback(err, null); }
  6317. return def.reject(err);
  6318. }
  6319. );
  6320. return def.promise();
  6321. }
  6322.  
  6323. // タグ検索APIの「もしかして: xxx」を使って動画情報を取得する
  6324. WatchApp.ns.util.HTTPUtil.loadXDomainAPI({ // videoId
  6325. url: VideoInfoLoader.BASE_URL,
  6326. type: 'GET',
  6327. dataType: 'json',
  6328. xhrFields: {
  6329. withCredentials: true
  6330. },
  6331. data: {
  6332. words: 'watch/' + id,
  6333. sort: 'f',
  6334. order: 'd',
  6335. page: '1',
  6336. mode: 'watch'
  6337. },
  6338. success: function(result) {
  6339. if (result.suggest_video && result.suggest_video.id) {
  6340. if (typeof callback === 'function') { callback(null, result.suggest_video); }
  6341. def.resolve(Util.Cache.set(cache_key, result.suggest_video));
  6342. } else {
  6343. var err = {message: '動画が見つかりませんでした(2): ' + id, status: 'fail'};
  6344. if (typeof callback === 'function') { callback(err, null); }
  6345. def.reject(err);
  6346. }
  6347. },
  6348. error: function(resp) {
  6349. var err = {message: '通信に失敗しました(2)', status: 'fail'};
  6350. if (typeof callback === 'function') { callback(err, null); }
  6351. def.reject(err);
  6352. }
  6353. });
  6354. return def.promise();
  6355. }
  6356. };
  6357. WatchItLater.VideoInfoLoader = new VideoInfoLoader({});
  6358.  
  6359. var RelatedVideo = function() { this.initialize.apply(this, arguments); }
  6360. RelatedVideo.prototype = {
  6361. initialize: function(params) {
  6362. },
  6363. load: function(watchId) {
  6364. var def = new $.Deferred;
  6365. window.WatchApp.ns.init.VideoExplorerInitializer.relatedVideoAPILoader.load(
  6366. {'video_id': watchId},
  6367. function(err, result) {
  6368. if (err !== null) {
  6369. return def.reject({message: '通信に失敗しました(1)', status: 'fail', err: err});
  6370. }
  6371. return def.resolve(result);
  6372. }
  6373. );
  6374. return def.promise();
  6375. }
  6376. };
  6377. WatchItLater.RelatedVideo = new RelatedVideo({});
  6378.  
  6379. /**
  6380. * 動画視聴履歴をマイリストAPIと互換のある形式で返すことで、ダミーマイリストとして表示してしまう作戦
  6381. */
  6382. var VideoWatchHistory = (function(w, Util){
  6383. function load(callback) {
  6384. var watch, $, myNick, myId, url;
  6385. try{
  6386. watch = w.WatchApp.ns.init;
  6387. $ = w.$; url = '/my/history';
  6388. myNick = WatchController.getMyNick(); myId = WatchController.getMyUserId();
  6389. } catch (e) {
  6390. console.log(e);
  6391. throw { message: 'エラーが発生しました', status: 'fail'};
  6392. }
  6393.  
  6394. var CACHE_KEY = 'videohistory', CACHE_TIME = 1000 * 60 * 1, cacheData = Util.Cache.get(CACHE_KEY);
  6395. if (cacheData) {
  6396. setTimeout(function() {callback(cacheData);}, 0);
  6397. return;
  6398. }
  6399.  
  6400. var result = new DummyMylist({
  6401. id: '-1',
  6402. sort: '1',
  6403. name: myNick + 'の視聴履歴',
  6404. user_id: myId,
  6405. user_name: 'ニコニコ動画'
  6406. });
  6407. GM_xmlhttpRequest({
  6408. url: url,
  6409. onload: function(resp) {
  6410. var $dom = $(resp.responseText), $list = $dom.find('#historyList');
  6411. $list.find('.outer').each(function() {
  6412. var
  6413. $item = $(this), $meta = $item.find('.metadata'), $title = $item.find('.section h5 a'),
  6414. id = $title.attr('href').split('/').reverse()[0], title = $title.text(),
  6415. duration = $item.find('.videoTime').text(),
  6416. viewCnt = $meta.find('.play') .text().split(':')[1].replace(/,/g, ''),
  6417. resCnt = $meta.find('.comment').text().split(':')[1].replace(/,/g, ''),
  6418. mylistCnt = $meta.find('.mylist') .text().split(':')[1].replace(/,/g, ''),
  6419. postedAt = '20' + $meta.find('.posttime').text().replace(/(年|月)/g, '-').replace(/(日| *投稿)/g, ''),
  6420. thumbnail = $item.find('.thumbContainer a .video').attr('src');
  6421.  
  6422. var item = new DummyMylistVideo({
  6423. id: id,
  6424. length: duration,
  6425. mylist_counter: mylistCnt,
  6426. view_counter: viewCnt,
  6427. num_res: resCnt,
  6428. first_retrieve: postedAt,
  6429. thumbnail_url: thumbnail,
  6430. title: title,
  6431. _info: {first_retrieve: postedAt},
  6432. description_short: $item.find('.section .posttime span').text()
  6433. });
  6434. result.push(item);
  6435. });
  6436. callback(Util.Cache.set(CACHE_KEY, result, CACHE_TIME));
  6437. },
  6438. onerror: function() {
  6439. Popup.alert('視聴履歴の取得に失敗しました');
  6440. }
  6441. });
  6442.  
  6443. }
  6444. var self = {
  6445. load : load
  6446. };
  6447. return self;
  6448. })(w, Util);
  6449.  
  6450.  
  6451.  
  6452. var VideoRecommendations = (function(w, Util){
  6453. var histories = {};
  6454. function request(callback) {
  6455. var watch, $, url, myNick, myId;
  6456. try{
  6457. watch = w.WatchApp.ns.init;
  6458. $ = w.$;
  6459. url = '/recommendations';
  6460. myNick = WatchController.getMyNick();
  6461. myId = WatchController.getMyUserId();
  6462. } catch (e) {
  6463. console.log(e);
  6464. throw { message: 'エラーが発生しました', status: 'fail'};
  6465. }
  6466. var CACHE_KEY = 'recommend', CACHE_TIME = 1000 * 60 * 1, cacheData = Util.Cache.get(CACHE_KEY);
  6467. if (cacheData) {
  6468. setTimeout(function() {callback(cacheData); }, 0);
  6469. return;
  6470. }
  6471.  
  6472. var result = new DummyMylist({
  6473. id: '-2',
  6474. sort: '1',
  6475. name: 'あなたにオススメの動画'
  6476. });
  6477. GM_xmlhttpRequest({
  6478. url: url,
  6479. onload: function(resp) {
  6480. var text = resp.responseText, lines = text.split(/[\r\n]/), found = false, data, i, len;
  6481. for (i = 0, len = lines.length; i < len; i++) {
  6482. var line = lines[i];
  6483. if (line.indexOf('var Nico_RecommendationsParams') >= 0 &&
  6484. lines[i + 5] && lines[i + 5].indexOf('first_data') >= 0) {
  6485. data = JSON.parse(lines[i + 5].replace(/^.*?:/, ''));
  6486. if (data && data.videos) {
  6487. found = true;
  6488. break;
  6489. }
  6490. }
  6491. }
  6492. if (!found) {
  6493. throw { message: '取得に失敗しました', status: 'fail'};
  6494. }
  6495.  
  6496. for (i = 0, len = data.videos.length; i < len; i++) {
  6497. var video = data.videos[i];
  6498. if (histories[video.id]) {
  6499. delete histories[video.id];
  6500. }
  6501. var item = new DummyMylistVideo({
  6502. id: video.id,
  6503. length: video.length,
  6504. mylist_counter: video.mylist_counter,
  6505. view_counter: video.view_counter,
  6506. num_res: video.num_res,
  6507. first_retrieve: video.first_retrieve,
  6508. thumbnail_url: video.thumbnail_url,
  6509. title: video.title_short,
  6510. _info: video,
  6511. description_short: '関連タグ: ' + video.recommend_tag
  6512. });
  6513. histories[video.id] = item;
  6514. }
  6515. for (var v in histories) {
  6516. result.unshift(histories[v]);
  6517. }
  6518. result.slice(0, 128);
  6519. callback(Util.Cache.set(CACHE_KEY, result, CACHE_TIME));
  6520. },
  6521. onerror: function() {
  6522. throw { message: '取得に失敗しました', status: 'fail'};
  6523. }
  6524. });
  6525.  
  6526. }
  6527. function load(callback, param) {
  6528. request(function(result) {
  6529. var viewPage = (param && typeof param.page === 'number') ? param.page : 1;
  6530. result.setPage(viewPage);
  6531. callback(result);
  6532. });
  6533. }
  6534. var self = {
  6535. load : load
  6536. };
  6537. return self;
  6538. })(w, Util);
  6539.  
  6540.  
  6541. var NicorepoVideo = (function(w, Util) {
  6542. if (!window.PlayerApp) return {};
  6543.  
  6544. var CACHE_TIME = 1000 * 60 * 10;
  6545. var WatchApp = w.WatchApp;
  6546.  
  6547. var getNicorepoTitle = function(type, param) {
  6548. var base = '【ニコレポ】';
  6549. if (type === 'all') {
  6550. return base + 'すべての動画';
  6551. } else
  6552. if (type === 'chcom') {
  6553. return base + 'お気に入りチャンネル&コミュニティの動画';
  6554. } else
  6555. if (type === 'mylist') {
  6556. return base + 'お気に入りマイリストの動画';
  6557. } else
  6558. if (type === 'owner') {
  6559. return WatchController.getOwnerName() + 'のニコレポ';
  6560. }
  6561. return base + 'お気に入りユーザーの動画';
  6562. };
  6563.  
  6564. var parseItemList = function($dom) {
  6565. var $list = $dom.find('.timeline');
  6566. return $list.find([
  6567. '.log-user-mylist-add',
  6568. '.log-user-uad-advertise',
  6569. '.log-user-video-upload',
  6570. '.log-user-video-review',
  6571. '.log-mylist-added-video',
  6572. '.log-community-video-upload',
  6573. '.log-user-video-round-number-of-view-counter',
  6574. '.log-user-video-round-number-of-mylist-counter'
  6575. ].join(', '));
  6576. };
  6577.  
  6578. var ownerReg = /\/(community|user|channel)\/((co|ch)?\d+)\??/;
  6579. var parseNicorepoItem = function(src) {
  6580. var
  6581. $item = $(src), $title = $item.find('.log-content .log-target-info a'),
  6582. id = $title.attr('href').split('/').reverse()[0].replace(/\?.*$/, ''), title = $title.text(),
  6583. duration = '--:--',
  6584. viewCnt = '-',
  6585. resCnt = '-',
  6586. mylistCnt = '-',
  6587. postedAt = WatchApp.ns.util.DateFormat.strftime('%Y-%m-%d %H:%M:%S', new Date($item.find('.log-footer-date time').attr('datetime'))),
  6588. thumbnail = $item.find('.log-target-thumbnail .video').attr('data-src'),
  6589. description_short = $.trim($item.find('.log-body').text()).replace(/(しました|されました)。/g, ''),
  6590. $owner = $item.find('.author-user, .author-community'),
  6591. ownerPage = $owner.attr('href'),
  6592. ownerMatch = ownerReg.exec(ownerPage),
  6593. ownerName = $owner.text(),
  6594. ownerId = (ownerMatch !== null && ownerMatch.length >= 3) ? ownerMatch[2] : null,
  6595. ownerIcon = $item.find('.log-author img').attr('data-src'),
  6596. mylistComment = $item.find('.log-content .log-subdetails').text().trim()
  6597. ;
  6598.  
  6599. $item.removeClass('log').removeClass('passive').removeClass('first');
  6600. if (src.className === 'log-mylist-added-video') {
  6601. ownerName = $item.find('.log-body a:first').text();
  6602. ownerPage = $item.find('.log-body a:last').attr('href');
  6603. }
  6604.  
  6605. var item = new DummyMylistVideo({
  6606. id: id,
  6607. length: duration,
  6608. mylist_counter: mylistCnt,
  6609. view_counter: viewCnt,
  6610. num_res: resCnt,
  6611. first_retrieve: postedAt,
  6612. thumbnail_url: thumbnail,
  6613. mylist_comment: mylistComment,
  6614. title: title,
  6615. _info: {
  6616. first_retrieve: postedAt,
  6617. nicorepo_className: src.className,
  6618. nicorepo_log: [window._.escape(description_short)],
  6619. nicorepo_owner: {
  6620. id: ownerId,
  6621. icon: ownerIcon,
  6622. page: ownerPage,
  6623. name: ownerName
  6624. }
  6625. },
  6626. description_short: description_short
  6627. });
  6628. return item;
  6629. };
  6630.  
  6631. var loadPage = function(baseUrl, result, nextLink, type) {
  6632. var def = new $.Deferred();
  6633. if (nextLink === null) {
  6634. return def.resolve(baseUrl, result, null, null);
  6635. }
  6636. var url = baseUrl;
  6637. if (type === 'offset') {
  6638. url += nextLink ? ('&offset=' + nextLink) : '';
  6639. } else {
  6640. url += nextLink ? ('&last_timeline=' + nextLink) : '';
  6641. }
  6642. console.log('load Url=', url);
  6643.  
  6644. $.ajax({
  6645. url: url,
  6646. timeout: 30000
  6647. }).then(
  6648. function(resp) {
  6649. var $dom = $(resp),
  6650. $nextPageLink = $dom.find('.next-page-link'),
  6651. hasNextPage = $nextPageLink.length > 0;
  6652.  
  6653. parseItemList($dom).each(function() {
  6654. result.push(parseNicorepoItem(this));
  6655. });
  6656.  
  6657. var nextLinkReg = /(last_timeline|offset)=(\d+)/;
  6658. if (hasNextPage) {
  6659. var href = $nextPageLink.attr('href');
  6660. if (nextLinkReg.test(href)) {
  6661. def.resolve(baseUrl, result, RegExp.$2, RegExp.$1);
  6662. } else {
  6663. def.resolve(baseUrl, result, null, null);
  6664. }
  6665. } else {
  6666. def.resolve(baseUrl, result, null, null);
  6667. }
  6668. },
  6669. function() {
  6670. def.reject();
  6671. });
  6672.  
  6673. return def.promise();
  6674. };
  6675.  
  6676. var pipeRequest = function(baseUrl, result, maxPages, callback) {
  6677. var def = new $.Deferred(), p = def.promise();
  6678.  
  6679. for (var i = maxPages; i >= 0; i--) {
  6680. p = p.then(loadPage);
  6681. if (i > 0) p = p.then(Util.Deferred.wait(300));
  6682. }
  6683.  
  6684. p.then(
  6685. function() {
  6686. var uniq = {}, uniq_items = [];
  6687. for (var i = result.rawData.list.length - 1; i >= 0; i--) {
  6688. var item = result.rawData.list[i], id = item.id, mc = item.mylist_comment;
  6689. if (uniq[id + mc]) {
  6690. uniq[id + mc]._info.nicorepo_log.push(item.first_retrieve + ' ' + item._info.nicorepo_log[0].replace(/^.*?さん(の|が)動画(が|を) ?/, ''));
  6691. } else {
  6692. uniq[id + mc] = item;
  6693. }
  6694. }
  6695. for (var v in uniq) {
  6696. uniq_items.unshift(uniq[v]);
  6697. }
  6698. result.rawData.list = uniq_items;
  6699. callback(result);
  6700. }
  6701. );
  6702. def.resolve(baseUrl, result, '', '');
  6703.  
  6704. };
  6705.  
  6706. var request = function(param) {
  6707. var url, nickname, userId, type, baseUrl;
  6708. var def = new $.Deferred;
  6709. try {
  6710. url = '';
  6711. nickname = param.nickname || WatchController.getMyNick();
  6712. userId = param.userId || WatchController.getMyUserId();
  6713. type = param.type || 'user';
  6714. baseUrl = '/my/top/' + type + '?innerPage=1&mode=next_page';
  6715. if (param.userId) {
  6716. baseUrl = '/user/'+ param.userId +'/top?innerPage=1&mode=next_page';
  6717. }
  6718. } catch (e) {
  6719. console.log(e);
  6720. return def.reject({message: 'エラーが発生しました', status: 'fail'});
  6721. }
  6722.  
  6723. var cacheData = Util.Cache.get(baseUrl);
  6724. if (cacheData) {
  6725. return def.resolve(cacheData);
  6726. }
  6727.  
  6728. var
  6729. result = new DummyMylist({
  6730. id: '-10',
  6731. sort: '1',
  6732. default_sort: '1',
  6733. name: getNicorepoTitle(type, param),
  6734. user_id: type === 'owner' ? WatchController.getOwnerId() : userId,
  6735. user_nickname: type === 'owner' ? WatchController.getOwnerName() : nickname
  6736. });
  6737.  
  6738. pipeRequest(baseUrl, result, 2, function(result) {
  6739. def.resolve(Util.Cache.set(baseUrl, result, CACHE_TIME));
  6740. });
  6741.  
  6742. return def.promise();
  6743. };
  6744.  
  6745. var load = function(callback, param) {
  6746. return request(param)
  6747. .then(function(result) {
  6748. var viewPage = (param && typeof param.page === 'number') ? param.page : 1;
  6749. result.sortItem(param.sort || 1, true);
  6750. result.setPage(viewPage);
  6751. if (typeof result === 'function') { callback(result); }
  6752. return this.done(result);
  6753. }, function() {
  6754. return this.fail({message: 'ニコレポの取得に失敗しました', status: 'fail'});
  6755. });
  6756. };
  6757.  
  6758. var self = {
  6759. load: load,
  6760. REPO_ALL: -10,
  6761. REPO_USER: -11,
  6762. REPO_CHCOM: -12,
  6763. REPO_MYLIST: -13,
  6764. REPO_OWNER: -14,
  6765. loadAll: function(callback, p) {
  6766. p = p || {};
  6767. p.type = 'all';
  6768. return self.load(callback, p);
  6769. },
  6770. loadUser: function(callback, p) {
  6771. p = p || {};
  6772. p.type = 'user';
  6773. return self.load(callback, p);
  6774. },
  6775. loadChCom: function(callback, p) {
  6776. p = p || {};
  6777. p.type = 'chcom';
  6778. return self.load(callback, p);
  6779. },
  6780. loadMylist: function(callback, p) {
  6781. p = p || {};
  6782. p.type = 'mylist';
  6783. return self.load(callback, p);
  6784. },
  6785. loadOwner: function(callback, p) {
  6786. p = p || {};
  6787. p.type = 'owner';
  6788. p.userId = WatchController.getOwnerId();
  6789. return self.load(callback, p);
  6790. }
  6791. };
  6792. WatchItLater.NicorepoVideo = self;
  6793.  
  6794. return self;
  6795. })(w, Util);
  6796.  
  6797.  
  6798.  
  6799. /**
  6800. * ランキングのRSSをマイリストAPIと互換のある形式に変換することで、ダミーマイリストとして表示してしまう作戦
  6801. */
  6802. var VideoRanking = (function(w, Util) {
  6803. if (!window.PlayerApp) return {};
  6804. var $ = w.jQuery;
  6805.  
  6806. var
  6807. genreIdTable = {
  6808. all: -100,
  6809. g_ent2: -110,
  6810. ent: -111,
  6811. music: -112,
  6812. sing: -113,
  6813. play: -114,
  6814. dance: -115,
  6815. vocaloid: -116,
  6816. nicoindies: -117,
  6817. g_life2: -120,
  6818. animal: -121,
  6819. cooking: -122,
  6820. nature: -123,
  6821. travel: -124,
  6822. sport: -125,
  6823. lecture: -126,
  6824. drive: -127,
  6825. history: -128,
  6826. g_politics: -130,
  6827. g_tech: -140,
  6828. science: -141,
  6829. tech: -142,
  6830. handcraft: -143,
  6831. make: -144,
  6832. g_culture2: -150,
  6833. anime: -151,
  6834. game: -152,
  6835. toho: -153,
  6836. imas: -154,
  6837. radio: -155,
  6838. draw: -156,
  6839. g_other: -160,
  6840. are: -161,
  6841. diary: -162,
  6842. other: -163
  6843. // r18: -170
  6844. },
  6845. genreNameTable = {
  6846. all: 'カテゴリ合算',
  6847. g_ent2: 'エンタメ・音楽',
  6848. ent: 'エンターテイメント',
  6849. music: '音楽',
  6850. sing: '歌ってみた',
  6851. play: '演奏してみた',
  6852. dance: '踊ってみた',
  6853. vocaloid: 'VOCALOID',
  6854. nicoindies: 'ニコニコインディーズ',
  6855. g_life2: '生活・一般・スポ',
  6856. animal: '動物',
  6857. cooking: '料理',
  6858. nature: '自然',
  6859. travel: '旅行',
  6860. sport: 'スポーツ',
  6861. lecture: 'ニコニコ動画講座',
  6862. drive: '車載動画',
  6863. history: '歴史',
  6864. g_politics: '政治',
  6865. g_tech: '科学・技術',
  6866. science: '科学',
  6867. tech: 'ニコニコ技術部',
  6868. handcraft: 'ニコニコ手芸部',
  6869. make: '作ってみた',
  6870. g_culture2: 'アニメ・ゲーム・絵',
  6871. anime: 'アニメ',
  6872. game: 'ゲーム',
  6873. toho: '東方',
  6874. imas: 'アイドルマスター',
  6875. radio: 'ラジオ',
  6876. draw: '描いてみた',
  6877. g_other: 'その他',
  6878. are: '例のアレ',
  6879. diary: '日記',
  6880. other: 'その他',
  6881. r18: 'R-18'
  6882. },
  6883. termIdTable = {
  6884. 'hourly': 0,
  6885. 'daily': -1000,
  6886. 'weekly': -2000,
  6887. 'monthly': -3000,
  6888. 'total': -4000
  6889. },
  6890. termNameTable = {
  6891. 'hourly': '(毎時)',
  6892. 'daily': '(24時間)',
  6893. 'weekly': '(週間)',
  6894. 'monthly': '(月間)',
  6895. 'total': '(合計)'
  6896. },
  6897. idTermTable = {},
  6898. idGenreTable = {}
  6899. ;
  6900. if (conf.debugMode) { genreIdTable['r18'] = -170; }
  6901. for (var genre in genreIdTable) { idGenreTable[genreIdTable[genre]] = genre;}
  6902. for (var term in termIdTable ) { idTermTable [termIdTable [term ]] = term; }
  6903.  
  6904. /**
  6905. * ニコニコ動画ランキングのRSSをマイリストAPI互換のデータ形式に変換
  6906. */
  6907. var rss2mylist = function(xml) {
  6908. var
  6909. $x = $(xml),
  6910. title = $x.find('channel title:first').text(),
  6911. $items = $x.find('channel item'),
  6912. result = new DummyMylist({
  6913. name: title,
  6914. id: '-100'
  6915. });
  6916. $items.each(function() {
  6917. var video = parseRssItem($(this));
  6918. var item = new DummyMylistVideo({
  6919. id: video.id,
  6920. length: video.duration,
  6921. mylist_counter: video.mylistCnt,
  6922. view_counter: video.viewCnt,
  6923. num_res: video.resCnt,
  6924. first_retrieve: video.postedAt,
  6925. thumbnail_url: video.thumbnail,
  6926. title: video.title.replace(/^.*?第(\d+)位/, '第000$1位').replace(/^第\d+(\d{3})位/, '第$1位'),
  6927. _info: {first_retrieve: video.postedAt},
  6928. description_short: video.description.substring(0, 50)
  6929. });
  6930. result.push(item);
  6931. });
  6932. return result;
  6933. };
  6934.  
  6935. var parseRssItem = function($item) {
  6936. var
  6937. desc_cdata = $item.find('description').text(),
  6938. $desc = $('<div>' + desc_cdata + '</div>');
  6939. return {
  6940. title : $item.find('title') .text(),
  6941. id : $item.find('guid') .text().split('/').reverse()[0],
  6942. duration : $desc.find('.nico-info-length') .text(),
  6943. viewCnt : $desc.find('.nico-info-total-view') .text().replace(/,/g, ''),
  6944. resCnt : $desc.find('.nico-info-total-res') .text().replace(/,/g, ''),
  6945. mylistCnt : $desc.find('.nico-info-total-mylist').text().replace(/,/g, ''),
  6946. postedAt : $desc.find('.nico-info-date') .text()
  6947. .replace(/(年|月)/g, '-')
  6948. .replace(/:/g, ':')
  6949. .replace(/(日)/g, ''),
  6950. description : $desc.find('.nico-description') .text(),
  6951. thumbnail : $desc.find('.nico-thumbnail img').attr('src')
  6952. };
  6953. };
  6954.  
  6955. var pipeRequest = function(baseUrl, result, page, maxPage) {
  6956. var def = new $.Deferred(), p = def.promise();
  6957.  
  6958. var getPipe = function(result, url, page) {
  6959. return function() {
  6960. console.log('load RSS', url, page);
  6961. return $.ajax({
  6962. url: url,
  6963. timeout: 30000,
  6964. data: {rss: '2.0', lang: 'ja-jp', page: page},
  6965. beforeSend: function(xhr) {
  6966. xhr.setRequestHeader('X-Requested-With', {toString: function(){ return ''; }});
  6967. }
  6968. }).then(function(resp) {
  6969. var res = rss2mylist(resp);
  6970. for (var i = 0, len = res.rawData.list.length; i < len; i++) {
  6971. result.push(res.rawData.list[i]);
  6972. }
  6973. });
  6974. };
  6975. };
  6976.  
  6977. for (var i = page; i <= maxPage; i++) {
  6978. p = p.then(getPipe(result, baseUrl, i));
  6979. if (i < maxPage) { p = p.then(Util.Deferred.wait(300)); }
  6980. }
  6981.  
  6982. def.resolve();
  6983.  
  6984. return p;
  6985. };
  6986.  
  6987. var CACHE_TIME = 1000 * 60 * 30;
  6988. var request = function(baseUrl, page, maxPage) {
  6989. var def = new $.Deferred();
  6990. var cacheData = Util.Cache.get(baseUrl);
  6991. if (cacheData) {
  6992. return def.resolve(cacheData);
  6993. }
  6994.  
  6995. var result = new DummyMylist({
  6996. name: '総合ランキング',
  6997. id: '-100'
  6998. });
  6999.  
  7000. pipeRequest(baseUrl, result, page, maxPage).then(
  7001. function() {
  7002. def.resolve(Util.Cache.set(baseUrl, result, CACHE_TIME));
  7003. },
  7004. function() {
  7005. def.reject();
  7006. });
  7007.  
  7008. return def.promise();
  7009. };
  7010.  
  7011. var parseParam = function(param) {
  7012. var
  7013. id = parseInt(param.id || -100, 10),
  7014. genreId = getGenreId(id),
  7015. termId = getTermId(id),
  7016. category = idGenreTable[genreId] || 'all', type = 'fav', term = 'daily', lang= 'ja-jp',
  7017. viewPage = (param && typeof param.page === 'number') ? param.page : 1,
  7018. genreName = genreNameTable[category] || genreNameTable['all'],
  7019. maxRssPage = 1, sort = param.sort || '4';
  7020.  
  7021. term = idTermTable[termId] || idTermTable[0];
  7022. maxRssPage = (category === 'all' && term !== 'hourly') ? 3 : 1;
  7023. return {
  7024. genreId: genreId,
  7025. genreName: genreName,
  7026. category: category,
  7027. type: type,
  7028. term: term,
  7029. lang: lang,
  7030. viewPage: viewPage,
  7031. sort: sort,
  7032. maxRssPage: maxRssPage,
  7033. baseUrl:
  7034. '/ranking/'+ type +'/'+ term + '/'+ category //+'?rss=2.0&lang=' + lang
  7035. };
  7036. };
  7037.  
  7038. var loadRanking = function(param) {
  7039. var p = parseParam(param);
  7040. return request(p.baseUrl, 1, p.maxRssPage)
  7041. .then(function(result) {
  7042. result.name = p.genreName;
  7043. result.setPage(p.viewPage);
  7044.  
  7045. this.done(result);
  7046. });
  7047. };
  7048.  
  7049. var load = function(onload, param) {
  7050. var p = parseParam(param);
  7051. return request(p.baseUrl, 1, p.maxRssPage)
  7052. .then(function(result) {
  7053. result.name = p.genreName;
  7054. result.setPage(p.viewPage);
  7055.  
  7056. if (typeof onload === 'function') {
  7057. onload(result);
  7058. }
  7059. return this.done(result);
  7060. }, function() {
  7061. return this.fail({message: 'ランキングの取得に失敗しました', status: 'fail'});
  7062. });
  7063. };
  7064.  
  7065. var getTermId = function(t) {
  7066. if (typeof t === 'string') {
  7067. return termIdTable[t] || 0;
  7068. } else
  7069. if (typeof t === 'number'){
  7070. return (t - (t % 1000)) % 10000;
  7071. }
  7072. return 0;
  7073. };
  7074. var getGenreId = function(g, term) {
  7075. if (typeof g === 'string') {
  7076. return (genreIdTable[g] || 0) + getTermId(term);
  7077. } else
  7078. if (typeof g === 'number'){
  7079. return g % 1000;
  7080. } else {
  7081. return genreIdTable;
  7082. }
  7083. };
  7084. var getGenreName = function(g) {
  7085. if (typeof g === 'number' || (typeof g === 'string' && g.match(/^-?[0-9]+$/))) {
  7086. g = g % 1000;
  7087. var genre = idGenreTable[g];
  7088. return genreNameTable[genre];
  7089. } else
  7090. if (typeof g === 'string') {
  7091. return genreNameTable[g];
  7092. } else {
  7093. return genreNameTable;
  7094. }
  7095. };
  7096. var getCategory = function(g) {
  7097. if (typeof g === 'number') {
  7098. g = g % 1000;
  7099. return idGenreTable[g - (g %10)];
  7100. } else
  7101. if (typeof g === 'string') {
  7102. g = genreIdTable[g];
  7103. return idGenreTable[g - (g %10)];
  7104. } else {
  7105. return 'all';
  7106. }
  7107. };
  7108.  
  7109. var self = {
  7110. load: load,
  7111. getTermId: getTermId,
  7112. getGenreId: getGenreId,
  7113. getGenreName: getGenreName,
  7114. getCategory: getCategory
  7115. };
  7116. WatchItLater.VideoRanking = self;
  7117. return self;
  7118. })(w, Util);
  7119.  
  7120.  
  7121.  
  7122. /**
  7123. * チャンネル動画一覧をマイリストAPIと互換のある形式で返すことで、ダミーマイリストとして表示してしまう作戦
  7124. */
  7125. var ChannelVideoList = (function(w, Util){
  7126. if (!window.PlayerApp) return {};
  7127. var
  7128. CACHE_TIME = 1000 * 60 * 1, MAX_PAGE = 3,
  7129. getPipe = function(baseUrl, result, page) {
  7130. return function(hasPage) {
  7131. var def = new $.Deferred();
  7132. if (!hasPage) return def.resolve(hasPage);
  7133. var url = baseUrl + '?page=' + page;
  7134. console.log('load page', url);
  7135.  
  7136. $.ajax({url: url, timeout: 30000}).then(function(resp) {
  7137. var hasNextPage = parseItems(resp, result);
  7138. def.resolve(hasNextPage);
  7139. }, function(err) {
  7140. def.reject(err);
  7141. });
  7142. return def.promise();
  7143. };
  7144. },
  7145. pipeRequest = function(baseUrl, result) {
  7146. var def = new $.Deferred(), p = def.promise();
  7147.  
  7148. var maxPage = MAX_PAGE;
  7149. for (var i = 1; i <= maxPage; i++) {
  7150. p = p.then(getPipe(baseUrl, result, i));
  7151. if (i < maxPage) { p = p.then(Util.Deferred.wait(300)); }
  7152. }
  7153.  
  7154. p.then(function() {
  7155. this.done(result);
  7156. });
  7157.  
  7158. def.resolve(true);
  7159. return p;
  7160. },
  7161. load = function(callback, params) {
  7162. var myId, url, id, ownerName, def = new $.Deferred();
  7163. try{
  7164. id = params.id.toString().replace(/^ch/, '');
  7165. ownerName = params.ownerName;
  7166. url = 'http://ch.nicovideo.jp/channel/ch'+ id + '/video';
  7167. myId = WatchController.getMyUserId();
  7168. } catch (e) {
  7169. console.log(e);
  7170. throw { message: 'エラーが発生しました', status: 'fail'};
  7171. }
  7172.  
  7173. var CACHE_KEY = 'ch-' + id, cacheData = Util.Cache.get(CACHE_KEY);
  7174. if (cacheData) {
  7175. if (typeof callback === 'function') {
  7176. setTimeout(function() { callback(cacheData); } , 0);
  7177. }
  7178. return def.resolve(cacheData);
  7179. }
  7180.  
  7181.  
  7182. var result = new DummyMylist({
  7183. id: 'ch' + id,
  7184. sort: '1',
  7185. name: ownerName + 'の動画',
  7186. user_id: myId,
  7187. user_name: 'ニコニコ動画'
  7188. });
  7189.  
  7190. pipeRequest(url, result).then(function() {
  7191. Util.Cache.set(CACHE_KEY, result, CACHE_TIME);
  7192. if (typeof callback === 'function') callback(result);
  7193. def.resolve(result);
  7194. });
  7195.  
  7196. return def.promise();
  7197. },
  7198. parseItems = function(html, result) {
  7199. var $html = $(html), $list = $html.find('.contents_list .item');
  7200. var hasNextPage = false;
  7201. $list.each(function() {
  7202. var $item = $(this);
  7203. var id = $item.find('.title a').attr('href').split('/').reverse()[0];
  7204. var $counts = $item.find('.counts'), first_retrieve = $item.find('.time var').attr('title');
  7205. w.$item = $item;
  7206. result.push(new DummyMylistVideo({
  7207. id: id,
  7208. length: $item.find('.length').text(),
  7209. mylist_counter: $counts.find('.mylist var').text().split(',').join(''),
  7210. view_counter: $counts.find('.view var').text().split(',').join(''),
  7211. num_res: $counts.find('.comment var').text().split(',').join(''),
  7212. first_retrieve: first_retrieve,
  7213. thumbnail_url: $item.find('.lazyimage').data('original'),
  7214. title: $item.find('.title').text().trim(),
  7215. _info: {first_retrieve: first_retrieve, is_channel: true},
  7216. description_short: $item.find('.description').text().trim()
  7217. }));
  7218. });
  7219. if ($html.find('.pager .next:not(.disabled)').length > 0) {
  7220. hasNextPage = true;
  7221. }
  7222. return hasNextPage;
  7223. },
  7224. loadOwnerVideo = function(callback) {
  7225. if (!WatchController.isChannelVideo()) {
  7226. throw {message: 'チャンネル情報の取得に失敗しました', status: 'fail'};
  7227. }
  7228. var params = {
  7229. id: WatchController.getOwnerId(),
  7230. ownerName: WatchController.getOwnerName()
  7231. };
  7232. var def = new $.Deferred();
  7233. load(callback, params).then(function(result) {
  7234. if (typeof callback === 'function') callback(result);
  7235. def.resolve(result);
  7236. }, function() {
  7237. def.reject({message: 'チャンネル動画の取得に失敗しました', status: 'fail'});
  7238. });
  7239. return def.promise();
  7240. };
  7241.  
  7242. var self = {
  7243. load: load,
  7244. loadOwnerVideo: loadOwnerVideo
  7245. };
  7246. WatchItLater.ChannelVideo = self;
  7247. return self;
  7248. })(w, Util);
  7249.  
  7250.  
  7251.  
  7252. var niconicodoRedirect = function() {
  7253. // www.nicovideo.jp/stampを watchにパラメータを中継するためのクッションページとして使う。
  7254. // watchと同じドメインならどこでもいいけど、ここはDBアクセスもなさそうな静的ページため採用
  7255. var hash = location.hash.toString();
  7256. if (hash.indexOf('#json={') !== 0) {
  7257. return;
  7258. }
  7259. console.log('%cNiconicodo redirect', 'background: lightgreen;');
  7260.  
  7261. LocationHashParser.initialize();
  7262. var blankWatchId = 'sm20353707';
  7263. var redirectWatchId = LocationHashParser.getValue('redirectWatchId');
  7264. // 見た目が残念なので消す
  7265. document.body.innerHTML = '';
  7266.  
  7267. window.sessionStorage.setItem('watchItLater_redirectedHash', location.hash);
  7268. var redirectTo = '/watch/' + (redirectWatchId ? redirectWatchId : blankWatchId);
  7269. location.replace(redirectTo);
  7270. };
  7271.  
  7272.  
  7273. /**
  7274. * GINZAwatch上でのあれこれ
  7275. * 無計画に増築中
  7276. *
  7277. * watch.jsを解析すればわかる
  7278. *
  7279. */
  7280. var ZeroFunc = function(w) { // Zero Watch
  7281. var
  7282. video_id = '', watch_id = '',
  7283. // WatchApp = w.WatchApp, WatchJsApi = w.WatchJsApi,
  7284. isTouchActive = false,
  7285. console = conf.debugMode ? window.console : {log: _.noop, trace: _.noop, time: _.noop, timeEnd: _.noop},
  7286. watch = WatchApp.ns.init,
  7287. watchInfoModel = WatchApp.ns.model.WatchInfoModel.getInstance();
  7288. if (!WatchApp.mixin) {
  7289. WatchApp.mixin = _.mixin;
  7290. }
  7291.  
  7292. console.log('%cGinza', 'background: lightgreen;');
  7293.  
  7294. /**
  7295. * ゆっくり再生(スロー再生)メニュー
  7296. */
  7297. var Yukkuri = (function($, conf, w) {
  7298. var self, $content = null, $button = null, timer = null, cnt = 0, isActive = false;
  7299.  
  7300. function createDom() {
  7301. $content = $('<div id="yukkuriPanel" />');
  7302. $button = $('<button>yu</button>').addClass('yukkuriButton').attr({title: 'ゆっくり(スロー再生)'});
  7303. $button.click(function() {
  7304. toggleActive();
  7305. });
  7306. $content.append($button);
  7307.  
  7308. $('body').append($content);
  7309. }
  7310.  
  7311. function show() {
  7312. if ($content === null) {
  7313. createDom();
  7314. }
  7315. updateView();
  7316. $content.show();
  7317. }
  7318. function hide() {
  7319. $content.hide();
  7320. }
  7321. function updateView() {
  7322. $button.toggleClass('active', isActive);
  7323. }
  7324.  
  7325. function start() {
  7326. if (timer !== null) {
  7327. clearInterval(timer);
  7328. }
  7329. isActive = true;
  7330. updateView();
  7331. timer = setInterval(function() {
  7332. var v = cnt++ % 4;
  7333. if (v === 0) {
  7334. WatchController.play();
  7335. } else
  7336. if (v === 1) {
  7337. WatchController.pause();
  7338. }
  7339. }, 20);
  7340. }
  7341. function stop() {
  7342. if (timer !== null) {
  7343. clearInterval(timer);
  7344. timer = null;
  7345. }
  7346. isActive = false;
  7347. updateView();
  7348. WatchController.pause();
  7349. }
  7350.  
  7351. function toggleActive() {
  7352. if (isActive) {
  7353. stop();
  7354. } else {
  7355. start();
  7356. }
  7357. return isActive;
  7358. }
  7359.  
  7360. self = {
  7361. show: show,
  7362. hide: hide,
  7363. start: start,
  7364. stop: stop
  7365. };
  7366. return self;
  7367. })($, conf, w);
  7368.  
  7369. function onWindowResizeEnd() {
  7370. setTimeout(function() {
  7371. EventDispatcher.dispatch('onWindowResizeEnd');
  7372. }, 1000);
  7373. }
  7374.  
  7375. /**
  7376. * デフォルトの市場貼付ボタンはなぜかページの一番上までスクロールするという意地悪な仕様だが、
  7377. * こっちはがんばって見やすい位置に調整して開く
  7378. */
  7379. function ichibaSearch(word, shopCode) {
  7380. var wait = 10, opened = false;
  7381. //shopCode = shopCode || 'az'; // az = amazon
  7382. var search = function() {
  7383. if ($('#ichibaConsole').is(':visible')) {
  7384. setTimeout(function() {
  7385. w.WatchApp.ns.util.WindowUtil.scrollFitMinimum('#ichibaConsole', 300);
  7386. }, 1000);
  7387. if (!word) {
  7388. return;
  7389. }
  7390. if ($('#ichiba_search_form_query').is(':visible')) {
  7391. $('#ichiba_search_form_query').val(word);
  7392. w.ichiba.search(shopCode, 0, 'all');
  7393. setTimeout(function() {$('#ichiba_search_form_query').focus();}, 1000);
  7394. } else {
  7395. if (!opened) {
  7396. if(shopCode) { w.ichiba.showRelatedTagItems(shopCode, 0, 'all'); }
  7397. opened = true;
  7398. }
  7399. if (wait-- > 0) setTimeout(search, 1000);
  7400. }
  7401. } else {
  7402. if (wait-- > 0) setTimeout(search, 1000);
  7403. }
  7404. };
  7405. search();
  7406. w.ichiba.showConsole();
  7407. }
  7408. WatchController.ichibaSearch = ichibaSearch;
  7409.  
  7410. function initVideoCounter() {
  7411. var
  7412. playerAreaConnector = watch.PlayerInitializer.playerAreaConnector,
  7413. counter = {mylistCount: 0, viewCount: 0, commentCount: 0},
  7414. blinkItem = function($elm) {
  7415. $elm.removeClass('animateBlink').addClass('blink');
  7416. setTimeout(function() {
  7417. $elm.addClass('animateBlink').removeClass('blink');
  7418. $elm = null;
  7419. }, 500);
  7420. };
  7421. var setVideoCounter = function(watchId, title) {
  7422. var $tpl = $(
  7423. '<span>再生: <span class="viewCountDiff videoCountDiff"></span><span class="viewCount videoCount"></span> コメ: <span class="commentCountDiff videoCountDiff"></span><span class="commentCount videoCount"></span> マイ: <span class="mylistCountDiff videoCountDiff"></span><span class="mylistCount videoCount"></span></span>'
  7424. );
  7425. assignVideoCountToDom($tpl, counter);
  7426.  
  7427. if ((conf.popupViewCounter === 'always') ||
  7428. (conf.popupViewCounter === 'full' && $('body').hasClass('full_with_browser'))
  7429. ) {
  7430. Popup.show(
  7431. $('<div/>')
  7432. .append(
  7433. $('<a/>')
  7434. .text(window._.unescape(title))
  7435. .attr('href', 'http://nico.ms/' + watchId)
  7436. )
  7437. .html() +
  7438. '<br/><span style="margin-left:10px; font-size: 90%;">'+ $tpl.html() + '</span>'
  7439. );
  7440. }
  7441. $('#trueBrowserFullShield').html([
  7442. '<img class="ownerIcon" src="', WatchController.getOwnerIcon(), '">',
  7443. '<div class="title">', title, '</div>',
  7444. '<p class="postedAt">',$('.videoPostedAt:last').text(), '</p>',
  7445. '<p class="videoCounter">', $tpl.html(), '</p>',
  7446. ''].join(''))
  7447. .toggleClass('favorite', WatchController.isFavoriteOwner())
  7448. .find('img').attr('title', WatchController.getOwnerName());
  7449.  
  7450. if (conf.headerViewCounter) {
  7451. var vc = $('#videoCounter');
  7452. if (vc.length < 1) {
  7453. var li = $('<li></li>')[0];
  7454. li.id = 'videoCounter';
  7455. $('#siteHeaderLeftMenu').after(li);
  7456. vc = $('#videoCounter');
  7457. }
  7458. vc.empty().append($tpl);
  7459. }
  7460. };
  7461.  
  7462. playerAreaConnector.addEventListener('onWatchCountUpdated', function(c) {
  7463. var diff = c - counter.viewCount;
  7464. if (diff === 0) return;
  7465. counter.viewCount = c;
  7466. EventDispatcher.dispatch('onVideoCountUpdated', counter, 'viewCount', diff);
  7467. });
  7468. playerAreaConnector.addEventListener('onCommentCountUpdated', function(c) {
  7469. var diff = c - counter.commentCount;
  7470. if (diff === 0) return;
  7471. counter.commentCount = c;
  7472. EventDispatcher.dispatch('onVideoCountUpdated', counter, 'commentCount', diff);
  7473. });
  7474. playerAreaConnector.addEventListener('onMylistCountUpdated', function(c) {
  7475. var diff = c - counter.mylistCount;
  7476. if (diff === 0) return;
  7477. counter.mylistCount = c;
  7478. EventDispatcher.dispatch('onVideoCountUpdated', counter, 'mylistCount', diff);
  7479. });
  7480.  
  7481. EventDispatcher.addEventListener('onWatchInfoReset', function(watchInfoModel){
  7482. counter.mylistCount = watchInfoModel.mylistCount;
  7483. counter.viewCount = watchInfoModel.viewCount;
  7484. counter.commentCount = watchInfoModel.commentCount;
  7485.  
  7486. setVideoCounter(watchInfoModel.v, watchInfoModel.title);
  7487. });
  7488. EventDispatcher.addEventListener('onVideoCountUpdated', function(c, type, diff) {
  7489. var $target = $('.sidePanel .videoInfo, #trueBrowserFullShield, #videoCounter');
  7490. assignVideoCountToDom($target, c);
  7491. $target.find('.' + type + 'Diff').text(diff).toggleClass('down', diff < 0);
  7492. blinkItem($target.find('.' + type + ', .' + type + 'Diff'));
  7493. });
  7494.  
  7495. } //
  7496.  
  7497. var isFirst = true;
  7498. function onVideoInitialized() {
  7499. watch = WatchApp.ns.init;
  7500. AnchorHoverPopup.hidePopup().updateNow();
  7501. tagv = watch.TagInitializer.tagViewController;
  7502. WatchCounter.add();
  7503.  
  7504. if (isFirst) {
  7505. if (conf.autoPlayIfWindowActive === 'yes' && w.document.hasFocus()) {
  7506. // ウィンドウがアクティブの時だけ自動再生する。 複数タブ開いてるときは便利
  7507. setTimeout(function() { WatchController.play(); }, 2000);
  7508. }
  7509.  
  7510. if (isFirst && conf.commentVisibility !== 'visible') {
  7511. if (conf.commentVisibility === 'hidden') {
  7512. console.log('comment off');
  7513. WatchController.commentVisibility(false);
  7514. } else {
  7515. console.log('last state', conf.lastCommentVisibility);
  7516. WatchController.commentVisibility(conf.lastCommentVisibility === 'visible');
  7517. }
  7518. }
  7519. EventDispatcher.dispatch('onFirstVideoInitialized');
  7520. }
  7521.  
  7522. EventDispatcher.dispatch('onVideoInitialized', isFirst);
  7523. isFirst = false;
  7524. } //
  7525.  
  7526. function onVideoChangeStatusUpdated(isChanging) {
  7527. AnchorHoverPopup.hidePopup();
  7528. if (isChanging) {
  7529. $('.sidePanel .sideVideoInfo').removeClass('show');
  7530. }
  7531. if ((conf.enableAutoPlaybackContinue || conf.debugMode) && watch.PlayerInitializer.noUserOperationController.autoPlaybackModel._isAutoPlayback) {
  7532. watch.PlayerInitializer.noUserOperationController.autoPlaybackModel.setCount(0);
  7533. }
  7534. EventDispatcher.dispatch('onVideoChangeStatusUpdated', isChanging);
  7535. }
  7536.  
  7537. var $sideInfoPanelTemplate = $([
  7538. '<div class="sideVideoInfoInner">',
  7539.  
  7540. '<div class="videoTitleContainer"><h3 class="videoTitle"></h3></div>',
  7541. '<div class="videoOwnerInfoContainer">',
  7542. '<div class="channelIconContainer"><a target="_blank" class="channelIconLink">',
  7543. '<img class="channelIcon"></a>',
  7544. '<span class="channelName">提供: ',
  7545. '<a class="showOtherVideos" target="_blank"><span class="channelNameInner"></span></a></span>',
  7546. '</span>',
  7547. '</div>',
  7548. '<div class="userIconContainer"><a target="_blank" class="userIconLink">',
  7549. '<img class="userIcon"></a>',
  7550. '<span class="userName">投稿者: ',
  7551. '<span class="userNameInner notPublic"></span>',
  7552. '<span class="isPublic"><a class="showOtherVideos"><span class="userNameInner"></span></a></span>',
  7553. '</span>',
  7554. '</div>',
  7555. '</div>',
  7556. '<div class="videoInfo">',
  7557. '<span class="videoPostedAt"></span>',
  7558. '<ul class="videoStats">',
  7559. '<li style="position: relative;">再生: <span class="viewCountDiff videoCountDiff"></span><span class="videoCount viewCount"></span></li>',
  7560. '<li style="position: relative;">コメント: <span class="commentCountDiff videoCountDiff"></span><span class="videoCount commentCount"></span></li>',
  7561. '<li style="position: relative;">マイリスト: <span class="mylistCountDiff videoCountDiff"></span><span class="videoCount mylistCount"></span></li>',
  7562. '</ul>',
  7563. '</div>',
  7564.  
  7565.  
  7566. '<div class="videoThumbnailContainer" style="display: none;">',
  7567. '<img class="videoThumbnailImage">',
  7568. '</div>',
  7569. '<div class="videoDetails">',
  7570. '<div class="videoDescription">',
  7571. '<div class="videoDescriptionInner">',
  7572. '</div>',
  7573. '</div>',
  7574. '</div>',
  7575. '</div>',
  7576. ''].join(''));
  7577.  
  7578.  
  7579. // - 左パネル乗っ取る
  7580. function initLeftPanel($, conf, w) {
  7581.  
  7582. var $tab = $([
  7583. '<ul id="leftPanelTabContainer">',
  7584. '<li class="tab ichiba" data-selection="ichiba" >市場</li>',
  7585. '<li class="tab videoInfo" data-selection="videoInfo">情報</li>',
  7586. '</ul>'].join(''));
  7587.  
  7588. var
  7589. $sidePanel = $('<div id="leftPanel" />').addClass('sidePanel'),
  7590. $infoPanel = $('<div/>').attr({'id': 'leftVideoInfo', 'class': 'sideVideoInfo sidePanelInner'}),
  7591. $ichibaPanel = $('<div/>').attr({'id': 'leftIchibaPanel', 'class': 'sideIchibaPanel sidePanelInner'});
  7592. $sidePanel.append($tab).append($infoPanel).append($ichibaPanel);
  7593. $('#playerTabWrapper').after($sidePanel);
  7594.  
  7595. var
  7596. onTabSelect = function(e) {
  7597. e.preventDefault();
  7598. AnchorHoverPopup.hidePopup();
  7599. var selection = $(e.target).attr('data-selection');
  7600. if (typeof selection === 'string') {
  7601. conf.setValue('lastLeftTab', selection);
  7602. changeTab(selection);
  7603. }
  7604. },
  7605. changeTab = function(selection) {
  7606. $sidePanel.removeClass('videoInfo ichiba').addClass(selection);
  7607. if (selection === 'ichiba') {
  7608. resetIchiba(false);
  7609. }
  7610. },
  7611. lastIchibaVideoId = '',
  7612. resetIchiba = function(force) {
  7613. var videoId = watchInfoModel.id;
  7614. if (lastIchibaVideoId === videoId && !force) {
  7615. return;
  7616. }
  7617. lastIchibaVideoId = videoId;
  7618. resetSideIchibaPanel($ichibaPanel, true);
  7619. },
  7620. resetScroll = function() {
  7621. $(this).animate({scrollTop: 0}, 600);
  7622. };
  7623.  
  7624. $infoPanel .on('dblclick', resetScroll);
  7625. $ichibaPanel.on('dblclick', resetScroll);
  7626.  
  7627. $tab.on('click', onTabSelect).on('touchend', onTabSelect);
  7628. changeTab(conf.lastLeftTab);
  7629.  
  7630. var refreshPanel = function(isFirst) {
  7631. if (isFirst) { return; }
  7632.  
  7633. sidePanelRefresh($infoPanel, $ichibaPanel, $sidePanel, $sideInfoPanelTemplate.clone());
  7634. if ($ichibaPanel.is(':visible')) {
  7635. resetIchiba(true);
  7636. }
  7637. };
  7638. EventDispatcher.addEventListener('onVideoInitialized', refreshPanel);
  7639. refreshPanel();
  7640.  
  7641. } // end of initLeftPanel
  7642.  
  7643. function initRightPanel($, conf, w) {
  7644. var $rightPanel = $('#playerTabWrapper').addClass('sidePanel');
  7645. initRightPanelVerticalTab($rightPanel);
  7646. initRightPanelHorizontalTab($, conf, w);
  7647. var $playerTabWrapper = $rightPanel, wideCss = null;
  7648. var
  7649. createWideCommentPanelCss = function (targetWidth) {
  7650. var px = targetWidth - $rightPanel.outerWidth();
  7651. var elms = [
  7652. '#playerTabWrapper', //'#playerTabWrapper',
  7653. '#commentDefaultHeader',
  7654. '#playerCommentPanel .commentTable',
  7655. '#playerCommentPanel .commentTable .commentTableContainer'
  7656. ];
  7657. var css = [
  7658. 'body.videoExplorer #content.w_adjusted #playerTabWrapper { width: ', targetWidth,'px; }\n',
  7659. 'body:not(.full_with_browser) .w_wide #playerTabWrapper { width: ', targetWidth,'px; }\n',
  7660. 'body:not(.videoExplorer):not(.full_with_browser) .w_wide #playerAlignmentArea { width: 1100px; }\n', // 960 + 140
  7661. 'body:not(.videoExplorer):not(.full_with_browser) .w_wide #playerAlignmentArea.size_normal { width: 1326px; }\n\n' // 1186 + 140
  7662. ];
  7663. for (var v in elms) {
  7664. var $e = $(elms[v]), newWidth = $e.width() + px;
  7665. css.push([
  7666. '.w_wide #playerTabWrapper ', elms[v],
  7667. ' , body.videoExplorer #content.w_adjusted ',
  7668. elms[v], '\n{ width: ', newWidth,'px !important; }\n\n'
  7669. ].join(''));
  7670. }
  7671. wideCss = addStyle(css.join(''), 'wideCommentPanelCss');
  7672. console.log(css.join(''));
  7673. },
  7674. toggleWide = function(v) {
  7675. $('#content').toggleClass('w_wide', v);
  7676. EventDispatcher.dispatch('onWindowResizeEnd');
  7677. };
  7678.  
  7679. var wideCommentPanelCss = Util.here(function() {/*
  7680. body.videoExplorer #content.w_adjusted #playerTabWrapper { width: 420px; }
  7681. body:not(.full_with_browser) .w_wide #playerTabWrapper { width: 420px; }
  7682.  
  7683. body:not(.videoExplorer):not(.full_with_browser) .w_wide #playerAlignmentArea { width: 1100px; }
  7684. body:not(.videoExplorer):not(.full_with_browser) .w_wide #playerAlignmentArea.size_normal { width: 1326px; }
  7685.  
  7686. body:not(.full_with_browser) .w_wide #playerTabWrapper #playerTabWrapper,
  7687. body.videoExplorer #content.w_adjusted #playerTabWrapper
  7688. { width: 420px !important; }
  7689.  
  7690. body:not(.full_with_browser) .w_wide #playerTabWrapper #commentDefaultHeader,
  7691. body.videoExplorer #content.w_adjusted #commentDefaultHeader
  7692. { width: 408px !important; }
  7693.  
  7694. body:not(.full_with_browser) .w_wide #playerTabWrapper #playerCommentPanel .commentTable,
  7695. body.videoExplorer #content.w_adjusted #playerCommentPanel .commentTable
  7696. { width: 406px !important; }
  7697.  
  7698. body:not(.full_with_browser) .w_wide #playerTabWrapper #playerCommentPanel .commentTable .commentTableContainer,
  7699. body.videoExplorer #content.w_adjusted #playerCommentPanel .commentTable .commentTableContainer
  7700. { width: 406px !important; }
  7701. */});
  7702. addStyle(wideCommentPanelCss, 'wideCommentPanelCss');
  7703.  
  7704. EventDispatcher.addEventListener('on.config.wideCommentPanel', toggleWide);
  7705. toggleWide(!!conf.wideCommentPanel);
  7706.  
  7707. EventDispatcher.addEventListener('onFirstVideoInitialized', function() {
  7708.  
  7709. //EventDispatcher.dispatch('onWindowResizeEnd');
  7710. //createWideCommentPanelCss(420);
  7711.  
  7712. var $div = $([
  7713. '<div id="sharedNgSettingContainer" style="display: none;">NG共有: ',
  7714. '<select id="sharedNgSetting">',
  7715. '<option value="HIGH">高</option>',
  7716. '<option value="MIDDLE">中</option>',
  7717. '<option value="LOW">低</option>',
  7718. '<option value="NONE">無</option>',
  7719. '</select>',
  7720. '</div>',
  7721. ''].join('')), $ngs = $div.find('select');
  7722.  
  7723. $ngs
  7724. .val(watch.PlayerInitializer.nicoPlayerConnector.playerConfig.get().ngScoringFilteringLevel)
  7725. .on('change', function() {
  7726. var val = this.value;
  7727. watch.PlayerInitializer.nicoPlayerConnector.playerConfig.set({ngScoringFilteringLevel: this.value});
  7728. });
  7729. $('#commentDefaultHeader').append($div);
  7730.  
  7731. EventDispatcher.addEventListener('on.config.enableSharedNgSetting', function(newValue, oldValue) {
  7732. if (newValue) {
  7733. $div.show();
  7734. } else {
  7735. $div.hide();
  7736. }
  7737. });
  7738. if (conf.enableSharedNgSetting) { $div.show(); }
  7739. });
  7740.  
  7741. if (conf.removeCommentPanelHoverEvent) {
  7742. $("#commentDefault").find(".commentTableContainerInner") .off('mouseover').off('mouseenter').off('mouseleave').off('mouseout');
  7743. $('#playerCommentPanel .section .commentTable .commentTableContainer') .off('mouseover').off('mouseenter').off('mouseleave').off('mouseout');
  7744. watch.PlayerInitializer.commentPanelViewController.commentContent.$commentTableContainer
  7745. .off('contextmenu')
  7746. .on('contextmenu', '.cell',
  7747. $.proxy(Util.Closure.commentPanelContextMenu(), watch.PlayerInitializer.commentPanelViewController.commentContent)
  7748. );
  7749. }
  7750. EventDispatcher.addEventListener('onVideoChangeStatusUpdated', function(isChanging) {
  7751. if (isChanging) {
  7752. watch.PlayerInitializer.commentPanelViewController.commentContent.$commentTableContainer
  7753. .find('.cell').off();
  7754. }
  7755. });
  7756. } // end initRightPanel
  7757.  
  7758. function initRightPanelHorizontalTab($, conf, w) {
  7759. } //
  7760.  
  7761. function initRightPanelVerticalTab($sidePanel) {
  7762. if (!conf.rightPanelJack) { return; }
  7763.  
  7764. var $tab = $([
  7765. '<ul id="sidePanelTabContainer">',
  7766. '<li class="tab comment" data-selection="w_comment" >コメント</li>',
  7767. '<li class="tab videoInfo" data-selection="w_videoInfo">動画情報</li>',
  7768. '<li class="tab ichiba" data-selection="w_ichiba" >ニコニコ市場</li>',
  7769. '<li class="tab review" data-selection="w_review" >レビュー</li>',
  7770. '</ul>'].join(''));
  7771.  
  7772. var $infoPanel = $('<div/>').attr({'id': 'rightVideoInfo', 'class': 'sideVideoInfo sidePanelInner'});
  7773. var $ichibaPanel = $('<div/>').attr({'id': 'rightIchibaPanel', 'class': 'sideIchibaPanel sidePanelInner'});
  7774. var $reviewPanel = $('<div/>').attr({'id': 'rightReviewPanel', 'class': 'sideReviewPanel sidePanelInner'});
  7775. $sidePanel.append($tab).append($infoPanel).append($ichibaPanel).append($reviewPanel);
  7776.  
  7777. var
  7778. onTabSelect = function(e) {
  7779. e.preventDefault();
  7780. AnchorHoverPopup.hidePopup();
  7781. var selection = $(e.target).attr('data-selection');
  7782. if (typeof selection === 'string') {
  7783. if (WatchController.isSearchMode()) {
  7784. conf.setValue('lastRightTabInExplorer', selection);
  7785. } else {
  7786. conf.setValue('lastRightTab', selection);
  7787. }
  7788. changeTab(selection);
  7789. }
  7790. },
  7791. $videoReview = $('#videoReview'),
  7792. toggleReview = function(f) {
  7793. if (f) {
  7794. $reviewPanel.append($videoReview);
  7795. } else {
  7796. $('#playerBottomAd').after($videoReview);
  7797. }
  7798. },
  7799. changeTab = function(selection) {
  7800. if ($sidePanel.hasClass('w_review') && selection !== 'w_review') {
  7801. toggleReview(false);
  7802. }
  7803. $sidePanel.removeClass('w_videoInfo w_comment w_ichiba w_review').addClass(selection);
  7804. if (selection === 'w_ichiba') {
  7805. resetIchiba(false);
  7806. } else
  7807. if (selection === 'w_review') {
  7808. toggleReview(true);
  7809. } else
  7810. if (selection === 'w_comment') {
  7811. setTimeout(function() {
  7812. watch.PlayerInitializer.commentPanelViewController.contentManager.activeContent().refresh();
  7813. }, 500);
  7814. }
  7815. return changeTab;
  7816. },
  7817. lastIchibaVideoId = '', resetIchiba = function(force) {
  7818. var videoId = watchInfoModel.id;
  7819. if (lastIchibaVideoId === videoId && !force) {
  7820. return;
  7821. }
  7822. lastIchibaVideoId = videoId;
  7823. resetSideIchibaPanel($ichibaPanel, true);
  7824. },
  7825. resetScroll = function() {
  7826. $(this).animate({scrollTop: 0}, 600);
  7827. };
  7828.  
  7829. $infoPanel .on('dblclick', resetScroll);
  7830. $ichibaPanel.on('dblclick', resetScroll);
  7831. $reviewPanel.on('dblclick', resetScroll);
  7832.  
  7833. $tab.on('click', onTabSelect).on('touchend', onTabSelect);
  7834. changeTab(conf.lastRightTab);
  7835.  
  7836. EventDispatcher.addEventListener('onVideoExplorerOpening', function() {
  7837. changeTab('w_comment');
  7838. });
  7839. EventDispatcher.addEventListener('onVideoExplorerClosing', function() {
  7840. changeTab(conf.lastRightTab);
  7841. });
  7842.  
  7843. var onOuterResize = function() {
  7844. var $body = $('body'), $right = $('#playerTabWrapper');
  7845. if (WatchController.isSearchMode() || $body.hasClass('full_with_browser')) { return; }
  7846. var w = $('#external_nicoplayer').outerWidth(), margin = 124;
  7847. w += $right.is(':visible') ? $right.outerWidth() : 0;
  7848. $('#sidePanelTabContainer').toggleClass('left', (window.innerWidth - w - margin < 0));
  7849. };
  7850. EventDispatcher.addEventListener('onWindowResizeEnd', onOuterResize);
  7851. EventDispatcher.addEventListener('onPlayerAlignmentAreaResize', onOuterResize);
  7852.  
  7853. var refreshPanel = function(isFirst) {
  7854.  
  7855. window.setTimeout(function() {
  7856. $sidePanel
  7857. .toggleClass('reviewEmpty', $('#videoReview').find('.stream').length < 1)
  7858. .toggleClass('ichibaEmpty', WatchController.isIchibaEmpty());
  7859. }, 2000);
  7860.  
  7861. if (isFirst) { return; }
  7862.  
  7863. sidePanelRefresh($infoPanel, $ichibaPanel, $sidePanel, $sideInfoPanelTemplate.clone());
  7864. if ($ichibaPanel.is(':visible')) {
  7865. resetIchiba(true);
  7866. }
  7867. };
  7868. EventDispatcher.addEventListener('onVideoInitialized', refreshPanel);
  7869.  
  7870. refreshPanel();
  7871.  
  7872. } // end of initRightPanelVerticalTab
  7873.  
  7874.  
  7875. function assignVideoCountToDom($tpl, count) {
  7876. var addComma = WatchApp.ns.util.StringUtil.addComma;
  7877. $tpl
  7878. .find('.viewCount' ).text(addComma(count.viewCount )).end()
  7879. .find('.commentCount').text(addComma(count.commentCount)).end()
  7880. .find('.mylistCount' ).text(addComma(count.mylistCount ));
  7881. return $tpl;
  7882. } //
  7883.  
  7884. function sidePanelRefresh($sideInfoPanel, $ichibaPanel, $sidePanel, $template) {
  7885. var isFavorite = WatchController.isFavoriteOwner();
  7886. //var h = $sideInfoPanel.innerHeight() - 100;
  7887.  
  7888. $template.find('.videoTitle').html(watchInfoModel.title);
  7889.  
  7890. assignVideoCountToDom($template, watchInfoModel);
  7891. $template.find('.videoPostedAt').text($('.videoPostedAt:last').text());
  7892.  
  7893. var $videoDescription = $template.find('.videoDescription');
  7894.  
  7895. $videoDescription.find('.videoDescriptionInner').append(create$videoDescription(watchInfoModel.description));
  7896.  
  7897. var $userIconContainer = $template.find('.userIconContainer');
  7898. var $channelIconContainer = $template.find('.channelIconContainer');
  7899.  
  7900. var info = WatchController.getOwnerInfo();
  7901. if (info.type === 'channel') {
  7902. if (info.id && info.id !== '0') {
  7903. $channelIconContainer
  7904. .find('.channelIcon')
  7905. .attr({'src': info.icon}).end()
  7906. .find('.channelIconLink')
  7907. .attr({'href': info.page})
  7908. .on('click', Util.Closure.openVideoOwnersVideo()).end()
  7909. .find('.channelNameInner')
  7910. .text(info.name).end()
  7911. .find('.showOtherVideos')
  7912. .attr({'href': info.page})
  7913. .on('click', Util.Closure.openVideoOwnersVideo());
  7914. }
  7915. $userIconContainer.remove();
  7916. } else {
  7917. if (info.id && info.id !== '0') { // ユーザーが退会してたりすると情報が無いのでチェックしてから
  7918. $userIconContainer
  7919. .find('.userIcon')
  7920. .attr({'src': info.icon}).end()
  7921. .find('.userIconLink')
  7922. .attr({'href': info.page})
  7923. .on('click', Util.Closure.openVideoOwnersNicorepo()).end()
  7924. .find('.userNameInner')
  7925. .text(info.name).end()
  7926. .find('.showOtherVideos')
  7927. .attr({'href': info.page + '/video'})
  7928. .on('click', Util.Closure.openVideoOwnersVideo() ).end()
  7929. .toggleClass('isUserVideoPublic', info.isVideoPublic);
  7930. $channelIconContainer.remove();
  7931. } else {
  7932. $userIconContainer.remove();
  7933. $channelIconContainer.remove();
  7934. }
  7935. }
  7936.  
  7937. $sideInfoPanel.find('*').unbind();
  7938.  
  7939. $sidePanel
  7940. .toggleClass('ichibaEmpty', WatchController.isIchibaEmpty());
  7941.  
  7942. $sideInfoPanel
  7943. .empty()
  7944. .scrollTop(0)
  7945. .toggleClass('isFavorite', isFavorite)
  7946. .toggleClass('isChannel', WatchController.isChannelVideo())
  7947. .append($template);
  7948.  
  7949. window.setTimeout(function() {
  7950. $sideInfoPanel.addClass('show');
  7951. $sideInfoPanel = $ichibaPanel = $sidePanel = $template =
  7952. $videoDescription = $userIconContainer =
  7953. $channelIconContainer = null;
  7954. }, 100);
  7955.  
  7956. } // end of sidePanelRefresh
  7957.  
  7958. /**
  7959. * 説明文中の動画リンク類を加工
  7960. */
  7961. function decorateVideoDescriptionLink($description) {
  7962. var watchLinks = [], watchIds = [];
  7963. var videoReg = /\/watch\/((sm|nm|so|)\d+)$/;
  7964. var seigaReg = /seiga\/im(\d+)/;
  7965. $description.find('a').each(function() {
  7966. var url = this.href, text, $this = $(this);
  7967. if (videoReg.test(url)) {
  7968. var watchId = RegExp.$1;
  7969. var $nextButton = $([
  7970. '<div class="nextPlayButton" title="次に再生" onclick="WatchItLater.WatchController.insertVideoToPlaylist(\'', watchId, '\')">次に再生</div>',
  7971. ''].join(''));
  7972. $this.after($nextButton);
  7973.  
  7974. watchLinks.push({id: watchId, $target: $nextButton});
  7975. watchIds.push(watchId);
  7976. } else if (seigaReg.test(url)) {
  7977. var illustId = RegExp.$1;
  7978. var $thumbnail = $([
  7979. '<div class="descriptionThumbnail illust">',
  7980. '<img src="http://lohas.nicoseiga.jp/thumb/',
  7981. illustId,
  7982. 'z" onclick="WatchItLater.WatchController.showLargeThumbnail(this.src);">',
  7983. '</div>',
  7984. ''].join(''));
  7985. $this.after($thumbnail);
  7986. }
  7987. });
  7988.  
  7989. if (conf.enableDescriptionThumbnail && watchIds.length > 0) {
  7990. var ac = function(s) {
  7991. s = parseInt(s, 10);
  7992. s = s < 1 ? '-' : s;
  7993. return '<span class="count">' + WatchApp.ns.util.StringUtil.addComma(s) + '</span>';
  7994. };
  7995. var onWatchIdInfoReady = function(result) {
  7996. $(watchLinks).each(function(i, watchLink) {
  7997. var id = watchLink.id, $target = watchLink.$target;
  7998. if (result[id]) {
  7999. var info = result[id];
  8000. var $thmb = $([
  8001. '<div class="descriptionThumbnail video">',
  8002. '<img src="', info.thumbnail_url, '" onclick="WatchItLater.WatchController.showLargeThumbnail(this.src);">',
  8003. '<span class="uploadAt">', info.first_retrieve ,' 投稿</span>',
  8004. '<p>', info.title, ' (', info.length, ')</p>',
  8005. '<div class="counterContainer">',
  8006. '<span class="view">再生: ', ac(info.view_counter) ,'</span> ',
  8007. '<span class="comment">コメ: ', ac(info.num_res) ,'</span> ',
  8008. '<span class="mylist">マイ: ', ac(info.mylist_counter),'</span>',
  8009. '</div>',
  8010. '</div>'].join(''));
  8011. $target.after($thmb);
  8012. }
  8013. $target = watchLink = null;
  8014. });
  8015. watchIds = watchLinks = null;
  8016. };
  8017. var onWatchIdInfoFail = function() {
  8018. watchIds = watchLinks = null;
  8019. };
  8020. window.setTimeout(function() {
  8021. window.WatchItLater.loader.videoArrayAPILoader.load(watchIds).then(onWatchIdInfoReady, onWatchIdInfoFail);
  8022. }, 1000);
  8023. } else {
  8024. watchIds = watchLinks = null;
  8025. }
  8026. $description = null;
  8027. }
  8028.  
  8029. /**
  8030. * 動画説明文のクリックイベント類を割り当てる
  8031. */
  8032. function bindDescriptionEvents($description) {
  8033. $description.on('click', function(e) {
  8034. if (e.button !== 0 || e.metaKey || e.shiftKey || e.altKey || e.ctrlKey) { return; }
  8035.  
  8036. var elm = e.target;
  8037. if (elm.tagName !== 'A') { return; }
  8038. if (elm.className === 'otherSite') return;
  8039.  
  8040. var $elm = $(elm);
  8041.  
  8042. if (elm.textContent.indexOf('mylist/') === 0) {
  8043. e.preventDefault(); e.stopPropagation();
  8044. var mylistId = elm.textContent.split('/').reverse()[0];
  8045.  
  8046. WatchController.showMylist(mylistId);
  8047. } else
  8048. if (elm.className === 'seekTime') {
  8049. e.preventDefault(); e.stopPropagation();
  8050. var data = $elm.attr('data-seekTime').split(":"),
  8051. vpos = (data[0] * 60 + parseInt(data[1], 10)) * 1000;
  8052. WatchController.vpos(vpos);
  8053. }
  8054. });
  8055. $description.find('.watch').unbind('click');
  8056. $description = null;
  8057. }
  8058.  
  8059. function create$videoDescription(html) {
  8060. var linkmatch = /<a.*?<\/a>/, links = [], n;
  8061. html = html.split('<br />').join(' <br /> ');
  8062. while ((n = linkmatch.exec(html)) !== null) {
  8063. links.push(n);
  8064. html = html.replace(n, ' <!----> ');
  8065. }
  8066.  
  8067. // (htttp://example.com) -> ( htttp://example.com ) にして、 閉じカッコがリンクされるのを抑止
  8068. html = html.replace(/\((https?:\/\/[\x21-\x3b\x3d-\x7e]+)\)/gi, '( $1 )');
  8069. html = html.replace(/(https?:\/\/[\x21-\x3b\x3d-\x7e]+)/gi, '<a href="$1" target="_blank" class="otherSite">$1</a>');
  8070. for (var i = 0, len = links.length; i < len; i++) {
  8071. html = html.replace(' <!----> ', links[i]);
  8072. }
  8073. html = html.split(' <br /> ').join('<br />');
  8074. var $description = $('<p class="videoDescription description">' + html + '</p>');
  8075.  
  8076. bindDescriptionEvents($description);
  8077. decorateVideoDescriptionLink($description);
  8078. return $description;
  8079. } //
  8080.  
  8081. function resetSideIchibaPanel($ichibaPanel, force) {
  8082.  
  8083. $ichibaPanel.scrollTop(0).find('*').unbind().empty();
  8084. var $inner = $('<div class="ichibaPanelInner" />');
  8085. var $header = $('<div class="ichibaPanelHeader"><p class="logo">ニコニコ市場出張所</p></div>');
  8086. $inner.append($header);
  8087.  
  8088.  
  8089. var items = [];
  8090. $('#ichibaMain').find('.ichiba_mainitem>div').each(function() {
  8091. var $item = $(this).clone().attr('id', null);
  8092. var $dl = $('<dl class="ichiba_mainitem" />').append($item);
  8093. $item.find('.thumbnail span').css({fontSize: ''});
  8094. // 誤クリックしやすいのでサムネはリンクを外す
  8095. $item.find('.thumbnail a img, .blomagaThumbnail, .blomagaText')
  8096. .parent().attr('href', null).attr('style', null).css({'text-decoration': 'none'});
  8097. $item.find('a').attr('onclick', null);
  8098. items.push($dl);
  8099. $inner.append($dl);
  8100. });
  8101. if (items.length > 0) {
  8102. for (var i = items.length -1; i >= 0; i--) {
  8103. $inner.find('#watch' + i + '_mq').attr('id', null).addClass('ichibaMarquee');
  8104. }
  8105. }
  8106. $inner.find('.nicoru').remove();
  8107.  
  8108. var $footer = $('<div class="ichibaPanelFooter"></div>');
  8109.  
  8110. var $addIchiba = $('<button class="addIchiba">商品を貼る</button>');
  8111. $addIchiba.click(function() {
  8112. AnchorHoverPopup.hidePopup();
  8113. ichibaSearch();
  8114. });
  8115. $footer.append($addIchiba);
  8116.  
  8117. var $reloadIchiba = $('<button class="reloadIchiba">リロード</button>');
  8118. $reloadIchiba.click(function() {
  8119. resetSideIchibaPanel($ichibaPanel, true);
  8120. $ichibaPanel = null;
  8121. });
  8122. $footer.append($reloadIchiba);
  8123.  
  8124. $inner.append($footer);
  8125. $inner.hide();
  8126. $ichibaPanel.append($inner);
  8127. $inner.fadeIn();
  8128. $inner = $header = $footer = $addIchiba = $reloadIchiba = null;
  8129. } //
  8130.  
  8131. function initHidariue() {
  8132. // 再生終了時に勝手にメニューが開閉するのを止める
  8133. window.WatchApp.ns.init.PlayerInitializer.videoendViewController.videoHeaderViewController = {openVideoMenu: function(){}, closeVideoMenu: function() {}};
  8134. var hidariue = null;
  8135. var resetHidariue = function() {
  8136. // var dt = new Date();
  8137. // if (dt.getMonth() < 1 && dt.getDate() <=1) {
  8138. // $('#videoMenuTopList').append('<li style="font-size:50%"> \ │ /<br>  /‾\   /‾‾‾‾‾‾‾‾‾<br>─( ゜ ∀ ゜ )< しんねんしんねん!<br>  \_/   \_________<br> / │ \</li>');
  8139. // }
  8140. if (!conf.hidariue) { return; }
  8141. if (!hidariue) {
  8142. $('#videoMenuTopList').append('<li class="hidariue" style="text-align: center;"><a href="http://userscripts.org/scripts/show/151269" target="_blank" style="color:black;"><img id="hidariue" style="border-radius: 8px; box-shadow: 1px 1px 2px #ccc;"></a><p id="nicodou" style="padding-left: 4px; display: inline-block"><a href="http://www.nicovideo.jp/video_top" target="_top"><img src="http://res.nimg.jp/img/base/head/logo/q.png" alt="ニコニコ動画:GINZA"></a></p></li>');
  8143. hidariue = $('#hidariue')[0];
  8144. }
  8145. hidariue.src = 'http://res.nimg.jp/img/base/head/icon/nico/' +
  8146. (1000 + Math.floor(Math.random() * 1000)).toString().substr(1) + '.gif';
  8147. };
  8148. EventDispatcher.addEventListener('onVideoInitialized', resetHidariue);
  8149. } //
  8150.  
  8151.  
  8152. var VideoExplorerToggleMenu = function(title, titleLink) {
  8153. this.initializeBaseDom(title, titleLink);
  8154. };
  8155. WatchApp.mixin(VideoExplorerToggleMenu.prototype, {
  8156. initializeBaseDom: function(title, titleLink) {
  8157.  
  8158. this._$toggle = $('<li style="display:none;" class="toggleVideoExplorerMenu watchItLaterMenu"></li>');
  8159. this._$menu = $('<li class="slideMenu"><ul></ul></li>');
  8160. this._$list = this._$menu.find('ul');
  8161.  
  8162. var $a = $('<a/>').text(title).attr('href', titleLink);
  8163. this._$toggle.append($a);
  8164.  
  8165. this._initializeToggleEvent();
  8166. this._initializeItemEvent();
  8167. },
  8168. _initializeToggleEvent: function() {
  8169. this._$toggle.on('click', $.proxy(function(e) {
  8170. if (e.button !== 0 || e.metaKey || e.shiftKey || e.altKey || e.ctrlKey) { return; }
  8171. e.preventDefault(); e.stopPropagation();
  8172.  
  8173. var isVisible = this._$menu.hasClass('open');
  8174. this._$toggle.addClass('opening');
  8175. this._$menu.toggleClass('open', !isVisible);
  8176.  
  8177. window.setTimeout($.proxy(function() {
  8178. this._$toggle.toggleClass('open', !isVisible);
  8179. this._$toggle.removeClass('opening');
  8180. }, this), 500);
  8181. }, this));
  8182. },
  8183. _initializeItemEvent: function() {
  8184. this._$menu.on('click', function(e) {
  8185. if (e.button !== 0 || e.metaKey || e.shiftKey || e.altKey || e.ctrlKey) { return; }
  8186.  
  8187. var elm = e.target;
  8188. if (elm.tagName !== 'A') { return; }
  8189. var $elm = $(elm);
  8190. var type = $elm.attr('data-menu-type');
  8191.  
  8192. if (type === 'mylist') {
  8193. e.preventDefault(); e.stopPropagation();
  8194. var mylistId = $elm.attr('data-mylist-id');
  8195. WatchController.showMylist(mylistId);
  8196. } else
  8197. if (type === 'deflist') {
  8198. e.preventDefault(); e.stopPropagation();
  8199. WatchController.showDeflist();
  8200. } else
  8201. if (type === 'tag') {
  8202. e.preventDefault(); e.stopPropagation();
  8203. var tag = $elm.attr('data-search-tag');
  8204. WatchController.nicoSearch(tag, 'tag');
  8205. }
  8206. });
  8207. },
  8208. attach: function() {
  8209. $('.videoExplorerMenu').find('ul:first li:first').after(this._$menu).after(this._$toggle);
  8210. },
  8211. detach: function() {
  8212. this._$toggle.detach();
  8213. this._$menu.detach();
  8214. },
  8215. add$listItem: function($item) {
  8216. this._$list.append($item);
  8217. },
  8218. addItem: function(name, title, attr) {
  8219. var
  8220. $a = $('<a/>')
  8221. .attr(attr)
  8222. .text(title),
  8223. $li = $('<li/>').addClass(iconType);
  8224. $li.append($a);
  8225. this._$list.append($li);
  8226. return $a;
  8227. },
  8228. addMylistItem: function(name, mylistId, title, iconType) {
  8229. var
  8230. $a = $('<a/>')
  8231. .text(name)
  8232. .attr({
  8233. href: '/mylist/' + mylistId,
  8234. title: title,
  8235. 'data-menu-type': 'mylist',
  8236. 'data-mylist-id': mylistId,
  8237. }),
  8238. $li = $('<li/>').addClass(iconType || '');
  8239. $li.append($a);
  8240. this._$list.append($li);
  8241. return $a;
  8242. },
  8243. show: function() {
  8244. this._$toggle.fadeIn(500);
  8245. }
  8246. });
  8247.  
  8248. WatchItLater.videoExplorerMenu = {};
  8249. WatchItLater.videoExplorerMenu.favMylists = (function() {
  8250. var toggleMenu;
  8251.  
  8252. var initialize = function() {
  8253. initialize = window._.noop;
  8254. console.log('%cinitialize WatchItLater.videoExplorerMenu.favMylists', 'background: lightgreen;');
  8255.  
  8256. toggleMenu = new VideoExplorerToggleMenu('お気に入りマイリスト', '/my/fav/mylist');
  8257.  
  8258. window.WatchItLater.loader.favMylists.load(function(mylists) {
  8259. if (mylists.length < 1) {
  8260. return;
  8261. }
  8262. for (var i = 0, len = mylists.length; i < len; i++) {
  8263. var mylist = mylists[i], lastVideo = mylist.lastVideo, $li = $('<li/>'),
  8264. title = [
  8265. '/mylist/', mylist.id, '\n',
  8266. mylist.description, '\n',
  8267. '最新動画: ', lastVideo.title, '\n',
  8268. '投稿日時: ', lastVideo.postedAt, '\n',
  8269. ''].join('');
  8270. toggleMenu.addMylistItem(
  8271. mylist.name,
  8272. mylist.id,
  8273. title,
  8274. mylist.iconType
  8275. );
  8276. }
  8277. toggleMenu.show();
  8278. });
  8279. };
  8280.  
  8281. return {
  8282. attach: function() {
  8283. initialize();
  8284. toggleMenu.attach();
  8285. },
  8286. detach: function() {
  8287. if (!toggleMenu) {
  8288. return;
  8289. }
  8290. console.log(toggleMenu);
  8291. toggleMenu.detach();
  8292. }
  8293. };
  8294. })();
  8295.  
  8296. WatchItLater.videoExplorerMenu.favTags = (function() {
  8297. var toggleMenu;
  8298.  
  8299. var initialize = function() {
  8300. initialize = window._.noop;
  8301. console.log('%cinitialize WatchItLater.videoExplorerMenu.favTags', 'background: lightgreen;');
  8302.  
  8303. toggleMenu = new VideoExplorerToggleMenu('お気に入りタグ', '/my/fav/tag');
  8304.  
  8305. window.WatchItLater.loader.favTags.load(function(tags) {
  8306. if (tags.length < 1) {
  8307. $toggle.remove();
  8308. return;
  8309. }
  8310. var sortOrder = '?sort=' + conf.searchSortType + '&order=' + conf.searchSortOrder;
  8311. for (var i = 0, len = tags.length; i < len; i++) {
  8312. var tag = tags[i], $li = $('<li/>'),
  8313. $a = $('<a/>')
  8314. .attr({
  8315. href: '/tag/' + encodeURIComponent(tag.name + ' ' + conf.defaultSearchOption) + sortOrder,
  8316. 'data-menu-type': 'tag',
  8317. 'data-search-tag': tag.name
  8318. })
  8319. .text(tag.name);
  8320. toggleMenu.add$listItem($li.append($a));
  8321. }
  8322. toggleMenu.show();
  8323. });
  8324. };
  8325.  
  8326. return {
  8327. attach: function() {
  8328. initialize();
  8329. toggleMenu.attach();
  8330. },
  8331. detach: function() {
  8332. if (!toggleMenu) {
  8333. return;
  8334. }
  8335. toggleMenu.detach();
  8336. }
  8337. };
  8338. })();
  8339.  
  8340.  
  8341. WatchItLater.videoExplorerMenu.myShortcuts = (function() {
  8342. var toggleMenu;
  8343.  
  8344. var initialize = function() {
  8345. initialize = window._.noop;
  8346. console.log('%cinitialize WatchItLater.videoExplorerMenu.myShortcuts', 'background: lightgreen;');
  8347.  
  8348. toggleMenu = new VideoExplorerToggleMenu('マイショートカット', '/my/mylist');
  8349. toggleMenu.attach();
  8350.  
  8351. window.WatchItLater.mylist.loadMylistList(function(mylistList) {
  8352. toggleMenu.add$listItem(
  8353. $('<li/>').append(
  8354. $('<a/>')
  8355. .addClass('defMylist')
  8356. .attr({href: '/my/mylist', 'data-menu-type': 'deflist'})
  8357. .text('とりあえずマイリスト')
  8358. ));
  8359. var items = [
  8360. {id: -1, href: '/my/history', name: '視聴履歴'},
  8361. {id: -2, href: '/recommendations', name: 'あなたにオススメの動画'},
  8362. {id: NicorepoVideo.REPO_ALL, href: '/my/top/all', name: '【ニコレポ】すべての動画'},
  8363. {id: NicorepoVideo.REPO_USER, href: '/my/top/user', name: '【ニコレポ】お気に入りユーザー'},
  8364. {id: NicorepoVideo.REPO_CHCOM, href: '/my/top/chcom', name: '【ニコレポ】チャンネル&コミュニティ'},
  8365. {id: NicorepoVideo.REPO_MYLIST, href: '/my/top/mylist', name: '【ニコレポ】お気に入りマイリスト'}
  8366. ];
  8367. for (var v in items) {
  8368. var item = items[v];
  8369. toggleMenu
  8370. .addMylistItem(item.name, item.id)
  8371. .addClass('defMylist')
  8372. .attr({
  8373. href: item.href
  8374. });
  8375. }
  8376. for (var i = 0, len = mylistList.length; i < len; i++) {
  8377. var mylist = mylistList[i];
  8378. toggleMenu.addMylistItem(mylist.name, mylist.id, '', 'folder' + mylist.icon_id);
  8379. }
  8380.  
  8381. toggleMenu.show();
  8382. });
  8383. };
  8384.  
  8385. return {
  8386. attach: function() {
  8387. initialize();
  8388. toggleMenu.attach();
  8389. },
  8390. detach: function() {
  8391. if (!toggleMenu) {
  8392. return;
  8393. }
  8394. toggleMenu.detach();
  8395. }
  8396. };
  8397. })();
  8398.  
  8399. WatchItLater.videoExplorerMenu.videoRanking = (function() {
  8400. var toggleMenu;
  8401.  
  8402. var VideoRankingToggleMenu = function(title, titleLink) {
  8403. WatchApp.extend(this, VideoRankingToggleMenu, VideoExplorerToggleMenu, [title, titleLink]);
  8404.  
  8405. this._$menu.addClass('videoRankingList');
  8406. this.initializeCategoryToggleEvents();
  8407. };
  8408. WatchApp.mixin(VideoRankingToggleMenu.prototype, {
  8409. initializeCategoryToggleEvents: function() {
  8410. this._$menu.on('click', '.rankingCategoryToggle', function(e) {
  8411. e.preventDefault(); e.stopPropagation();
  8412.  
  8413. var $target = $(e.currentTarget), category = $target.attr('data-category');
  8414. var $popup = $target.closest('.slideMenu');
  8415. var isClose = $popup.find('li.' + category).toggleClass('categoryClose').hasClass('categoryClose');
  8416.  
  8417. conf.setValue('rankingCategory_' + category + '_Close', isClose);
  8418. });
  8419. },
  8420. addRankingItem: function($, genre, id, name, category, term) {
  8421. var $a =
  8422. $('<a/>')
  8423. .attr({
  8424. href: '/ranking/fav/' + term + '/' + genre,
  8425. 'data-menu-type': 'mylist',
  8426. 'data-mylist-id': id
  8427. })
  8428. .text(name)
  8429. .addClass(genre);
  8430. var $li = $('<li/>');
  8431.  
  8432. if (genre === category) {
  8433. $li.addClass('isCategory'); // nameと同じならカテゴリランキング、違うならジャンルランキング
  8434. if (genre !== 'all' && genre !== 'g_politics' && genre !== 'r18') {
  8435. var $button = $([
  8436. '<button class="rankingCategoryToggle">',
  8437. '<span class="open" title="サブカテゴリを開く">▼</span>',
  8438. '<span class="close" title="サブカテゴリを閉じる">▲</span>',
  8439. '</button>'
  8440. ].join(''));
  8441. $button.attr('data-category', category);
  8442. $li.append($button);
  8443. }
  8444. }
  8445. var isClose = conf.getValue('rankingCategory_' + category + '_Close');
  8446. $li
  8447. .toggleClass('categoryClose', isClose)
  8448. .attr({'data-genre': genre, 'data-category': category})
  8449. .addClass(category).addClass(genre)
  8450. .append($a);
  8451.  
  8452. this._$list.append($li);
  8453. return $a;
  8454. }
  8455. });
  8456.  
  8457.  
  8458. var initialize = function() {
  8459. initialize = window._.noop;
  8460. console.log('%cinitialize WatchItLater.videoExplorerMenu.videoRanking', 'background: lightgreen;');
  8461.  
  8462. toggleMenu = new VideoRankingToggleMenu('動画ランキング', '/ranking');
  8463. toggleMenu.attach();
  8464.  
  8465. // TODO: マジックナンバーを
  8466. toggleMenu.addRankingItem($, 'all', -100, 'カテゴリ合算(毎時)', 'all', 'hourly');
  8467. toggleMenu.addRankingItem($, 'all', -1100, 'カテゴリ合算(24時間)', 'all', 'daily');
  8468. // toggleMenu.addRankingItem($, 'all', -4100, 'カテゴリ合算(合計)', 'all', 'total');
  8469.  
  8470. var genreId = VideoRanking.getGenreId();
  8471. for (var genre in genreId) {
  8472. if (genre === 'all') { continue; }
  8473. var id = genreId[genre], name = VideoRanking.getGenreName(genre), category = VideoRanking.getCategory(id);
  8474. toggleMenu.addRankingItem($, genre, id, name, category, 'hourly');
  8475. }
  8476.  
  8477. window.setTimeout(function() { toggleMenu.show(); }, 100);
  8478. };
  8479.  
  8480. return {
  8481. attach: function() {
  8482. initialize();
  8483. toggleMenu.attach();
  8484. },
  8485. detach: function() {
  8486. if (!toggleMenu) {
  8487. return;
  8488. }
  8489. toggleMenu.detach();
  8490. }
  8491. };
  8492. })();
  8493.  
  8494.  
  8495. var WatchingVideoView = function() { this.initialize.apply(this, arguments); };
  8496. WatchingVideoView.prototype = {
  8497. _params: null,
  8498. _$view: null,
  8499. _watchInfoModel: null,
  8500. _type: null,
  8501. initialize: function(params) {
  8502. this._content = params.content;
  8503. this._watchInfoModel = params.watchInfoModel;
  8504. this._$view = params.$view;
  8505. this._mylistController = params.mylistController;
  8506. this._type = params.type;
  8507.  
  8508. this._$title = this._$view.find('.title');
  8509. this._$thumb = this._$view.find('.thumbnail');
  8510. this._$add = this._$view.find('.add');
  8511. this._$remove = this._$view.find('.remove');
  8512.  
  8513. this._$add .on('click', $.proxy(this._onAddClick, this));
  8514. this._$remove.on('click', $.proxy(this._onRemoveClick, this));
  8515.  
  8516. EventDispatcher.addEventListener('onWatchInfoReset', $.proxy(this.onVideoChange, this));
  8517. },
  8518. getView: function() {
  8519. return this._$view;
  8520. },
  8521. detach: function() {
  8522. this._$view.detach();
  8523. },
  8524. update: function() {
  8525. $('.videoExplorerBody').toggleClass('containsWatchingVideo', this._content.containsWatchId());
  8526. },
  8527. onVideoChange: function() {
  8528. this._$title.html(this._watchInfoModel.title);
  8529. this._$thumb
  8530. .attr('src', this._watchInfoModel.thumbnail)
  8531. .off('click').on('click', Util.Closure.showLargeThumbnail(this._watchInfoModel.thumbnail));
  8532. if (this._content.isActive()) {
  8533. this.update();
  8534. }
  8535. },
  8536. _setIsUpdating: function() {
  8537. this._$view.addClass('updating');
  8538. setTimeout($.proxy(this._clearIsUpdating, this), 3000);
  8539. },
  8540. _clearIsUpdating: function() {
  8541. this._$view.removeClass('updating');
  8542. },
  8543. _getIsUpdating: function() {
  8544. return this._$view.hasClass('updating');
  8545. },
  8546. _onAddClick: function() {
  8547. var watchId = WatchController.getWatchId();
  8548. this._setIsUpdating();
  8549. if (this._type === 'deflist') {
  8550. this._mylistController.addDefListItem(watchId, $.proxy(this._onMylistUpdate, this));
  8551. } else {
  8552. var mylistId = this._content.getMylistId();
  8553. this._mylistController.addMylistItem (watchId, mylistId, $.proxy(this._onMylistUpdate, this));
  8554. }
  8555. },
  8556. _onRemoveClick: function() {
  8557. var watchId = WatchController.getWatchId();
  8558. this._setIsUpdating();
  8559. if (this._type === 'deflist') {
  8560. this._mylistController.deleteDefListItem(watchId, $.proxy(this._onMylistUpdate, this));
  8561. } else {
  8562. var mylistId = this._content.getMylistId();
  8563. this._mylistController.deleteMylistItem (watchId, mylistId, $.proxy(this._onMylistUpdate, this));
  8564. }
  8565. },
  8566. _onMylistUpdate: function(status, result) {
  8567. if (status === 'ok') {
  8568. if (this._type === 'deflist') {
  8569. WatchController.clearDeflistCache();
  8570. }
  8571. } else {
  8572. Popup.alert('更新に失敗: ' + result.error.description);
  8573. }
  8574. this._content.setFilter(null);
  8575. setTimeout(
  8576. $.proxy(function() {
  8577. //this._content.changeState({page: 1});
  8578. this.contentRefresh();
  8579. this._clearIsUpdating();
  8580. }, this), 500);
  8581. },
  8582. contentRefresh: function() {
  8583. var params = this._content.getParams();
  8584. params.page = 1;
  8585. this._content.changeState(params);
  8586. this._content.refresh({page: 1});
  8587. }
  8588. }; // end WatchingVideoView.prototype
  8589.  
  8590.  
  8591. var GrepOptionView = function() { this.initialize.apply(this, arguments); };
  8592. GrepOptionView.prototype = {
  8593. _params: null,
  8594. _$view: null,
  8595. initialize: function(params) {
  8596. this._content = params.content;
  8597. this._$view = params.$view;
  8598. this._$form = this._$view.find('form');
  8599. this._$input = this._$view.find('.grepInput').attr('list', params.listName);
  8600. this._$community = this._$view.find('.community');
  8601. this._$alive = this._$view.find('.alive');
  8602. this._$invert = this._$view.find('.invert');
  8603. this._$checkboxes = this._$view.find('input[type=checkbox]');
  8604.  
  8605. this._not = false;
  8606.  
  8607. this._$view.toggleClass('debug', !!conf.debugMode);
  8608.  
  8609. this._$list = $('<datalist />').attr('id', params.listName);
  8610. $('body').append(this._$list);
  8611.  
  8612. this._$form.on('submit', $.proxy(this._onFormSubmit, this));
  8613. this._$checkboxes.on('click', $.proxy(this._onCheckClick, this));
  8614.  
  8615. this._$input.on('click', $.proxy(function(e) {
  8616. e.stopPropagation();
  8617. }, this)) .on('focus', $.proxy(function(e) {
  8618. window.WatchApp.ns.util.WindowUtil.scrollFit('#videoExplorer');
  8619. }, this));
  8620.  
  8621. this._$view .on('click', $.proxy(function(e) {
  8622. this._$input.focus();
  8623. }, this));
  8624. },
  8625. getView: function() {
  8626. return this._$view;
  8627. },
  8628. detach: function() {
  8629. this._$view.detach();
  8630. },
  8631. clear: function() {
  8632. this._$input.val('');
  8633. this._$checkboxes.prop('checked', false);
  8634. this._$view.removeClass('active');
  8635. },
  8636. update: function() {
  8637. var list = this._content.getRawList();
  8638. var tmp = [];
  8639. for (var i = list.length -1; i >= 0; i--) {
  8640. tmp.push('<option>');
  8641. tmp.push(list[i].title); // 既にエスケープされてる
  8642. tmp.push('</option>');
  8643. }
  8644. this._$list.html(tmp.join(''));
  8645.  
  8646. if (this._getWord().length > 0) {
  8647. window.setTimeout($.proxy(function() { this._$input.focus(); }, this), 100);
  8648. }
  8649. },
  8650. _isActive: function() {
  8651. return (this._$input.val().length > 0 ||
  8652. !!this._$community.prop('checked') ||
  8653. !!this._$alive .prop('checked'));
  8654. },
  8655. _getWord: function() {
  8656. return $.trim(this._$input.val());
  8657. },
  8658. _onCheckClick: function(e) {
  8659. e.stopPropagation();
  8660. this._submit();
  8661. },
  8662. _onFormSubmit: function(e) {
  8663. e.preventDefault();
  8664. e.stopPropagation();
  8665. this._submit();
  8666. },
  8667. _submit: function() {
  8668. var isActive = this._isActive();
  8669. this._$view.toggleClass('active', isActive);
  8670.  
  8671. if (isActive) {
  8672. this._content.setFilter(this._getFilter());
  8673. } else {
  8674. this._content.setFilter(null);
  8675. }
  8676.  
  8677. this.contentRefresh();
  8678. },
  8679. contentRefresh: function() {
  8680. var params = this._content.getParams();
  8681. params.page = 1;
  8682. this._content.changeState(params);
  8683. this._content.refresh({page: 1});
  8684. },
  8685. _getFilter: function() {
  8686. var to_h = function(str) {
  8687. return str.replace(/[A-Za-z0-9]/g, function(s) {
  8688. return String.fromCharCode(s.charCodeAt(0) - 65248);
  8689. }).toLowerCase();
  8690. };
  8691. var word = to_h(this._getWord());
  8692. var communityReg = /^so|^\d+$/;
  8693. var wordFilter = word.length > 0;
  8694. var communityFilter = !!this._$community.prop('checked');
  8695. var aliveFilter = !!this._$alive.prop('checked');
  8696. var isInvert = !!this._$invert.prop('checked');
  8697.  
  8698.  
  8699. var isCommunity = function(item) {
  8700. return communityReg.test(item.id);
  8701. };
  8702. var isMatch = function(item) {
  8703. var title = item.title;
  8704. var desc = item.description_full || item.description_short || '';
  8705. var mc = item.mylist_comment || '';
  8706. var text = to_h([title, desc, mc].join('\n'));
  8707.  
  8708. return text.indexOf(word) >= 0;
  8709. };
  8710. var isAlive = function(item) {
  8711. var thumbnail = item.thumbnail_url || '';
  8712. if (thumbnail.indexOf('http://res.nimg.jp/img/common/video_deleted') < 0) {
  8713. return true;
  8714. }
  8715. return false;
  8716. };
  8717.  
  8718. var grepFilter = function(item) {
  8719. var result = true, i = func.length, f;
  8720. while (--i >= 0 && (result || isInvert)) {
  8721. f = func[i];
  8722. result &= f(item);
  8723. }
  8724. return isInvert ? !result : result;
  8725. };
  8726.  
  8727. var func = [], f;
  8728. if (wordFilter) { func.push(isMatch); }
  8729. if (communityFilter) { func.push(isCommunity); }
  8730. if (aliveFilter) { func.push(isAlive); }
  8731.  
  8732. if (func.length < 1) { return null; }
  8733.  
  8734. return grepFilter;
  8735. }
  8736. }; // end GrepOptionView.prototype
  8737.  
  8738. function initMylistContent($, conf, w) {
  8739. var ContentType = WatchApp.ns.components.videoexplorer.model.ContentType;
  8740. var ContentView = WatchApp.ns.components.videoexplorer.view.content.MylistVideoContentView;
  8741. var vec = watch.VideoExplorerInitializer.videoExplorerController;
  8742. var explorer = vec.getVideoExplorer();
  8743. var myUserId = WatchController.getMyUserId();
  8744. var content = explorer.getContentList().getContent(ContentType.MYLIST_VIDEO);
  8745. var loader = content._mylistVideoAPILoader;
  8746. var pager = content._pager;
  8747. var watchingVideoView = new WatchingVideoView({
  8748. content: content,
  8749. watchInfoModel: watchInfoModel,
  8750. mylistController: Mylist,
  8751. type: 'mylist',
  8752. $view: $([
  8753. '<div class="watchingVideo">',
  8754. '<img class="thumbnail">',
  8755. '<p class="title"></p>',
  8756. '<span class="contains" >この動画はリストに登録されています</span>',
  8757. '<span class="not_contains">この動画はリストにありません</span>',
  8758. '<span class="edit">',
  8759. '<button class="add" >登録</button>',
  8760. '<button class="remove">外す</button>',
  8761. '</span>',
  8762. '</div>',
  8763. ''].join(''))
  8764. });
  8765.  
  8766. var grepOptionView = new GrepOptionView({
  8767. content: content,
  8768. listName: 'suggestMylistTitle',
  8769. $view: $([
  8770. '<div class="grepOption">',
  8771. '<form>',
  8772. '<input type="search" class="grepInput" autocomplete="on" placeholder="タイトル・説明文で絞り込む(G)" accesskey="g">',
  8773. '<label class="communityFilter filter"><input type="checkbox" class="community">チャンネル・コミュニティ・マイメモリーのみ</label>',
  8774. '<label class="aliveFilter filter"><input type="checkbox" class="alive">生存動画のみ</label>',
  8775. '<label class="invertFilter filter"><input type="checkbox" class="invert">絞り込みの反転</label>',
  8776. '</form>',
  8777. '</div>',
  8778. ].join(''))
  8779. });
  8780.  
  8781.  
  8782. pager._pageItemCount = conf.searchPageItemCount;
  8783. pager._displayPageCount = 5;
  8784. EventDispatcher.addEventListener('on.config.searchPageItemCount', function(v) {
  8785. pager._pageItemCount = v;
  8786. });
  8787.  
  8788. content._isOwnerNicorepo = false;
  8789. content._isRanking = false;
  8790. content.getIsMine = $.proxy(function() {
  8791. return parseInt(this.getUserId(), 10) === parseInt(myUserId, 10) && parseInt(this.getMylistId(), 10) > 0;
  8792. }, content);
  8793. content.getIsDummy = $.proxy(function() {
  8794. var id = this.getMylistId();
  8795. return parseInt(id, 10) <= 0 || id.toString().indexOf('repo') === 0;
  8796. }, content);
  8797. content.getIsOwnerNicorepo = $.proxy(function() { return this._isOwnerNicorepo; }, content);
  8798. content.getIsRanking = $.proxy(function() { return this._isRanking; }, content);
  8799.  
  8800. // grep対応のための拡張
  8801. content._rawList = [];
  8802. content.getRawList = $.proxy(function() { return this._rawList; }, content);
  8803. content._filter = null;
  8804. content.setFilter = $.proxy(function(filter) {
  8805. this._filter = filter;
  8806. }, content);
  8807. content.getFilter = $.proxy(function() { return this._filter; }, content);
  8808.  
  8809.  
  8810. content.clear_org = content.clear;
  8811. content.clear = $.proxy(function() {
  8812. this.setFilter(null);
  8813. this.clear_org();
  8814. grepOptionView.clear();
  8815. }, content);
  8816.  
  8817. content.getNickname = $.proxy(function() {
  8818. if (this._nickname && this._nickname.length > 0) {
  8819. return this._nickname;
  8820. }
  8821. return 'no-name';
  8822. }, content);
  8823.  
  8824. content.onLoad_org = content.onLoad;
  8825. content.onLoad = $.proxy(function(err, result) {
  8826. this._isOwnerNicorepo = result.isOwnerNicorepo;
  8827. this._isRanking = result.isRanking;
  8828.  
  8829. var filter = this.getFilter();
  8830. if (err === null && result.list && result.list.length) {
  8831. if (!result.rawList) result.rawList = result.list.concat();
  8832. if (filter) {
  8833. var list = [];
  8834.  
  8835. for (var i = result.rawList.length - 1; i >= 0; i--) {
  8836. var item = result.rawList[i];
  8837. if (item.title && filter(item)) {
  8838. list.unshift(item);
  8839. }
  8840. }
  8841. result.list = list;
  8842. } else {
  8843. result.list = result.rawList.concat();
  8844. }
  8845. } else
  8846. if (result.rawList) {
  8847. result.list = result.rawList.concat();
  8848. }
  8849.  
  8850. this._rawList = result.rawList || [];
  8851. this.onLoad_org(err, result);
  8852. if (this.getIsMine()) {
  8853. EventDispatcher.dispatch('onMyMylistLoad', this.getMylistId(), {
  8854. name: this.getName(),
  8855. items: result.rawList
  8856. });
  8857. }
  8858. }, content);
  8859.  
  8860. content.containsWatchId = $.proxy(function(watchId) {
  8861. var list = this.getRawList();
  8862. if (!watchId) { watchId = WatchController.getWatchId(); }
  8863.  
  8864. for (var i = list.length - 1; i >= 0; i--) {
  8865. if (list[i].id === watchId) { return true; }
  8866. }
  8867. return false;
  8868. }, content);
  8869.  
  8870. loader.load_org = loader.load;
  8871. loader.load = $.proxy(function(params, callback) {
  8872. var isOwnerNicorepo = false, isRanking = false;
  8873. var id = params.id;
  8874. if (typeof id === 'string' && id.indexOf('repo-owner-') === 0) {
  8875. id = NicorepoVideo.REPO_OWNER;
  8876. }
  8877.  
  8878. var applyFilter = function(err, result) {
  8879. result.isOwnerNicorepo = isOwnerNicorepo;
  8880. result.isRanking = isRanking;
  8881. callback(err, result);
  8882. };
  8883. if (id < 0) {
  8884. var timeoutTimer = null;
  8885. var onload = function(result) {
  8886. window.clearTimeout(timeoutTimer);
  8887. // 投稿者ニコレポが0件で、投稿動画一覧を公開していたらそっちを開くタイマーをセット
  8888. if (result.list.length < 1 &&
  8889. parseInt(id, 10) === NicorepoVideo.REPO_OWNER &&
  8890. WatchController.isVideoPublic()) {
  8891. window.setTimeout(function() {
  8892. WatchController.openVideoOwnersVideo();
  8893. }, 500);
  8894. }
  8895. applyFilter(null, result);
  8896. };
  8897. var onerror = function(result) {
  8898. window.clearTimeout(timeoutTimer);
  8899. callback('error', result);
  8900. };
  8901. timeoutTimer = window.setTimeout(function() {
  8902. onload = onerror = window._.noop;
  8903. onerror({message: '通信がタイムアウトしました:' + id, status: 'fail'});
  8904. }, 30 * 1000);
  8905.  
  8906. // マイリストIDに負の数字(通常ないはず)が来たら乗っ取るサイン
  8907. // そもそもマイリストIDはstringのようなので数字にこだわる必要なかったかも
  8908. //
  8909. try {
  8910. if (typeof VideoRanking.getGenreName(id) === 'string') {
  8911. isRanking = true;
  8912. VideoRanking.load(null, {id: id}).then(onload, onerror);
  8913. return;
  8914. }
  8915. // TODO: マジックナンバーを
  8916. switch (parseInt(id, 10)) {
  8917. case -1:
  8918. VideoWatchHistory.load(onload);
  8919. break;
  8920. case -2:
  8921. VideoRecommendations.load(onload);
  8922. break;
  8923. case -3:
  8924. ChannelVideoList.loadOwnerVideo(null).then(onload, onerror);
  8925. break;
  8926. case NicorepoVideo.REPO_ALL:
  8927. NicorepoVideo.loadAll() .then(onload, onerror);
  8928. break;
  8929. case NicorepoVideo.REPO_USER:
  8930. NicorepoVideo.loadUser() .then(onload, onerror);
  8931. break;
  8932. case NicorepoVideo.REPO_CHCOM:
  8933. NicorepoVideo.loadChCom() .then(onload, onerror);
  8934. break;
  8935. case NicorepoVideo.REPO_MYLIST:
  8936. NicorepoVideo.loadMylist().then(onload, onerror);
  8937. break;
  8938. case NicorepoVideo.REPO_OWNER:
  8939. isOwnerNicorepo = true;
  8940. NicorepoVideo.loadOwner() .then(onload, onerror);
  8941. break;
  8942. default:
  8943. throw {message: '未定義のIDです:' + id, status: 'fail'};
  8944. }
  8945. } catch(e) {
  8946. // TODO: ここのエラーをちゃんと投げる
  8947. if (e.message && e.status) {
  8948. onerror({
  8949. status: e.status,
  8950. message: e.message
  8951. });
  8952. } else {
  8953. console.log(e); console.trace();
  8954. onerror({message: 'エラーが発生しました:' + id, status: 'fail'});
  8955. }
  8956. }
  8957. } else {
  8958. this.load_org(params, applyFilter);
  8959. }
  8960. }, loader);
  8961.  
  8962.  
  8963. var __css__ = Util.here(function() {/*
  8964. #videoExplorer .watchingVideo { display: none; }
  8965. #videoExplorer .watchingVideo .title { display: none; }
  8966. #videoExplorer .watchingVideo.updating * {
  8967. cursor: wait; opacity: 0.5;
  8968. }
  8969. #videoExplorer .watchingVideo button {
  8970. padding: 2px 12px; margin: 12px 24px;
  8971. }
  8972. #videoExplorer .isMine .watchingVideo {
  8973. display: block; background: #f4f4f4; border: 1px solid #ccc;
  8974. margin: auto; width: 500px; min-height: 48px; padding: 16px;
  8975. }
  8976.  
  8977. #videoExplorer .watchingVideo .thumbnail {
  8978. float: left; width: 72px; margin-right: 24px; cursor: pointer;
  8979. }
  8980.  
  8981.  
  8982. #videoExplorer .watchingVideo .title {
  8983. font-weight: bolder;
  8984. }
  8985. #videoExplorer .watchingVideo .title:before { content: ''; }
  8986. #videoExplorer .watchingVideo .title:after { content: ' '; }
  8987.  
  8988. #videoExplorer .watchingVideo .contains { display: none; }
  8989. #videoExplorer .containsWatchingVideo .watchingVideo .contains { display: inline; }
  8990. #videoExplorer .watchingVideo .not_contains { display: inline; }
  8991. #videoExplorer .containsWatchingVideo .watchingVideo .not_contains { display: none; }
  8992.  
  8993. #videoExplorer .watchingVideo .edit { display: none; }
  8994. #videoExplorer .isMine .watchingVideo .edit { display: inline-block; }
  8995.  
  8996. #videoExplorer .watchingVideo .add { display: inline-block; }
  8997. #videoExplorer .containsWatchingVideo .watchingVideo .add { display: none; }
  8998.  
  8999. #videoExplorer .watchingVideo .remove{ display: none; }
  9000. #videoExplorer .containsWatchingVideo .watchingVideo .remove{ display: inline-block; }
  9001.  
  9002. .isMine .editFavorite {
  9003. display: none; {* 自分のマイリストにはお気に入り登録ボタンを出さない *}
  9004. }
  9005. .watchingVideo button {
  9006. cursor: pointer;
  9007. }
  9008.  
  9009. .grepOption {
  9010. padding: 16px;
  9011. width: 500px;
  9012. margin: 16px auto;
  9013. background: #f4f4f4; border: 1px solid #ccc;
  9014. }
  9015. .grepOption .grepInput {
  9016. font-size: 120%;
  9017. width: 100%;
  9018. }
  9019.  
  9020. .grepOption .filter {
  9021. display: block; margin: 8px;
  9022. }
  9023. .grepOption .filter:hover {
  9024. background: #ccc;
  9025. }
  9026. .grepOption .filter.invertFilter {
  9027. display: none;
  9028. }
  9029. .grepOption.active .filter.invertFilter {
  9030. display: block; text-align: right;
  9031. }
  9032.  
  9033.  
  9034. */});
  9035. addStyle(__css__, 'mylistContentCss');
  9036.  
  9037. var MylistDetailView = WatchApp.ns.components.videoexplorer.view.content.parts.MylistDetailView;
  9038. MylistDetailView.prototype.update_org = MylistDetailView.prototype.update;
  9039. MylistDetailView.prototype.update = function(id, name, description, count) {
  9040. this.update_org(id, name, description, count);
  9041. if (id.toString().match(/repo-owner-(\d+)/)) {
  9042. this._$name.attr('href', '/user/' + RegExp.$1);
  9043. } else
  9044. if (parseInt(id, 10) <= 0) {
  9045. this._$name.attr('href', '');
  9046. }
  9047. };
  9048.  
  9049. var
  9050. overrideContentView = function(proto, watchingVideoView, grepOptionView) {
  9051. var updateCssClass = function(content) {
  9052. $('.videoExplorerBody')
  9053. .toggleClass('dummyMylist', content.getIsDummy())
  9054. .toggleClass('isMine', content.getIsMine())
  9055. .toggleClass('ownerNicorepo', content.getIsOwnerNicorepo())
  9056. .toggleClass('ranking', content.getIsRanking())
  9057. ;
  9058. };
  9059.  
  9060. proto.detach_org = proto.detach;
  9061. proto.detach = function() {
  9062. this.detach_org();
  9063. watchingVideoView.detach();
  9064. grepOptionView.detach();
  9065. };
  9066.  
  9067. proto.onUpdate_org = proto.onUpdate;
  9068. proto.onUpdate = function() {
  9069. this.onUpdate_org();
  9070. updateCssClass(this._content);
  9071. watchingVideoView.update();
  9072. grepOptionView.update();
  9073. this._$content.find('.mylistSortOrder').before(watchingVideoView.getView());
  9074. this._$content.find('.mylistSortOrder').before(grepOptionView.getView());
  9075. };
  9076.  
  9077. proto.onError_org = proto.onError;
  9078. proto.onError = function() {
  9079. this.onError_org();
  9080. updateCssClass(this._content);
  9081. watchingVideoView.update();
  9082. grepOptionView.update();
  9083. this._$content.find('.mylistSortOrder').before(grepOptionView.getView());
  9084. };
  9085.  
  9086. };
  9087.  
  9088. overrideContentView(ContentView.prototype, watchingVideoView, grepOptionView);
  9089. } // end initMylistContent
  9090.  
  9091. function initDeflistContent($, conf, w) {
  9092. var ContentType = WatchApp.ns.components.videoexplorer.model.ContentType;
  9093. var ContentView = WatchApp.ns.components.videoexplorer.view.content.DeflistVideoContentView;
  9094. var vec = watch.VideoExplorerInitializer.videoExplorerController;
  9095. var explorer = vec.getVideoExplorer();
  9096. var content = explorer.getContentList().getContent(ContentType.DEFLIST_VIDEO);
  9097. var loader = content._deflistVideoAPILoader;
  9098. var pager = content._pager;
  9099. var watchingVideoView = new WatchingVideoView({
  9100. content: content,
  9101. watchInfoModel: watchInfoModel,
  9102. mylistController: Mylist,
  9103. type: 'deflist',
  9104. $view: $([
  9105. '<div class="watchingVideo">',
  9106. '<img class="thumbnail">',
  9107. '<p class="title"></p>',
  9108. '<span class="contains" >この動画はリストに登録されています</span>',
  9109. '<span class="not_contains">この動画はリストにありません</span>',
  9110. '<span class="edit">',
  9111. '<button class="add" >登録</button>',
  9112. '<button class="remove">外す</button>',
  9113. '</span>',
  9114. '</div>',
  9115. ''].join(''))
  9116. });
  9117. var grepOptionView = new GrepOptionView({
  9118. content: content,
  9119. listName: 'suggestDeflistTitle',
  9120. $view: $([
  9121. '<div class="grepOption">',
  9122. '<form>',
  9123. '<input type="search" class="grepInput" autocomplete="on" placeholder="とりマイをタイトル・説明文で絞り込む(G)" accesskey="g">',
  9124. '<label class="communityFilter filter"><input type="checkbox" class="community">チャンネル・コミュニティ・マイメモリーのみ</label>',
  9125. '<label class="aliveFilter filter"><input type="checkbox" class="alive">生存動画のみ</label>',
  9126. '<label class="invertFilter filter"><input type="checkbox" class="invert">絞り込みの反転</label>',
  9127. '</form>',
  9128. '</div>',
  9129. ].join(''))
  9130. });
  9131.  
  9132.  
  9133.  
  9134. pager._pageItemCount = conf.searchPageItemCount;
  9135. pager._displayPageCount = 5;
  9136. EventDispatcher.addEventListener('on.config.searchPageItemCount', function(v) {
  9137. pager._pageItemCount = v;
  9138. });
  9139.  
  9140.  
  9141. content.changeState_org = content.changeState;
  9142. content.changeState = $.proxy(function(params, callback) {
  9143. console.log('deflist refresh! ', params, callback);
  9144. if (!this.isActive()) {
  9145. WatchController.clearDeflistCache();
  9146. }
  9147. this.changeState_org(params, callback);
  9148. }, content);
  9149.  
  9150. content.getIsMine = function() { return true; };
  9151.  
  9152. // grep対応のための拡張
  9153. content._rawList = [];
  9154. content.getRawList = $.proxy(function() { return this._rawList; }, content);
  9155. content._filter = null;
  9156. content.setFilter = $.proxy(function(filter) {
  9157. this._filter = filter;
  9158. }, content);
  9159. content.getFilter = $.proxy(function() { return this._filter; }, content);
  9160.  
  9161. content.onLoad_org = content.onLoad;
  9162. content.onLoad = $.proxy(function(err, result) {
  9163. var filter = this.getFilter();
  9164. if (err === null && result.list && result.list.length) {
  9165. if (!result.rawList) result.rawList = result.list.concat();
  9166. if (filter) {
  9167. var list = [];
  9168.  
  9169. for (var i = result.rawList.length - 1; i >= 0; i--) {
  9170. var item = result.rawList[i];
  9171. if (item.title && filter(item)) {
  9172. list.unshift(item);
  9173. }
  9174. }
  9175. result.list = list;
  9176. } else {
  9177. result.list = result.rawList.concat();
  9178. }
  9179. } else
  9180. if (result.rawList) {
  9181. result.list = result.rawList.concat();
  9182. }
  9183. this._rawList = result.rawList || [];
  9184.  
  9185. this.onLoad_org(err, result);
  9186. }, content);
  9187.  
  9188. content.clear_org = content.clear;
  9189. content.clear = $.proxy(function() {
  9190. this.setFilter(null);
  9191. this.clear_org();
  9192. grepOptionView.clear();
  9193. }, content);
  9194.  
  9195. content.containsWatchId = $.proxy(function(watchId) {
  9196. var list = this.getRawList();
  9197. if (!watchId) { watchId = WatchController.getWatchId(); }
  9198.  
  9199. for (var i = list.length - 1; i >= 0; i--) {
  9200. if (list[i].id === watchId) { return true; }
  9201. }
  9202. return false;
  9203. }, content);
  9204.  
  9205. var
  9206. overrideContentView = function(proto, watchingVideoView) {
  9207. var updateCssClass = function(content) {
  9208. $('.videoExplorerBody').toggleClass('isMine', true);
  9209. };
  9210.  
  9211. proto.detach_org = proto.detach;
  9212. proto.detach = function() {
  9213. this.detach_org();
  9214. watchingVideoView.detach();
  9215. grepOptionView.detach();
  9216. };
  9217.  
  9218. proto.onUpdate_org = proto.onUpdate;
  9219. proto.onUpdate = function() {
  9220. this.onUpdate_org();
  9221. updateCssClass(this._content);
  9222. watchingVideoView.update();
  9223. grepOptionView.update();
  9224. this._$content.find('.deflistSortOrder').before(watchingVideoView.getView());
  9225. this._$content.find('.deflistSortOrder').before(grepOptionView.getView());
  9226. };
  9227.  
  9228. proto.onError_org = proto.onError;
  9229. proto.onError = function() {
  9230. this.onError_org();
  9231. updateCssClass(this._content);
  9232. watchingVideoView.update();
  9233. grepOptionView.update();
  9234. this._$content.find('.deflistSortOrder').before(grepOptionView.getView());
  9235. };
  9236.  
  9237. };
  9238.  
  9239. overrideContentView(ContentView.prototype, watchingVideoView);
  9240. } // end initDeflistContent
  9241.  
  9242.  
  9243.  
  9244.  
  9245. function showLargeThumbnail(baseUrl) {
  9246. var largeUrl = baseUrl, size;
  9247. if (baseUrl.indexOf('smilevideo.jp') >= 0) {
  9248. largeUrl = baseUrl.replace(/\.([LM])/, '') + '.L';
  9249. size = 'width: 360px; height: 270px; max-height: 500px;';
  9250. } else {
  9251. largeUrl = baseUrl.replace(/z$/, 'l');
  9252. size = 'width: 360px; max-height: 500px;';
  9253. }
  9254. var
  9255. html = [
  9256. '<div class="largeThumbnailPopup" onmousedown="if (event.button == 0) { $(\'#popupMarquee\').removeClass(\'show\'); event.preventDefault(); }" style="width: 360px; height: 270px; background-image: url(' , largeUrl, ')">',
  9257. '<img src="', largeUrl, '" style="display: none;" onload="$(\'#popupMarquee .largeThumbnailPopup *\').hide()">',
  9258. // '<img src="', baseUrl, '" style="', size, ' z-index: 2;">',
  9259. '<div style="', size, ' background-image: url(' + baseUrl + '); "></div>',
  9260. '</div>',
  9261. ''].join('');
  9262. Popup.show(html);
  9263. } //
  9264. WatchController.showLargeThumbnail = showLargeThumbnail;
  9265.  
  9266. function onVideoStopped() {
  9267. EventDispatcher.dispatch('onVideoStopped');
  9268. }
  9269.  
  9270. function onVideoEnded() {
  9271. EventDispatcher.dispatch('onVideoEnded');
  9272. }
  9273.  
  9274. var videoExplorerOpenCount = 0;
  9275. function onVideoExplorerOpened(params) {
  9276. window.console.timeEnd('onVideoExplorerOpen');
  9277. var target = params.target, contentList = params.contentList, content = params.content;
  9278. if (videoExplorerOpenCount++ === 0) {
  9279. EventDispatcher.dispatch('onFirstVideoExplorerOpened', content);
  9280. }
  9281. EventDispatcher.dispatch('onVideoExplorerOpened', content);
  9282.  
  9283. AnchorHoverPopup.hidePopup().updateNow();
  9284. }
  9285.  
  9286. function onVideoExplorerOpening(params) {
  9287. window.console.time('onVideoExplorerOpen');
  9288. var target = params.target, contentList = params.contentList, content = params.content;
  9289. if (videoExplorerOpenCount === 0) {
  9290. EventDispatcher.dispatch('onFirstVideoExplorerOpening', params);
  9291. }
  9292. EventDispatcher.dispatch('onVideoExplorerOpening', params);
  9293. }
  9294.  
  9295. function onVideoExplorerClosing(params) {
  9296. var target = params.target, contentList = params.contentList, content = params.content;
  9297. EventDispatcher.dispatch('onVideoExplorerClosing', content);
  9298. }
  9299.  
  9300. function onVideoExplorerClosed(params) {
  9301. var target = params.target, contentList = params.contentList, content = params.content;
  9302. AnchorHoverPopup.hidePopup().updateNow();
  9303. EventDispatcher.dispatch('onVideoExplorerClosed', content);
  9304. setTimeout(function() {
  9305. watch.PlaylistInitializer.playlistView.resetView();
  9306. }, 1000);
  9307. }
  9308.  
  9309. function onVideoExplorerRefreshStart(params) {
  9310. window.console.time('videoExplorerRefresh');
  9311. var target = params.target, contentList = params.contentList, content = params.content;
  9312. var
  9313. ContentType = WatchApp.ns.components.videoexplorer.model.ContentType,
  9314. type = content.getType(),
  9315. $ve = $('#videoExplorer')
  9316. .removeClass('w_user').removeClass('w_upload').removeClass('w_mylist')
  9317. .removeClass('w_deflist').removeClass('w_related').removeClass('w_search'),
  9318. $body = $ve.find('.videoExplorerBody')
  9319. .removeClass('isMine').removeClass('dummyMylist')
  9320. .removeClass('isRanking').removeClass('isOwnerNicorepo'),
  9321. className = 'w_user';
  9322. switch (type) {
  9323. case ContentType.USER_VIDEO:
  9324. className = 'w_user';
  9325. break;
  9326. case ContentType.UPLOADED_VIDEO:
  9327. className = 'w_uploaded';
  9328. break;
  9329. case ContentType.MYLIST_VIDEO:
  9330. className = 'w_mylist';
  9331. break;
  9332. case ContentType.DEFLIST_VIDEO:
  9333. className = 'w_deflist';
  9334. break;
  9335. case ContentType.RELATED_VIDEO:
  9336. className = 'w_related';
  9337. break;
  9338. case ContentType.SEARCH:
  9339. className = 'w_search';
  9340. break;
  9341. }
  9342. $ve.addClass(className);
  9343.  
  9344. EventDispatcher.dispatch('onVideoExplorerRefreshStart', content);
  9345. }
  9346. function onVideoExplorerRefreshEnd(params) {
  9347. window.console.timeEnd('videoExplorerRefresh');
  9348. var target = params.target, contentList = params.contentList, content = params.content;
  9349. EventDispatcher.dispatch('onVideoExplorerRefreshEnd', content);
  9350. }
  9351. function onVideoExplorerChangePage(params) {
  9352. var target = params.target, contentList = params.contentList, content = params.content;
  9353. EventDispatcher.dispatch('onVideoExplorerChangePage', content);
  9354. }
  9355.  
  9356. /**
  9357. * 検索中の動画サイズを無理矢理でっかくするよ。
  9358. */
  9359. var videoExplorerStyle = null, lastAvailableWidth = 0, lastBottomHeight = 0;
  9360. function adjustVideoExplorerSize(force) {
  9361. if (force !== true && (!conf.videoExplorerHack || !WatchController.isSearchMode())) { return; }
  9362. $('#videoExplorer, #content, #bottomContentTabContainer').toggleClass('w_adjusted', conf.videoExplorerHack);
  9363. var
  9364. rightAreaWidth = $('.videoExplorerBody').outerWidth(), // 592
  9365. availableWidth = Math.max($(window).innerWidth() - rightAreaWidth, 300),
  9366. commentInputHeight = $('#playerContainer').hasClass('oldTypeCommentInput') ? 30 : 0,
  9367. controlPanelHeight = $('#playerContainer').hasClass('controll_panel') ? 46 : 0;
  9368.  
  9369. var
  9370. defPlayerWidth = 300, otherPluginsHeight = 0,
  9371. defPlayerHeight = (defPlayerWidth - 32) * 9 / 16 + 10,
  9372. ratio = availableWidth / defPlayerWidth , availableHeight = defPlayerHeight * ratio + commentInputHeight + controlPanelHeight,
  9373. xdiff = (availableWidth - defPlayerWidth /*- 20 */), windowHeight = $(window).innerHeight(),
  9374. bottomHeight = windowHeight - availableHeight - (WatchController.isFixedHeader() ? $('#siteHeader').outerHeight() : 0) - otherPluginsHeight;
  9375.  
  9376. if (ratio < 1 || availableWidth <= 0 || bottomHeight <= 0 || (lastAvailableWidth === availableWidth && lastBottomHeight === bottomHeight)) { return; }
  9377.  
  9378. var seekbarWidth = 675, scaleX = (availableWidth) / seekbarWidth;
  9379.  
  9380. lastAvailableWidth = availableWidth;
  9381. lastBottomHeight = bottomHeight;
  9382.  
  9383. // コメントパネル召喚
  9384. var commentPanelWidth = 420;
  9385. var dynamic_css = [//'<style type="text/css" id="explorerHack">',
  9386. 'body.videoExplorer #content.w_adjusted #playerContainerWrapper, \n',
  9387. 'body.videoExplorer #content.w_adjusted #playerAlignmentArea, \n',
  9388. 'body.videoExplorer #content.w_adjusted #playerContainer, \n',
  9389. 'body.videoExplorer #content.w_adjusted #nicoplayerContainer ,\n',
  9390. 'body.videoExplorer #content.w_adjusted #external_nicoplayer \n',
  9391. '{',
  9392. 'width: ', availableWidth, 'px !important; height: ', availableHeight, 'px !important;padding: 0; margin: 0; ',
  9393. '}\n',
  9394. 'body.videoExplorer #content.w_adjusted .videoExplorerMenu { ',
  9395. 'position: absolute; width: 300px;',
  9396. 'margin-top: ', availableHeight, 'px !important; left: ', (xdiff - 2), 'px; ',
  9397. 'max-height: ', bottomHeight + 'px; overflow-y: auto; overflow-x: hidden; height: auto;',
  9398. '}\n',
  9399. 'body.videoExplorer #videoExplorer.w_adjusted { ',
  9400. 'margin-left: ', availableWidth, 'px !important;',
  9401. 'min-height: ', (windowHeight + 200) ,'px !important;',
  9402. 'overflow-x: hidden;',
  9403. ' }\n',
  9404. 'body.videoExplorer #content.w_adjusted #playlist { margin-left: ', xdiff, 'px; }\n',
  9405.  
  9406. 'body.videoExplorer #content.w_adjusted #nicoHeatMap {',
  9407. '-webkit-transform: scaleX(', availableWidth / 100, ');',
  9408. 'transform: scaleX(', availableWidth / 100, ');',
  9409. '}\n',
  9410. 'body.videoExplorer #content.w_adjusted #nicoHeatMapContainer {',
  9411. 'width: ', availableWidth, 'px;',
  9412. '}\n',
  9413. 'body.videoExplorer #content.w_adjusted #smart_music_kiosk {',
  9414. '-webkit-transform: scaleX(', scaleX, ');',
  9415. 'left: ', ((availableWidth - seekbarWidth) / 2) ,'px !important;',
  9416. '}\n',
  9417. 'body.videoExplorer #content.w_adjusted #songrium_logo_mini {',
  9418. 'left: ', (availableWidth + 5) ,'px !important;',
  9419. '}\n',
  9420. 'body.videoExplorer #content.w_adjusted #inspire_category {',
  9421. 'left: ', (availableWidth + 32) ,'px !important;',
  9422. '}\n',
  9423.  
  9424.  
  9425. 'body.videoExplorer #content.w_adjusted #leftPanel {',
  9426. ' display: block !important; top: ', (availableHeight + otherPluginsHeight - 1), 'px; max-height: ', bottomHeight, 'px; width: ', (xdiff - 4 + 1), 'px; left: 0;',
  9427. ' height:', bottomHeight, 'px; display: block; border-radius: 0;',
  9428. '}',
  9429. 'body.videoExplorer #content.w_adjusted #leftPanel .sideVideoInfo, body.size_small.no_setting_panel.videoExplorer #content.w_adjusted #leftPanel .sideIchibaPanel {',
  9430. 'width: ', Math.max((xdiff - 4), 130), 'px; border-radius: 0;',
  9431. '}',
  9432.  
  9433. ((xdiff >= 400) ?
  9434. [
  9435. 'body.videoExplorer #content.w_adjusted #leftPanel .sideVideoInfo .videoTitleContainer{',
  9436. 'margin-left: 158px; border-radius: 0 0 ;background: #ddd; border: solid; border-color: #ccc; border-width: 1px 1px 0;',
  9437. '}',
  9438. 'body.videoExplorer #content.w_adjusted #leftPanel .sideVideoInfo .videoThumbnailContainer{',
  9439. 'position: absolute; max-width: 150px; top: 0; ',
  9440. '}',
  9441. 'body.videoExplorer #content.w_adjusted #leftPanel .sideVideoInfo .videoInfo{',
  9442. 'background: #ddd; margin-left: 158px; border-radius: 0 0; border: solid; border-color: #ccc; border-width: 0 1px 1px;',
  9443. '}',
  9444. 'body.videoExplorer #content.w_adjusted #leftPanel .sideVideoInfo .videoDetails{',
  9445. 'margin-left: 158px; height: 100%; ',
  9446. '}',
  9447. 'body.videoExplorer #content.w_adjusted #leftPanel .sideVideoInfo .videoOwnerInfoContainer{',
  9448. 'position: absolute; width: 150px; top: 0; border: 1px solid #ccc; margin: 0;',
  9449. '}',
  9450. 'body.videoExplorer #content.w_adjusted #leftPanel .sideVideoInfo .userIconContainer, body.videoExplorer #content.w_adjusted #leftPanel .sideVideoInfo .ch_profile{',
  9451. 'background: #ddd; max-width: 150px; float: none; border-radius: 0;',
  9452. '}',
  9453. 'body.videoExplorer:not(.content-fix) .w_adjusted .videoDetails, ',
  9454. 'body.videoExplorer:not(.content-fix) .w_adjusted #videoExplorerMenu {',
  9455. // タグ領域三行分 bodyのスクロール位置をタグの場所にしてる時でもパネルは文章の末端までスクロールできるようにするための細工
  9456. // (四行以上あるときは表示しきれないが)
  9457. 'padding-bottom: 72px; ',
  9458. '}',
  9459. ].join('') :
  9460. (
  9461. (xdiff >= 154) ?
  9462. ['body.videoExplorer #content.w_adjusted #leftPanel #leftPanelTabContainer { padding: 4px 2px 3px 2px; }'].join('') :
  9463. ['body.videoExplorer #content.w_adjusted #leftPanel { display: none !important;}'].join('')
  9464. )
  9465. ),
  9466. 'body.videoExplorer #bottomContentTabContainer.w_adjusted .videoExplorerFooterAdsContainer { width: 520px; }\n',
  9467.  
  9468. 'body.videoExplorer #content.w_adjusted #playerTabWrapper {',
  9469. 'height: ', (availableHeight), 'px !important;',
  9470. '}',
  9471.  
  9472. 'body.videoExplorer #content.w_adjusted #playerTabWrapper .sidePanelInner {',
  9473. 'height: ', (availableHeight - 2), 'px;',
  9474. '}',
  9475. 'body.videoExplorer #content.w_adjusted #playerTabWrapper.w_active {',
  9476. 'right: -',(commentPanelWidth - 2), 'px !important; top: 0 !important; background: transparent; border: 0; margin-top: 0px;',
  9477. '}',
  9478. 'body.videoExplorer #content.w_adjusted #playerTabWrapper.w_active #playerCommentPanel {',
  9479. 'display: block; background: #dfdfdf; border: 1px solid;',
  9480. '}',
  9481. ''].join('');
  9482.  
  9483. if (videoExplorerStyle) {
  9484. videoExplorerStyle.innerHTML = dynamic_css;
  9485. } else {
  9486. videoExplorerStyle = addStyle(dynamic_css, 'videoExplorerStyle');
  9487. }
  9488.  
  9489. } // end adjustVideoExplorerSize
  9490.  
  9491. function setupVideoExplorerStaticCss() {
  9492. var __css__ = Util.here(function() {/*
  9493. body.videoExplorerOpening {
  9494. overflow-y: scroll;
  9495. }
  9496. body.videoExplorerOpening .videoExplorerMenu {
  9497. {* display: none; *}
  9498. }
  9499. body.videoExplorerOpening #playerTabWrapper {
  9500. visibility: hidden;
  9501. }
  9502. #videoExplorer {
  9503. transition: margin-left 0.4s ease 0.4s; overflow-x: hidden;
  9504. }
  9505. #videoExplorer.w_adjusted .videoExplorerBody, #videoExplorer.w_adjusted .videoExplorerContent .contentItemList {
  9506. width: 592px; padding-left: 0; min-width: 592px; max-width: auto;
  9507. }
  9508. #videoExplorer.w_adjusted .searchBox {
  9509. width: 574px;\
  9510. }
  9511. #videoExplorer.w_adjusted .videoExplorerContent {
  9512. width: 592px;
  9513. }
  9514. #videoExplorer.w_adjusted .videoExplorerBody .resultContentsWrap {
  9515. width: 592px; padding: 16px 0px;
  9516. }
  9517. #videoExplorer.w_adjusted .videoExplorerMenu, #content .videoExplorerMenu:not(.initialized) { display: none; }
  9518. .videoExplorerMenu {
  9519. transition: margin-top 0.4s ease 0.4s; {*, left 0.4s ease-in-out*};
  9520. }
  9521. #leftPanel {
  9522. {* transition: width 0.4s ease 0.4s, height 0.4s ease 0.4s, top 0.4s ease 0.4s, left 0.4s ease 0.4s;*}
  9523. transition: width 0.4s ease 0.4s, height 0.4s ease 0.4s, left 0.4s ease 0.4s;
  9524. }
  9525. #content.w_adjusted #playlist {
  9526. min-width: 592px;
  9527. }
  9528. #content.w_adjusted .videoExplorerMenu:not(.w_touch) .itemList>li, body.videoExplorer #content.w_adjusted #videoExplorerExpand {
  9529. height: 26px;
  9530. }
  9531. #content.w_adjusted .videoExplorerMenu:not(.w_touch) .itemList>li>a,body.videoExplorer #content.w_adjusted #videoExplorerExpand a{
  9532. line-height: 26px; font-size: 100%;
  9533. }
  9534. .errorMessage {
  9535. max-height: 0; line-height: 30px; overflow: hidden; text-align: center; color: #f88; cursor: pointer;
  9536. transition: max-height 0.8s ease;
  9537. }
  9538. .videoErrorOccurred .errorMessage {
  9539. max-height: 100px;
  9540. }
  9541.  
  9542. .w_adjusted .videoExplorerMenu .itemList li .arrow {
  9543. top: 8px;
  9544. }
  9545.  
  9546.  
  9547. .w_adjusted .videoExplorerMenu .closeVideoExplorer {
  9548. width: 300px; position: relative; padding: 2px 10px; background: #f5f5f5;
  9549. }
  9550. .w_adjusted .videoExplorerMenu .closeVideoExplorer:hover {
  9551. background: #dbdbdb;
  9552. }
  9553. .w_adjusted .videoExplorerMenu .closeVideoExplorer a{
  9554. display: block;line-height: 26px; {*color: #CC0000;*}
  9555. }
  9556.  
  9557. #searchResultNavigation > ul > li a:after, #content.w_adjusted #videoExplorerExpand a#closeSearchResultExplorer:after {
  9558. top: 8px;
  9559. }
  9560.  
  9561.  
  9562.  
  9563.  
  9564. #content.w_adjusted #playerContainerWrapper, #content.w_adjusted #playlist { box-shadow: none; }
  9565. #content.w_adjusted #videoExplorerExpand .arrow { display: none; }
  9566.  
  9567. body.videoExplorer #footer.w_adjusted {
  9568. display: none;
  9569. }
  9570. .w_adjusted .uadTagRelated .default .itemList .item .videoTitleContainer {
  9571. width: 130px;
  9572. text-align: center;
  9573. }
  9574. .w_adjusted .uadTagRelated .uadTagRelated {
  9575. margin-bottom: 30px;
  9576. }
  9577. .w_adjusted .uadTagRelated .itemList .item,
  9578. .w_adjusted .uadTagRelated .emptyItem,
  9579. .w_adjusted .uadTagRelated .default .landing {
  9580. width: 130px; margin: 0 10px 0 8px;
  9581. }
  9582. .w_adjusted .uadTagRelated .default .itemList .item .imageContainer .itemImageWrapper .itemImage {
  9583. width: 130px; height: auto; top: 0; left: 0;
  9584. }
  9585. .w_adjusted .uadTagRelated .default .itemList .item .imageContainer .itemImageWrapper {
  9586. width: 130px; height: 100px;
  9587. }
  9588. .w_adjusted .uadTagRelated .emptyItem .emptyMessageContainer {
  9589. width: 130px; height: 100px;
  9590. }
  9591. .w_adjusted .videoExplorerContent .column1 .videoInformationOuter .link,
  9592. .w_adjusted .videoExplorerContent .column1 .videoInformationOuter .link .title {
  9593. display: inline;
  9594. }
  9595.  
  9596. #videoExplorer.w_adjusted .videoExplorerContent .column1 .commentBlank {
  9597. width: 96%;
  9598. }
  9599. #videoExplorer.w_adjusted .videoExplorerContent .column4 .commentBlank {
  9600. width: 24%;
  9601. }
  9602. .videoExplorerBody .videoExplorerContent .contentItemList.column4 .item .createdTime .submit
  9603. {
  9604. display: none !important;
  9605. }
  9606. .nicorepoResult .column4 .videoInformation {
  9607. display: none;
  9608. }
  9609.  
  9610. #videoExplorer .pager { margin-right: 20px; }
  9611. #videoExplorer .contentItemList { clear: both; }
  9612.  
  9613. body.videoExplorer #content.w_adjusted #playerContainerWrapper { overflow: visible; }
  9614. body.videoExplorer #videoExplorer.w_adjusted .videoExplorerContent { padding: 20px 0px; }
  9615. body.videoExplorer #videoExplorer.w_adjusted .videoExplorerContentWrapper
  9616. { margin-left: 0; padding: 20px 340px 20px 0px; }
  9617. body.videoExplorer.playlist #videoExplorer.w_adjusted .videoExplorerContentWrapper
  9618. { margin-left: 0; padding: 164px 340px 20px 0px; }
  9619.  
  9620. {* 謎のスペーサー *}
  9621. {*body.videoExplorer #content.w_adjusted .videoExplorerMenu>div:first *}
  9622. {* body.videoExplorer #content.w_adjusted .videoExplorerMenu>div:not(.videoExplorerMenuInner) { display: none; } *}
  9623. body.videoExplorer #content.w_adjusted .videoExplorerMenu>div:nth-child(1) { display: none; }
  9624.  
  9625. body.videoExplorer #content.w_adjusted .videoExplorerMenu
  9626. { width: 300px; }
  9627.  
  9628. body.videoExplorer #content.w_adjusted .videoExplorerMenuInner
  9629. { position: static !important; top: 0 !important; left: 0 !important; }
  9630.  
  9631. body.videoExplorer #bottomContentTabContainer.w_adjusted { {*background: #ccc;*} }
  9632. body:not(.videoExplorer) .videoExplorerMenu { display: none; }
  9633.  
  9634. body.videoExplorer #content.w_adjusted #nicoplayerContainer {
  9635. z-index: 100;
  9636. }
  9637. body.videoExplorer #content.w_adjusted #playerTabWrapper {
  9638. top: 0px !important; background: #dfdfdf; border-radius: 4px;
  9639. z-index: 99;
  9640. transition: right 0.3s ease-out;
  9641. }
  9642. #videoExplorer.w_adjusted .videoExplorerBody .videoExplorerContent .contentItemList.column1 .item {
  9643. margin-left: 8px;
  9644. }
  9645. #videoExplorer.w_adjusted .videoExplorerBody .videoExplorerContent .contentItemList.column4 .item {
  9646. width: 130px; margin-left: 8px; margin-right: 10px;
  9647. }
  9648. #videoExplorer.w_adjusted .videoExplorerBody .videoExplorerContent .column1 .videoInformationOuter {
  9649. width: 414px;
  9650. }
  9651. #videoExplorer.w_adjusted .videoExplorerBody .videoExplorerContent .column1 .nicorepoResult .videoInformationOuter {
  9652. width: auto;
  9653. }
  9654. #videoExplorer.w_adjusted .contentItemList .folder .column1 .description,
  9655. #videoExplorer.w_adjusted .suggestVideo .folder .column1 .description,
  9656. #videoExplorer.w_adjusted .descriptionShort {
  9657. width: 410px;
  9658. }
  9659.  
  9660. #videoExplorer .descriptionShort {
  9661. line-height: 1.5;
  9662. margin: 0 0 4px;
  9663. word-break: break-all;
  9664. clear: both;
  9665. color: #666;
  9666. font-size: 93%;
  9667. }
  9668.  
  9669. .w_adjusted .column1 .smallThumbnail .createdTime.at {
  9670. width: 130px;
  9671. }
  9672. .w_adjusted .column1 .createdTime.at {
  9673. width: 160px; text-align: center;
  9674. }
  9675. .w_adjusted .createdTime {
  9676. white-space: nowrap;
  9677. }
  9678. #videoExplorer.w_adjusted .videoExplorerBody .videoExplorerContent .contentItemList .folder .container,
  9679. #videoExplorer.w_adjusted .videoExplorerBody .videoExplorerContent .suggestVideo .folder .container {
  9680. background-position: -15px -270px; width: 130px; height: 100px;
  9681. }
  9682.  
  9683. body.size_small.no_setting_panel.videoExplorer #content #videoExplorerExpand { {*「閉じる」ボタン *}
  9684. position: static; top: auto; left: auto; margin-top: 0;',
  9685. }
  9686. body.videoExplorer #content.w_adjusted #playerTabWrapper #playerCommentPanel {
  9687. display: none;
  9688. }
  9689.  
  9690. .videoExplorerMenu .item:hover {
  9691. background: #dbdbdb; text-decoration: underline;
  9692. }
  9693. .videoExplorerMenu .item {
  9694. position: relative; border-bottom: 1px solid #CCCCCC; background: #f2f2f2;
  9695. text-decoration: none; cursor: pointer;
  9696. }
  9697. .videoExplorerMenu .item .arrow {
  9698. display: block; position: absolute; top: 14px; right: 12px; width: 9px; height: 12px;
  9699. background: url("http://res.nimg.jp/img/watch_q9/video_explorer/icon_normal.png") no-repeat 0 0;
  9700. }
  9701. .videoExplorerMenu .item .text {
  9702. position: relative; width: 100%; height: 100%; display: block; text-align: left;
  9703. text-decoration: none; padding: 0 12px; color: #000; box-sizing: border-box;
  9704. line-height: 26px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box;
  9705. }
  9706. .videoExplorerMenu .itemList>li:first {
  9707. position: relative;
  9708. }
  9709.  
  9710. #videoExplorer .videoExplorerMenu .closeVideoExplorer, #videoExplorer .quickSearchInput {
  9711. display: none;
  9712. }
  9713.  
  9714. #videoExplorer .videoExplorerBody .videoExplorerContent .contentItemList.column1 .video .column1 .videoInformationOuter .title
  9715. {
  9716. white-space: normal;
  9717. }
  9718.  
  9719. .videoExplorer #playlist {
  9720. transition: margin-left 0.4s ease 0.4s;
  9721. }
  9722.  
  9723. #playerAlignmentArea .toggleCommentPanel {
  9724. display: none;
  9725. position: absolute;
  9726. right: -119px;
  9727. bottom: 70px;
  9728. width: 100px;
  9729. height: 30px;
  9730. cursor: pointer;
  9731. outline: none;
  9732. background: #ccc;
  9733. border-radius: 16px 16px 0 0;
  9734. border: solid 1px black;
  9735. -webkit-transform: rotate(90deg); -webkit-transform-origin: 0 0 0;
  9736. transform: rotate(90deg); transform-origin: 0 0 0;
  9737. transition: 0.4s ease-in-out;
  9738. -webkit-transition: 0.4s ease-in-out;
  9739. }
  9740. #playerAlignmentArea .toggleCommentPanel::-moz-focus-inner {
  9741. border: 0px;
  9742. }
  9743. body.videoExplorer .w_adjusted #playerAlignmentArea .toggleCommentPanel { display: block; }
  9744. #playerAlignmentArea .toggleCommentPanel:before {
  9745. content: '↑ ';
  9746. }
  9747. #playerAlignmentArea .toggleCommentPanel:hover {
  9748. right: -129px;
  9749. }
  9750. #playerAlignmentArea .toggleCommentPanel.w_active {
  9751. right: -418px;
  9752. bottom: -29px;
  9753. border-radius: 0 0 16px 16px;
  9754. z-index: 10000;
  9755. -webkit-transform: rotate(360deg);
  9756. transform: rotate(360deg);
  9757. transition: none;
  9758. -webkit-transition: 0.4s ease-in-out;
  9759. {*transition: 0.4s ease-in-out;*}
  9760. }
  9761. #playerAlignmentArea .toggleCommentPanel.w_active:hover {
  9762. {*box-shadow: 2px 2px 2px #888;*}
  9763. right: -418px;
  9764. }
  9765. #playerAlignmentArea .toggleCommentPanel.w_active:before {
  9766. content: '← ';
  9767. }
  9768.  
  9769. .videoExplorerOpening .videoExplorerBody .videoExplorerConfig {
  9770. display: none;
  9771. }
  9772. .videoExplorerBody .videoExplorerConfig {
  9773. cursor: pointer;
  9774. width: 80px; margin-left: -36px; white-space: nowrap;
  9775. border-radius: 0 32px 0 0; border: solid 1px #666; border-width: 1px 1px 0;
  9776. color: #fff; background: #aaa;
  9777. }
  9778. #videoExplorer.w_adjusted .videoExplorerConfig .open,
  9779. #videoExplorer:not(.w_adjusted) .videoExplorerConfig .close {
  9780. display: none;
  9781. }
  9782. .videoExplorerConfig::-moz-focus-inner { border: 0px; }
  9783.  
  9784. .videoExplorer #playerContainer.appli_panel #appliPanel {
  9785. width: auto !important; background: #333;
  9786. }
  9787. .videoExplorerContent {
  9788. background: #fff;
  9789. }
  9790. */});
  9791. return addStyle(__css__, 'videoExplorerStyleStatic');
  9792. } // end setupVideoExplorerStaticCss
  9793.  
  9794. function initAutoComplete($searchInput) {
  9795. var
  9796. $suggestList = $('<datalist id="quickSearchSuggestList"></datalist>'),
  9797. wordSuggest = '',
  9798. favTagsSuggest = '',
  9799. loading = false,
  9800. val = '',
  9801. suggestLoader = new NicoSearchSuggest({}),
  9802. update = _.debounce(function() {
  9803. if (loading) {
  9804. return;
  9805. }
  9806. var value = $searchInput.val();
  9807. if (value.length >= 1 && val !== value) {
  9808. val = $searchInput.val();
  9809. loading = true;
  9810. suggestLoader.load(val, onSuggestLoaded);
  9811. } else {
  9812. loading = false;
  9813. }
  9814. }, 300),
  9815. onSuggestLoaded = function(err, result) {
  9816. if (err) {
  9817. return;
  9818. }
  9819. if (result.candidates) {
  9820. console.log(result.candidates);
  9821. var candidates = result.candidates;
  9822. var options = [];
  9823. for (var i = candidates.length - 1; i >= 0; i--) {
  9824. options.unshift(['<option value="', candidates[i], '"></option>'].join(''));
  9825. }
  9826. wordSuggest = options.join('');
  9827. refresh();
  9828. }
  9829. loading = false;
  9830. },
  9831. refresh = function() {
  9832. $suggestList.html(wordSuggest + favTagsSuggest);
  9833. },
  9834. bind = function($elm) {
  9835. $elm
  9836. .on('focus', update)
  9837. .on('keydown', update)
  9838. .on('keyup', update)
  9839. .on('keypress', update)
  9840. .on('click', update)
  9841. .on('mousedown', update)
  9842. .on('mouseup', update)
  9843. .attr({'autocomplete': 'on', 'list': 'quickSearchSuggestList', 'placeholder': '検索ワードを入力(Q)'});
  9844. // try {
  9845. // //$elm.attr('type', 'search');
  9846. // //$elm[0].setAttribute('type', 'search');//.attr('type', 'search');
  9847. // } catch (e) {
  9848. // console.log(e);
  9849. // }
  9850. };
  9851.  
  9852. EventDispatcher.addEventListener('onFavTagsLoad', function(tags) {
  9853. var options = [];
  9854. for (var i = tags.length - 1; i >= 0; i--) {
  9855. options.unshift(['<option value="', tags[i], '"></option>'].join(''));
  9856. }
  9857. favTagsSuggest = options.join('');
  9858. refresh();
  9859. });
  9860. $('body').append($suggestList);
  9861.  
  9862. bind($searchInput);
  9863. } //
  9864.  
  9865. function initVideoExplorer($, conf, w) {
  9866. setupVideoExplorerStaticCss();
  9867.  
  9868. var
  9869. _vp = WatchApp.ns.components.videoexplorer,
  9870. initializer = watch.VideoExplorerInitializer,
  9871. controller = initializer.videoExplorerController,
  9872. explorer = controller.getVideoExplorer(),
  9873. explorerConfig = _vp.config.VideoExplorerConfig,
  9874. menu = explorer.getMenu(),
  9875. ContentItemType = _vp.model.ContentItemType,
  9876. ContentType = _vp.model.ContentType,
  9877. watchPageRouter = WatchApp.ns.model.state.WatchPageRouter.getInstance(),
  9878. playerConnector = watch.PlayerInitializer.nicoPlayerConnector,
  9879. searchType = 'tag',
  9880. $menu = $('.videoExplorerMenu'),
  9881. $searchInput = $('<input class="quickSearchInput" type="search" name="q" accesskey="q" required="required" x-webkit-speech />')
  9882. .attr({'title': '検索ワードを入力', 'placeholder': '検索ワードを入力(Q)'}),
  9883. $closeExplorer = $('<div class="closeVideoExplorer"><a href="javascript:;">▲ 画面を戻す</a></div>'),
  9884. $inputForm = $('<form action="javascript:void(0);" />').append($searchInput),
  9885. $toggleCommentPanel = $('<button class="toggleCommentPanel">コメント</button>');
  9886.  
  9887. // init search menu
  9888. $searchInput.on('keyup', function(e) {
  9889. $('.searchText input').val(this.value);
  9890. }).on('click', function(e) {
  9891. e.stopPropagation();
  9892. });
  9893. $inputForm.on('submit', function(e) {
  9894. //e.preventDefault();
  9895. var val = $.trim($searchInput.val());
  9896. if (val.length > 0) {
  9897. if (val.match(/(sm|nm|so)\d+/)) {
  9898. WatchController.nicoSearch(val, 'tag');
  9899. } else {
  9900. WatchController.nicoSearch(val, 'keyword');
  9901. }
  9902. }
  9903. });
  9904.  
  9905. var clearButton = new window.Nico.ClearButton({targetInput: $searchInput});
  9906. EventDispatcher.addEventListener('onSearchStart', function(word, type) {
  9907. searchType = type.replace(/^.*\./, '');
  9908. $searchInput.val(word);
  9909. window.setTimeout(function() {clearButton.refresh(); }, 0);
  9910. });
  9911. initAutoComplete($searchInput);
  9912.  
  9913. $closeExplorer.find('a').on('click', function() {
  9914. WatchController.closeSearch();
  9915. });
  9916.  
  9917. // メニュー拡張
  9918. var
  9919. detachMenuItems = function() {
  9920. WatchItLater.videoExplorerMenu.myShortcuts .detach();
  9921. WatchItLater.videoExplorerMenu.videoRanking.detach();
  9922. WatchItLater.videoExplorerMenu.favTags .detach();
  9923. WatchItLater.videoExplorerMenu.favMylists .detach();
  9924.  
  9925. $inputForm.detach();
  9926. $closeExplorer.detach();
  9927. },
  9928. attachMenuItems = function() {
  9929. if (conf.enableFavTags ) { WatchItLater.videoExplorerMenu.favTags.attach(); }
  9930. if (conf.enableFavMylists) { WatchItLater.videoExplorerMenu.favMylists.attach(); }
  9931. WatchItLater.videoExplorerMenu.videoRanking.attach();
  9932. WatchItLater.videoExplorerMenu.myShortcuts.attach();
  9933.  
  9934.  
  9935. $('.videoExplorerMenu')
  9936. .find('.itemList>li:first').append($inputForm)
  9937. .end().find('.errorMessage').after($closeExplorer);
  9938. };
  9939. controller._refreshMenu_org = controller._refreshMenu;
  9940. controller._refreshMenu = $.proxy(function() {
  9941. detachMenuItems();
  9942. this._refreshMenu_org();
  9943. attachMenuItems();
  9944. }, controller);
  9945. controller.showDeflist_org = controller.showDeflist;
  9946. controller.showMylist_org = controller.showMylist;
  9947. controller.showOtherUserVideos_org = controller.showOtherUserVideos;
  9948. controller.showOwnerVideo_org = controller.showOwnerVideo;
  9949. controller.searchVideo_org = controller.searchVideo;
  9950. controller.showDeflist = $.proxy(function() {
  9951. if (conf.disableVideoExplorer && !WatchController.isSearchMode()) {
  9952. location.href = "/my/mylist";
  9953. return;
  9954. }
  9955. this.showDeflist_org();
  9956. }, controller);
  9957. controller.showMylist = $.proxy(function(id) {
  9958. if (conf.disableVideoExplorer && !WatchController.isSearchMode()) {
  9959. location.href = "/mylist/" + id;
  9960. return;
  9961. }
  9962. this.showMylist_org(id);
  9963. }, controller);
  9964. controller.showOtherUserVideos = $.proxy(function(id, name) {
  9965. if (conf.disableVideoExplorer && !WatchController.isSearchMode()) {
  9966. location.href = "/user/" + id;
  9967. return;
  9968. }
  9969. this.showOtherUserVideos_org(id, name);
  9970. }, controller);
  9971. controller.showOwnerVideo = $.proxy(function() {
  9972. if (conf.disableVideoExplorer && !WatchController.isSearchMode()) {
  9973. location.href = "/user/" + WatchController.getOwnerId();
  9974. return;
  9975. }
  9976. this.showOwnerVideo_org();
  9977. }, controller);
  9978. controller.searchVideo = $.proxy(function(word, type) {
  9979. if (conf.disableVideoExplorer && !WatchController.isSearchMode()) {
  9980. var sortOrder = '?sort=' + conf.searchSortType + '&order=' + conf.searchSortOrder;
  9981. location.href = (type === 'tag' ? 'tag' : 'search') + "/" + encodeURIComponent(word) + sortOrder;
  9982. return;
  9983. }
  9984. this.searchVideo_org(word, type);
  9985. }, controller);
  9986.  
  9987. EventDispatcher.addEventListener('onBottomContentTabViewReset', function(name) {
  9988. if (name === 'outline') {
  9989. WatchController.closeSearch();
  9990. }
  9991. });
  9992.  
  9993.  
  9994. EventDispatcher.addEventListener('on.config.enableFavTags',
  9995. function() { if (WatchController.isSearchMode()) { controller._refreshMenu(); }});
  9996. EventDispatcher.addEventListener('on.config.enableFavMylists',
  9997. function() { if (WatchController.isSearchMode()) { controller._refreshMenu(); }});
  9998.  
  9999. EventDispatcher.addEventListener('onVideoExplorerOpened', function(content) {
  10000. setTimeout(function() {
  10001. if (conf.videoExplorerHack) {
  10002. watch.PlayerInitializer.commentPanelViewController.contentManager.activeContent().refresh();
  10003. playerConnector.updatePlayerConfig({playerViewSize: ''}); // ノーマル画面モード
  10004. }
  10005. }, 100);
  10006. clearButton.refresh();
  10007.  
  10008. $('body').removeClass('videoExplorerOpening');
  10009. $('.videoExplorerMenu').addClass('initialized');
  10010. });
  10011. EventDispatcher.addEventListener('onVideoExplorerRefreshEnd', function(content) {
  10012. if (content.getType() === ContentType.USER_VIDEO) {
  10013. var items = content.getItems();
  10014. if (items.length === 1 && items[0].getContentItemType() !== ContentItemType.VIDEO) {
  10015. // ユーザーの投稿動画一覧が公開マイリスト一つだけだったら自動でそれを開く
  10016. items[0].stepIn();
  10017. return;
  10018. }
  10019. }
  10020. });
  10021. EventDispatcher.addEventListener('onVideoExplorerRefreshStart', function(content) {
  10022. window.WatchApp.ns.util.WindowUtil.scrollFit('#videoExplorer');
  10023. });
  10024. EventDispatcher.addEventListener('onVideoExplorerOpening', function(content) {
  10025. $('body').addClass('videoExplorerOpening');
  10026. adjustVideoExplorerSize(true);
  10027. });
  10028. EventDispatcher.addEventListener('onVideoExplorerClosing', function(content) {
  10029. });
  10030.  
  10031. EventDispatcher.addEventListener('onBeforeVideoExplorerMenuClear', function() {
  10032. detachMenuItems();
  10033. });
  10034.  
  10035. EventDispatcher.addEventListener('onUpdateSettingPanelVisible', function(isVisible, panel) {
  10036. if (isVisible && WatchController.isSearchMode()) {
  10037. setTimeout(function() {
  10038. WatchController.closeSearch();
  10039. setTimeout(function() {
  10040. playerConnector.updateSettingsPanelVisible(true, panel);
  10041. }, 800);
  10042. }, 100);
  10043. }
  10044. });
  10045.  
  10046. EventDispatcher.addEventListener('onFirstVideoExplorerOpened', function() {
  10047. window.console.time('onFirstVideoExplorerOpen');
  10048. EventDispatcher.addEventListener('onWindowResizeEnd', adjustVideoExplorerSize);
  10049. EventDispatcher.addEventListener('onVideoInitialized', adjustVideoExplorerSize);
  10050. window.console.timeEnd('onFirstVideoExplorerOpen');
  10051. });
  10052.  
  10053. var duration_match = /^([0-9]+):([0-9]+)/;
  10054. controller._item2playlistItem = function (item) {
  10055. // 動画長が入るようにする
  10056. var length_seconds = 0, len = item.getLength ? item.getLength() : '', m;
  10057. if (typeof len === 'string' && (m = duration_match.exec(len)) !== null) {
  10058. length_seconds = m[1] * 60 + m[2] * 1;
  10059. }
  10060. return new WatchApp.ns.model.playlist.PlaylistItem({
  10061. id : item.getId(),
  10062. title : item.getTitle(),
  10063. thumbnail_url : item.getThumbnailUrl(),
  10064. view_counter : item.getViewCounter(),
  10065. num_res : item.getNumRes(),
  10066. mylist_counter: item.getMylistCounter(),
  10067. mylist_comment: item.getMylistComment(),
  10068. first_retrieve: item.getFirstRetrieve(),
  10069. ads_counter : item.getUadCounter(),
  10070. length_seconds: length_seconds
  10071. });
  10072. };
  10073.  
  10074. initVideoExplorerItemContent();
  10075.  
  10076. $('#playerAlignmentArea').append($toggleCommentPanel);
  10077. $toggleCommentPanel.on('click', function() {
  10078. AnchorHoverPopup.hidePopup();
  10079. $('#playerTabWrapper').toggleClass('w_active');
  10080. $toggleCommentPanel.toggleClass('w_active', $('#playerTabWrapper').hasClass('w_active'));
  10081. setTimeout(function() {
  10082. watch.PlayerInitializer.commentPanelViewController.contentManager.activeContent().refresh();
  10083. }, 1000);
  10084. }).on('mouseover', function() {
  10085. AnchorHoverPopup.hidePopup();
  10086. });
  10087.  
  10088. var toggleVideoExplorerHack = function(v) {
  10089. $('#videoExplorer, #content, #footer, #bottomContentTabContainer').toggleClass('w_adjusted', v);
  10090. if (v) {
  10091. $('#content').append($('.videoExplorerMenu'));
  10092. if (WatchController.isSearchMode()) {
  10093. playerConnector.updatePlayerConfig({playerViewSize: ''}); // ノーマル画面モード
  10094. adjustVideoExplorerSize(true);
  10095. }
  10096. } else {
  10097. $('.videoExplorerContentWrapper').before($('.videoExplorerMenu'));
  10098. setTimeout(function() {
  10099. if (WatchController.isSearchMode()) {
  10100. playerConnector.updatePlayerConfig({playerViewSize: 'small'});
  10101. WatchApp.ns.util.WindowUtil.shake();
  10102. }
  10103. }, 1000);
  10104. }
  10105. };
  10106. EventDispatcher.addEventListener('on.config.videoExplorerHack', toggleVideoExplorerHack);
  10107. toggleVideoExplorerHack(conf.videoExplorerHack);
  10108.  
  10109.  
  10110. watchPageRouter._prepareState_org = watchPageRouter._prepareState;
  10111. watchPageRouter._prepareState = $.proxy(function(state) {
  10112. if (
  10113. conf.videoExplorerHack &&
  10114. WatchController.isSearchMode() &&
  10115. state.getVideoId() !== this._currentState.getVideoId()
  10116. ) {
  10117. state.prepare({
  10118. video: {id: this._watchInfoModel.v}
  10119. });
  10120. return state;
  10121. } else {
  10122. return this._prepareState_org(state);
  10123. }
  10124. }, watchPageRouter);
  10125. window.WatchApp.ns.model.state.WatchPageState.prototype.isVideoStateChange =
  10126. function(state) {
  10127. return this.getVideoId() !== state.getVideoId();
  10128. };
  10129.  
  10130.  
  10131. } // end initVideoExplorer
  10132.  
  10133. function initVideoExplorerItemContent() {
  10134.  
  10135. // 動画情報表示のテンプレートを拡張
  10136. var
  10137. overrideItemTemplate = function() {
  10138. var menu =
  10139. '<div class="thumbnailHoverMenu">' +
  10140. '<button class="showLargeThumbnail" onclick="WatchItLater.onShowLargeThumbnailClick(this);" title="大きいサムネイルを表示">+</button>' +
  10141. '<button class="deleteFromMyMylist" onclick="WatchItLater.onDeleteFromMyMylistClick(this);">マイリスト外す</button>' +
  10142. '</div>', $menu = $(menu);
  10143.  
  10144. var $template = $('<div/>').html(watch.VideoExplorerInitializer.videoExplorerView._contentListView._$view.find('.videoItemTemplate').html());
  10145. $template.find('.column1 .thumbnailContainer').append($menu).end()
  10146. .find('.column4 .balloon').before($menu.clone()).end()
  10147. .find('.column4 .balloon').remove().end()
  10148. .find('.messageContainer').remove().end()
  10149. .find('.lastResBody')
  10150. .before($('<p class="descriptionShort"/><p class="itemMylistComment mylistComment"/>')).end()
  10151. .find('.noImage').remove().end()
  10152. //.find.remove('div.descriptionShort').end()
  10153. // .before($('<p class="descriptionShort"/>')).end()
  10154. // .find('.descriptionShort')
  10155. // .after($('<p class="itemMylistComment mylistComment"/>')).end()
  10156. .find('.createdTime').after($('<div class="nicorepoOwnerIconContainer"><a target="_blank"><img /></a></div>'));
  10157. watch.VideoExplorerInitializer.videoExplorerView._contentListView._$view.find('.videoItemTemplate').html($template.html());
  10158. $template = $menu = null;
  10159.  
  10160. },
  10161. onDeleteFromMyMylistClick = function(elm) {
  10162. var ContentType = WatchApp.ns.components.videoexplorer.model.ContentType;
  10163. var contentList = WatchApp.ns.init.VideoExplorerInitializer.videoExplorer.getContentList();
  10164. var
  10165. $elm = $(elm),
  10166. $item = $elm.closest('.item'),
  10167. $videoItem = $item, //.find(''),//$elm.parent().parent(),
  10168. watchId = $item.attr('data-watch-id'),
  10169. ac = contentList.getActiveContent(),
  10170. type = contentList.getActiveContentType(),
  10171. onUpdate = function(status, result) {
  10172. if (status !== "ok") {
  10173. Popup.alert('削除に失敗: ' + result.error.description);
  10174. } else {
  10175. $videoItem.animate({opacity: 0.3}, 500);
  10176. }
  10177. };
  10178.  
  10179. if (type === ContentType.MYLIST_VIDEO) {
  10180. if (!ac.getIsMine()) { return; }
  10181. Mylist.deleteMylistItem(watchId, ac.getMylistId(), onUpdate, 0);
  10182. } else
  10183. if (type === ContentType.DEFLIST_VIDEO) {
  10184. Mylist.deleteDefListItem(watchId, onUpdate, 0);
  10185. }
  10186. },
  10187. onShowLargeThumbnailClick = function (elm) {
  10188. var $item = $(elm).closest('.item');
  10189. var src = $item.attr('data-thumbnail');
  10190. if (!src) { return; }
  10191. showLargeThumbnail(src);
  10192. };
  10193. overrideItemTemplate();
  10194. WatchItLater.onDeleteFromMyMylistClick = onDeleteFromMyMylistClick;
  10195. WatchItLater.onShowLargeThumbnailClick = onShowLargeThumbnailClick;
  10196.  
  10197. // 動画情報表示の拡張
  10198. var ItemView = WatchApp.ns.components.videoexplorer.view.content.item.AbstractVideoContentItemView;
  10199. ItemView.prototype._setView_org = ItemView.prototype._setView;
  10200.  
  10201. ItemView.prototype.update_org = ItemView.prototype.update;
  10202. ItemView.prototype.update = function() {
  10203. // 動画情報表示をゴリゴリいじる場所
  10204. var item = this._item, $item = this._$item;
  10205. this.update_org(item);
  10206.  
  10207. this._$item.find('.deleteFromMyMylist').data('watchId', this._item.getId());
  10208. if (item._mylistComment) { // マイリストコメント
  10209. $item.find('.mylistComment').css({display: 'block'});
  10210. } else {
  10211. $item.find('.mylistComment').remove();
  10212. }
  10213.  
  10214. var lastResBody = item.getLastResBody();
  10215. if (lastResBody.length > 0) {
  10216. this._$lastResBody.css('cssText', 'display: block !important').text(lastResBody);
  10217. } else {
  10218. this._$lastResBody.remove();
  10219. }
  10220.  
  10221. var thumbnail = this._$thumbnail.attr('src');
  10222. if (item.isMiddleThumbnail()) {
  10223. this._$item.find('.thumbnailContainer')
  10224. .css('background-image', 'url(' + thumbnail + ')');
  10225. this._$thumbnail.remove();
  10226. } else {
  10227. this._$item
  10228. .addClass('smallThumbnail')
  10229. .find('.thumbnailContainer')
  10230. .css('background-image', 'url(' + thumbnail + ')');
  10231. }
  10232. $item.attr({
  10233. 'data-thumbnail': thumbnail,
  10234. 'data-watch-id': item.getId()
  10235. });
  10236.  
  10237.  
  10238. if (item._seed && item._seed._info) {
  10239. var info = item._seed._info;
  10240. if (info.nicorepo_owner) { // ニコレポ
  10241. $item.addClass(info.nicorepo_className).addClass('nicorepoResult');
  10242. var owner = info.nicorepo_owner;
  10243. var $iconContainer = $item.find('.nicorepoOwnerIconContainer'), $icon = $iconContainer.find('img'), $link = $iconContainer.find('a');
  10244. $icon.attr('src', owner.icon);
  10245. $link.attr({'href': owner.page, 'data-ownerid': owner.id, 'title': owner.name + ' さん'});
  10246. if (info.nicorepo_className.indexOf('log-user-') >= 0) {
  10247. $link.attr(
  10248. 'onclick',
  10249. 'if (arguments[0].button > 0) return; arguments[0].preventDefault();' +
  10250. 'WatchApp.ns.init.VideoExplorerInitializer.videoExplorerController.showOtherUserVideos(this.dataset.ownerid, this.title);' +
  10251. 'WatchApp.ns.util.WindowUtil.scrollFit($("#videoExplorer"));'
  10252. );
  10253. }
  10254. if (info.nicorepo_log.length > 1) {
  10255. $item.find('.descriptionShort').html(info.nicorepo_log.join('<br>'));
  10256. }
  10257. // ニコレポは再生数が取れないので-で埋める
  10258. this._$viewCount .html('-');
  10259. this._$commentCount.html('-');
  10260. this._$mylistCount .html('-');
  10261. $iconContainer = $icon = $link = null;
  10262. }
  10263. }
  10264. if (item._seed && typeof item._seed.description_full === 'string' && item._seed.description_full.length > 150) {
  10265. this._$descriptionShort.attr('title', item._seed.description_full);
  10266. }
  10267. $item = null;
  10268.  
  10269. };
  10270. ItemView = null;
  10271.  
  10272. } // end initVideoExplorerItemContent
  10273.  
  10274.  
  10275.  
  10276. var lastVideoOwnerJson = '';
  10277. function onWatchInfoReset(watchInfoModel) {
  10278. $('body').toggleClass('w_channel', watchInfoModel.isChannelVideo());
  10279. EventDispatcher.dispatch('onWatchInfoReset', watchInfoModel);
  10280. var owner = WatchController.getOwnerInfo(), owner_json = JSON.stringify(owner);
  10281. if (lastVideoOwnerJson.length > 0 && lastVideoOwnerJson !== owner_json) {
  10282. EventDispatcher.dispatch('onVideoOwnerChanged', owner);
  10283. }
  10284. lastVideoOwnerJson = owner_json;
  10285. }
  10286.  
  10287. function onScreenModeChange(sc) {
  10288. setTimeout(function() {
  10289. EventDispatcher.dispatch('onScreenModeChange', sc);
  10290. }, 500);
  10291. }
  10292.  
  10293. function initMylistPanel($, conf, w) {
  10294. var iframe = Mylist.getPanel('');
  10295. iframe.id = "mylist_add_frame";
  10296. iframe.className += " fixed";
  10297. w.document.body.appendChild(iframe);
  10298. iframe.hide(); // ページの初期化が終わるまでは表示しない
  10299.  
  10300. var $iframe = $(iframe);
  10301. $iframe.find('.mylistSelect').attr('accesskey', ':');
  10302.  
  10303. var toggleMylistMenuInFull = function(v) {
  10304. $('.mylistPopupPanel')
  10305. .toggleClass('hideInFull', v === 'hide')
  10306. .toggleClass('hideAllInFull', v === 'hideAll');
  10307. };
  10308. EventDispatcher.addEventListener('on.config.hideMenuInFull', toggleMylistMenuInFull);
  10309. toggleMylistMenuInFull(conf.hideMenuInFull);
  10310.  
  10311. var setMylistPanelPosition = function(v) {
  10312. $iframe
  10313. .toggleClass('left', v.indexOf('left') >= 0)
  10314. .toggleClass('top', v.indexOf('top') >= 0);
  10315. setTimeout(function() {
  10316. $('#yukkuriPanel')
  10317. .toggleClass('mylistPanelLeft', v.indexOf('left') >= 0 && v.indexOf('top') < 0);
  10318. }, 500);
  10319. };
  10320. EventDispatcher.addEventListener('on.config.mylistPanelPosition', setMylistPanelPosition);
  10321. if (conf.mylistPanelPosition !== '') setMylistPanelPosition(conf.mylistPanelPosition);
  10322.  
  10323. var $footer = $('#footer'), $window = $(window);
  10324. var toggleMylistPanelStyle = function() {
  10325. if ($footer.is(':visible')) {
  10326. $iframe.toggleClass('black', $window.scrollTop() + $window.innerHeight() - $footer.offset().top >= 0);
  10327. } else {
  10328. $iframe.removeClass('black');
  10329. }
  10330. };
  10331.  
  10332. EventDispatcher.addEventListener('onVideoInitialized', function(isFirst) {
  10333. toggleMylistPanelStyle();
  10334. var newVideoId = watchInfoModel.id;
  10335. var newWatchId = watchInfoModel.v;
  10336. iframe.watchId(newVideoId, newWatchId);
  10337. if (isFirst) iframe.show();
  10338. });
  10339. EventDispatcher.addEventListener('onScrollEnd', toggleMylistPanelStyle);
  10340. EventDispatcher.addEventListener('onWindowResizeEnd', toggleMylistPanelStyle);
  10341. EventDispatcher.addEventListener('onScreenModeChange', toggleMylistPanelStyle);
  10342. EventDispatcher.addEventListener('on.config.hidePlaylist', toggleMylistPanelStyle);
  10343. EventDispatcher.addEventListener('on.config.bottomContentsVisibility', toggleMylistPanelStyle);
  10344. } //
  10345.  
  10346. function initScreenMode() {
  10347. EventDispatcher.addEventListener('onVideoInitialized', function(isFirst) {
  10348. if (conf.autoBrowserFull) {
  10349. setTimeout(function() {
  10350. if ($('body').hasClass('up_marquee') && conf.disableAutoBrowserFullIfNicowari) {
  10351. // ユーザーニコ割があるときは自動全画面にしない
  10352. return;
  10353. }
  10354. if (WatchController.isSearchMode()) { // TODO: localStorageに直接アクセスすんな
  10355. var settingSize = (localStorage["PLAYER_SETTINGS.LAST_PLAYER_SIZE"] === '"normal"') ? 'normal' : 'medium';
  10356. WatchController.changeScreenMode(settingSize);
  10357. }
  10358. WatchController.changeScreenMode('browserFull');
  10359. onWindowResizeEnd(); // TODO:;;;;
  10360. }, 100);
  10361. } else {
  10362. if (conf.autoOpenSearch && !WatchController.isSearchMode() && !$('body').hasClass('full_with_browser')) {
  10363. WatchController.openSearch();
  10364. }
  10365. if (conf.autoScrollToPlayer) {
  10366. // 初回のみ、プレイヤーが画面内に納まっていてもタグの位置まで自動スクロールさせる。(ファーストビューを固定するため)
  10367. // 二回目以降は説明文や検索結果からの遷移なので、必要最小限の動きにとどめる
  10368. if (!WatchController.isSearchMode() || isFirst) {
  10369. WatchController.scrollToVideoPlayer(isFirst);
  10370. }
  10371. }
  10372. }
  10373. });
  10374.  
  10375.  
  10376. var lastPlayerConfig = null, lastScreenMode = '';
  10377.  
  10378. function hideIfNeed() {
  10379. if (conf.controllerVisibilityInFull === 'hidden') {
  10380. watch.PlayerInitializer.nicoPlayerConnector.playerConfig.set({oldTypeCommentInput: true, oldTypeControlPanel: false});
  10381. $('body').addClass('hideCommentInput');
  10382. } else {
  10383. var $w = $(window), iw = $w.innerWidth(), ih = $w.innerHeight();
  10384. var controllerH = 46, inputH = 30;
  10385. }
  10386. }
  10387.  
  10388. function restoreVisibility() {
  10389. if (lastPlayerConfig !== null) {
  10390. watch.PlayerInitializer.nicoPlayerConnector.playerConfig.set(
  10391. {oldTypeCommentInput: !!lastPlayerConfig.oldTypeCommentInput, oldTypeControlPanel: !!lastPlayerConfig.oldTypeControlPanel}
  10392. );
  10393. $(window).resize();
  10394. }
  10395. $('body').removeClass('hideCommentInput');
  10396. }
  10397.  
  10398. function toggleTrueBrowserFull(v) {
  10399. v = (typeof v === 'boolean') ? v : !$('body').hasClass('trueBrowserFull');
  10400. $('body').toggleClass('trueBrowserFull', v).toggleClass('full_and_mini', v);
  10401. conf.setValue('enableTrueBrowserFull', v);
  10402. if (!v) {
  10403. watch.PlaylistInitializer.playlistView.resetView();
  10404. }
  10405. return v;
  10406. }
  10407.  
  10408. function initShield() {
  10409. var shield = $('<div id="trueBrowserFullShield" />');
  10410. shield.click(function(e) {
  10411. e.stopPropagation();
  10412. toggleTrueBrowserFull();
  10413. });
  10414. $('#external_nicoplayer').after(shield);
  10415. shield = null;
  10416. }
  10417. initShield();
  10418.  
  10419. EventDispatcher.addEventListener('onScreenModeChange', function(sc) {
  10420. var mode = sc.mode;
  10421. $('body').removeClass('w_fullScreenMenu');
  10422. if (mode === 'browserFull' && lastScreenMode !== mode) {
  10423. lastPlayerConfig = watch.PlayerInitializer.nicoPlayerConnector.playerConfig.get();
  10424. conf.setValue('lastControlPanelPosition', lastPlayerConfig.oldTypeControlPanel ? 'bottom' : 'over');
  10425. //$('body').toggleClass('w_fullWithPlaylist', WatchController.isFullScreenContentAll());
  10426. hideIfNeed();
  10427. if (conf.enableTrueBrowserFull) toggleTrueBrowserFull(conf.enableTrueBrowserFull);
  10428. } else
  10429. if (lastScreenMode === 'browserFull' && mode !== 'browserFull') {
  10430. conf.setValue('lastControlPanelPosition', '');
  10431. $('#playerContainerSlideArea').css({height: ''}); // wall bug fix
  10432. restoreVisibility();
  10433. }
  10434. lastScreenMode = mode;
  10435. });
  10436.  
  10437. $(window).on('beforeunload.watchItLater', function(e) {
  10438. conf.setValue('lastControlPanelPosition', '');
  10439. restoreVisibility();
  10440. });
  10441.  
  10442. var wheelCounter = 0, wheelTimer = null;
  10443. EventDispatcher.addEventListener('onWheelNoButton', function(e, delta) {
  10444. if (!conf.enableFullScreenMenu) return;
  10445. if ((e.target.tagName !== 'OBJECT' && e.target.tagName !== 'HTML') ||
  10446. !WatchController.isFullScreen()) return;
  10447. if (wheelTimer) {
  10448. wheelCounter += delta;
  10449. } else {
  10450. wheelCounter = 0;
  10451. wheelTimer = setTimeout(function() {//
  10452. wheelTimer = null;
  10453. if (Math.abs(wheelCounter) > 3) {
  10454. EventDispatcher
  10455. .dispatch('onToggleFullScreenMenu',
  10456. $('body').toggleClass('w_fullScreenMenu', wheelCounter < 0).hasClass('w_fullScreenMenu')
  10457. );
  10458. AnchorHoverPopup.hidePopup();
  10459. }
  10460. }, 500);
  10461. }
  10462. });
  10463.  
  10464. TouchEventDispatcher.onflick(function(e) {
  10465. if (!conf.enableFullScreenMenu) return;
  10466. if ((e.direction !=='up' && e.direction !=='down') || e.startEvent.target.tagName !== 'OBJECT' || !WatchController.isFullScreen()) return;
  10467. if (wheelTimer) {
  10468. clearTimeout(wheelTimer);
  10469. wheelTimer = null;
  10470. }
  10471. EventDispatcher
  10472. .dispatch('onToggleFullScreenMenu',
  10473. $('body').toggleClass('w_fullScreenMenu', e.direction === 'down').hasClass('w_fullScreenMenu')
  10474. );
  10475. AnchorHoverPopup.hidePopup();
  10476. });
  10477.  
  10478. var $fullScreenMenuContainer = $('<div id="fullScreenMenuContainer"/>');
  10479. var $fullScreenModeSwitch = $([
  10480. '<button class="fullScreenModeSwitch button">',
  10481. '画面モード: ',
  10482. '<span class="modeStatus mode_normal">標準</span>',
  10483. '<span class="modeStatus mode_noborder">最大化 </span>',
  10484. '</button>'
  10485. ].join('')).attr('title', '全画面時の表示切り替え').click(toggleTrueBrowserFull);
  10486. var $toggleStageVideo = $([
  10487. '<button class="stageVideoSwitch button">',
  10488. 'アクセラレーション: ',
  10489. '<span class="modeStatus mode_off">OFF</span>',
  10490. '<span class="modeStatus mode_on">ON</span>',
  10491. '</button>'
  10492. ].join('')).attr('title', 'ハードウェアアクセラレーションのON/OFF').click(function() { WatchController.toggleStageVideo(); });
  10493. var $toggleSetting = $([
  10494. '<button class="toggleSetting button">',
  10495. '</button>'
  10496. ].join('')).text('⛭設定').attr('title', '設定パネルを開閉します').click(function() { ConfigPanel.toggle();});
  10497. $fullScreenMenuContainer.append($fullScreenModeSwitch).append($toggleStageVideo).append($toggleSetting);
  10498. $('#nicoplayerContainerInner').append($fullScreenMenuContainer);
  10499.  
  10500.  
  10501. if (conf.lastControlPanelPosition === 'bottom' || conf.lastControlPanelPosition === 'over') {
  10502. EventDispatcher.addEventListener('onFirstVideoInitialized', function() {
  10503. console.log('restore oldTypeControlPanel ? ', conf.lastControlPanelPosition === 'bottom');
  10504. watch.PlayerInitializer.nicoPlayerConnector.playerConfig.set(
  10505. {oldTypeControlPanel: conf.lastControlPanelPosition === 'bottom'}
  10506. );
  10507. });
  10508. }
  10509.  
  10510. } // end initScreenMode()
  10511.  
  10512.  
  10513.  
  10514.  
  10515. function initPlaylist($, conf, w) {
  10516. var
  10517. playlist = watch.PlaylistInitializer.playlist,
  10518. blankVideoId = 'sm20353707', blankVideoUrl = 'http://www.nicovideo.jp/watch/' + blankVideoId + '?',
  10519. redirectPageUrl = 'http://www.nicovideo.jp/stamp',
  10520. items = {},
  10521. toCenter = function() { // 表示位置調整
  10522. var
  10523. pm = WatchApp.ns.view.playlist.PlaylistManager,
  10524. pv = watch.PlaylistInitializer.playlistView,
  10525. pl = playlist,
  10526. current = pl.getPlayingIndex(),
  10527. cols = Math.floor($('#playlistContainerInner').innerWidth() / pm.getItemWidth()),
  10528. center = Math.round(cols / 2);
  10529.  
  10530. if (cols < 1) { return; }
  10531. var currentLeft = pm.getLeftSideIndex();
  10532. pv.scroll(Math.max(0, current - center + 1));
  10533. },
  10534. scroll = function(d) {
  10535. var isEffectEnabled = watch.PlaylistInitializer.playlistView.isEffectEnabled;
  10536. var left = WatchApp.ns.view.playlist.PlaylistManager.getLeftSideIndex();
  10537. watch.PlaylistInitializer.playlistView.isEffectEnabled = false;
  10538. watch.PlaylistInitializer.playlistView.scroll(Math.max(0, left + d));
  10539. watch.PlaylistInitializer.playlistView.isEffectEnabled = isEffectEnabled;
  10540. };
  10541.  
  10542. playlist.isAutoPlay = playlist.isContinuous; // 互換用
  10543. playlist.enableAutoPlay = playlist.enableContinuous;
  10544. playlist.disableAutoPlay = playlist.disableContinuous;
  10545.  
  10546. $('#playlist').find('.playlistInformation').on('dblclick.watchItLater', function(e) {
  10547. e.preventDefault();
  10548. e.stopPropagation();
  10549. toCenter();
  10550. });
  10551.  
  10552. EventDispatcher.addEventListener('onVideoInitialized', function() {
  10553. var pm = WatchApp.ns.view.playlist.PlaylistManager, pv = watch.PlaylistInitializer.playlistView, pl = watch.PlaylistInitializer.playlist;
  10554. var current = pl.getPlayingIndex(), cols = Math.floor($('#playlistContainerInner').innerWidth() / pm.getItemWidth()), center = Math.floor(cols / 2);
  10555. if (pm.getLeftSideIndex() + cols <= pl.getNextPlayingIndex()) { toCenter(); }
  10556. });
  10557.  
  10558. $('#playlistContainer .prevArrow, #playlistContainer .nextArrow').on('mousewheel.watchItLater', function(e, delta) {
  10559. if (WatchController.isFullScreen()) { return; }
  10560. e.preventDefault();
  10561. e.stopPropagation();
  10562. scroll(delta *-1);
  10563. }).attr('title', 'ホイールで左右にスクロール');
  10564.  
  10565. // フルスクリーン中はプレイリストのどこでもスクロールできたほうがいいね
  10566. $('#playlist').on('mousewheel.watchItLater', function(e, delta) {
  10567. if (WatchController.isFullScreen() || WatchController.isSearchMode() || $('#footer').hasClass('noBottom')) {
  10568. e.preventDefault();
  10569. e.stopPropagation();
  10570. scroll(delta *-1);
  10571. }
  10572. });
  10573.  
  10574. EventDispatcher.addEventListener('onWheelAndButton', function(e, delta, button) {
  10575. if (WatchController.isFullScreen()) { return; }
  10576. if ($('#playlist').hasClass('dragging')) {
  10577. e.preventDefault();
  10578. scroll(delta *-1);
  10579. }
  10580. });
  10581.  
  10582. var
  10583. updatePos = function() {
  10584. if (
  10585. conf.hashPlaylistMode === 2 || (conf.hashPlaylistMode === 1 && WatchController.isPlaylistActive())) {
  10586. LocationHashParser.setValue('playlist', exportPlaylist());
  10587. LocationHashParser.updateHash();
  10588. }
  10589. if (conf.storagePlaylistMode === 'sessionStorage' || conf.storagePlaylistMode === 'localStorage') {
  10590. setTimeout(function() {
  10591. w[conf.storagePlaylistMode].setItem('watchItLater_playlist', JSON.stringify(exportPlaylist()));
  10592. }, 0);
  10593. }
  10594.  
  10595. var pos = Math.max((playlist.getPlayingIndex() + 1), 1) + '/' + Math.max(playlist.getItems().length, 1);
  10596. $('.generationMessage').text(pos + " - \n" + $('.generationMessage').text().replace(/^.*\n/, ''));
  10597. },
  10598. resetView = function() {
  10599. watch.PlaylistInitializer.playlistView.resetView();
  10600. },
  10601. exportPlaylist = function(option, type, continuous, shuffle) {
  10602. var
  10603. items = playlist.currentItems,
  10604. list = [],
  10605. current = 0,
  10606. len = conf.debugMode ? Math.min(600, items.length) : Math.min(300, items.length);
  10607.  
  10608. for (var i = 0; i < len; i++) {
  10609. var item = items[i];
  10610. if (item._isPlaying) current = i;
  10611. list.push([
  10612. item.id,
  10613. parseInt(item.mylistCounter, 10).toString(36),
  10614. parseInt(item.viewCounter, 10).toString(36),
  10615. parseInt(item.numRes, 10).toString(36),
  10616. (item.thumbnailUrl ? parseInt(item.thumbnailUrl.split('?i=')[1], 10).toString(36) : 'c490r'),
  10617. ].join(',') + ':' + item.title
  10618. );
  10619. }
  10620. return {
  10621. a: (typeof continuous === 'boolean') ? continuous : WatchController.isPlaylistContinuous(),
  10622. r: (typeof shuffle === 'boolean') ? shuffle : WatchController.isPlaylistRandom(),
  10623. o: option || playlist.option,
  10624. t: type || playlist.type,
  10625. i: list,
  10626. c: current
  10627. };
  10628. },
  10629. importPlaylist = function(list) {
  10630. var PlaylistItem = WatchApp.ns.model.playlist.PlaylistItem, newItems = [], uniq = {}, currentIndex = -1;
  10631.  
  10632. WatchController.clearPlaylist();
  10633. var currentItem = playlist.currentItems[0];
  10634. if (!currentItem) {
  10635. var wm = watchInfoModel;
  10636. currentItem = new PlaylistItem({
  10637. id: wm.v,
  10638. title: wm.title,
  10639. mylist_counter: wm.mylistCount,
  10640. view_counter: wm.viewCount,
  10641. num_res: wm.commentCount,
  10642. thumbnail_url: wm.thumbnail,
  10643. first_retriee: wm.postedAt
  10644. });
  10645. }
  10646.  
  10647. for (var i = 0, len = list.i.length; i < len; i++) {
  10648. var
  10649. dat = list.i[i],
  10650. c = dat.split(':')[0].split(','),
  10651. title = dat.replace(/^.*:/, ''),
  10652. id = c[0],
  10653. thumbnailId = parseInt(c[4], 36);
  10654.  
  10655. if (uniq[id] || typeof id !== 'string') { continue; }
  10656. uniq[id] = true;
  10657. if (id === watchInfoModel.v) {
  10658. currentIndex = i;
  10659. newItems.push(currentItem);
  10660. } else {
  10661. var item = new PlaylistItem({
  10662. id: id,
  10663. title: title.replace('<', '&lt;').replace('>', '&gt;'), // ないはずだけど一応
  10664. mylist_counter: parseInt(c[1], 36),
  10665. view_counter: parseInt(c[2], 36),
  10666. num_res: parseInt(c[3], 36),
  10667. thumbnail_url: 'http://tn-skr' + ((thumbnailId % 4) + 1) + '.smilevideo.jp/smile?i=' + thumbnailId,
  10668. first_retrieve: null
  10669. });
  10670. newItems.push(item);
  10671. }
  10672. }
  10673. // 復元するリストの中に現在の動画がなかった
  10674. if (currentIndex === -1) {
  10675. if (typeof list.c === 'number') {
  10676. if (list.c < newItems.length) {
  10677. currentIndex = list.c + 1;
  10678. newItems.splice(currentIndex, 0, currentItem);
  10679. } else {
  10680. currentIndex = list.length;
  10681. newItems.push(currentItem);
  10682. }
  10683. } else {
  10684. newItems.unshift(currentItem);
  10685. currentIndex = 0;
  10686. }
  10687. }
  10688.  
  10689. var isAutoPlay = playlist.isContinuous();//isAutoPlay();
  10690. playlist.reset(newItems, 'WatchItLater', list.t, list.o);
  10691. if (!isAutoPlay) { // 本家側の更新でリセット時に勝手に自動再生がONになるようになったので、リセット前の状態を復元する
  10692. playlist.disableContinuous();
  10693. }
  10694. if (currentIndex >= 0) { playlist.playingItem = newItems[currentIndex]; }
  10695. if (list.a) { playlist.enableContinuous(); }
  10696. if (list.r) {
  10697. if (watchInfoModel.id === blankVideoId) {
  10698. setTimeout(function() {
  10699. WatchController.shufflePlaylist();
  10700. }, 3000);
  10701. } else {
  10702. playlist.enableContinuous();
  10703. }
  10704. }
  10705. },
  10706. $dialog = null, $savelink = null, $continuous, $shuffle,
  10707. openSaveDialog = function() {
  10708. function resetLink() {
  10709. var playlist = exportPlaylist(null, null, $continuous.is(':checked'), $shuffle.is(':checked'));
  10710. playlist.o = playlist.o || [];
  10711. playlist.o.name = $savelink.text();
  10712. playlist.t = 'mylist';
  10713. LocationHashParser.setValue('playlist', playlist);
  10714. if (!playlist.r) {
  10715. LocationHashParser.setValue('redirectWatchId', watchInfoModel.id);
  10716. } else {
  10717. LocationHashParser.deleteValue('redirectWatchId');
  10718. }
  10719. $savelink
  10720. //.attr('href', blankVideoUrl + LocationHashParser.getHash().replace(/\?/g, ''))
  10721. .attr('href', redirectPageUrl + LocationHashParser.getHash().replace(/\?/g, ''))
  10722. .unbind();
  10723. }
  10724. function closeDialog() {
  10725. $dialog.removeClass('show');
  10726. }
  10727.  
  10728. if (!$dialog) {
  10729. $dialog = $('<div id="playlistSaveDialog" />');
  10730. $dialog.append($([
  10731. '<div class="shadow"></div>',
  10732. '<div class="formWindow"><div class="formWindowInner">',
  10733. '<h3>プレイリスト保存用リンク(実験中)</h3>',
  10734. '<p class="link"><a target="_blank" class="playlistSaveLink">保存用リンク</a><button class="editButton">編集</button></p>',
  10735. '<label><input type="checkbox" class="continuous">開始時に連続再生をONにする</label><br>',
  10736. '<label><input type="checkbox" class="shuffle">開始時にリストをシャッフルする</label>',
  10737. '<p class="desc">リンクを右クリックしてコピーやブックマークする事で、現在のプレイリストを保存する事ができます。</p>',
  10738. '<button class="closeButton">閉じる</button>',
  10739. '</div></div>',
  10740. ''].join('')));
  10741. $savelink = $dialog.find('a').attr('added', 1);
  10742. $continuous = $dialog.find('.continuous');
  10743. $shuffle = $dialog.find('.shuffle');
  10744. $dialog.find('.shadow').on('click', closeDialog);
  10745. $dialog.find('.editButton').on('click', function() {
  10746. var newTitle = prompt('タイトルを編集', $savelink.text());
  10747. if (newTitle) {
  10748. $savelink.text(newTitle);
  10749. resetLink();
  10750. }
  10751. });
  10752. $continuous.on('click', resetLink);
  10753. $shuffle .on('click', resetLink);
  10754. $dialog.find('.closeButton').on('click', closeDialog);
  10755.  
  10756. $('body').append($dialog);
  10757. }
  10758. $savelink.text(
  10759. $('#playlist .generationMessage')
  10760. .text()
  10761. .replace(/^.*?\n/, '')
  10762. .replace(/^.*「/, '')
  10763. .replace(/」.*?$/, '')
  10764. .replace(/ *- \d{4}-\d\d-\d\d \d\d:\d\d$/, '') +
  10765. ' - ' + WatchApp.ns.util.DateFormat.strftime('%Y-%m-%d %H:%M', new Date())
  10766. );
  10767. $continuous.attr('checked', WatchController.isPlaylistActive());
  10768. $shuffle .attr('checked', WatchController.isPlaylistRandom());
  10769. resetLink();
  10770. $dialog.addClass('show');
  10771. },
  10772. PlaylistMenu = (function($, conf, w, playlist) {
  10773. var $popup = null, $generationMessage = $('#playlist').find('.generationMessage'), self;
  10774.  
  10775. var
  10776. enableContinuous = function() {
  10777. playlist.enableContinuous();
  10778. },
  10779. createDom = function() {
  10780. $popup = $('<div class="playlistMenuPopup popupMenu"/>')
  10781. .addClass('pop')
  10782. .toggleClass('w_touch', isTouchActive);
  10783. var $ul = $('<ul/>');
  10784. $popup.click(function() {
  10785. self.hide();
  10786. });
  10787. var $shuffle = $('<li>シャッフル: 全体</li>').click(function(e) {
  10788. WatchController.shufflePlaylist();
  10789. enableContinuous();
  10790. });
  10791. $ul.append($shuffle);
  10792. var $shuffleR = $('<li>シャッフル: 右</li>').click(function(e) {
  10793. WatchController.shufflePlaylist('right');
  10794. enableContinuous();
  10795. });
  10796. $ul.append($shuffleR);
  10797.  
  10798. var $next = $('<li>検索結果を追加: 次に再生</li>').click(function() {
  10799. WatchController.appendSearchResultToPlaylist('next');
  10800. enableContinuous();
  10801. });
  10802. $ul.append($next);
  10803.  
  10804. var $insert = $('<li>検索結果を追加: 末尾</li>').click(function() {
  10805. WatchController.appendSearchResultToPlaylist();
  10806. enableContinuous();
  10807. });
  10808. $ul.append($insert);
  10809.  
  10810. var $clear = $('<li>リストを消去: 全体</li>').click(function() {
  10811. WatchController.clearPlaylist();
  10812. //watch.PlaylistInitializer.playlist.setPlaybackMode('normal');
  10813. });
  10814. $ul.append($clear);
  10815.  
  10816. var $clearLeft = $('<li>リストを消去: 左</li>').click(function() {
  10817. WatchController.clearPlaylist('left');
  10818. });
  10819. $ul.append($clearLeft);
  10820. var $clearRight = $('<li>リストを消去: 右</li>').click(function() {
  10821. WatchController.clearPlaylist('right');
  10822. });
  10823. $ul.append($clearRight);
  10824.  
  10825. var $saver = $('<li>リストを保存(実験中)</li>').click(function() {
  10826. openSaveDialog();
  10827. });
  10828.  
  10829. $ul.append($saver);
  10830. $popup.append($ul);
  10831. $('body').append($popup);
  10832. },
  10833. show = function() {
  10834. if ($popup === null) { createDom(); }
  10835. var offset = $generationMessage.offset(), $window = $(window) , pageBottom = $window.scrollTop() + $window.innerHeight();
  10836. $popup.css({
  10837. left: offset.left,
  10838. top: Math.min(offset.top + 24, pageBottom - $popup.outerHeight())
  10839. }).show();
  10840. },
  10841. hide = function() {
  10842. if ($popup) { $popup.hide(); }
  10843. },
  10844. toggle = function() {
  10845. if ($popup === null || !$popup.is(':visible')) {
  10846. show();
  10847. } else {
  10848. hide();
  10849. }
  10850. };
  10851.  
  10852. $generationMessage.click(function(e) {
  10853. e.preventDefault();
  10854. self.toggle();
  10855. });
  10856.  
  10857. $('body').on('click.watchItLater', function(e) {
  10858. var tagName = e.target.tagName, className = e.target.className;
  10859. if (className !== 'generationMessage') {
  10860. self.hide();
  10861. }
  10862. });
  10863. self = {
  10864. show: show,
  10865. hide: hide,
  10866. toggle: toggle
  10867. };
  10868. return self;
  10869. })($, conf, w, playlist);
  10870.  
  10871.  
  10872. var hashlist = LocationHashParser.getValue('playlist');
  10873. if (hashlist && hashlist.i && hashlist.i.length > 0) {
  10874. try {
  10875. console.log('restore playlist!!');
  10876. importPlaylist(hashlist);
  10877. if (conf.hashPlaylistMode < 1) {
  10878. LocationHashParser.removeHash();
  10879. }
  10880. setTimeout(function() { resetView(); } , 3000);
  10881. } catch (e) {
  10882. console.log(e);
  10883. console.trace();
  10884. }
  10885. } else
  10886. if ((conf.storagePlaylistMode === 'sessionStorage' || conf.storagePlaylistMode === 'localStorage') && w[conf.storagePlaylistMode] && !playlist.isContinuous()) {
  10887. try {
  10888. console.log('restore playlist:' + conf.storagePlaylistMode);
  10889. var list = JSON.parse(w[conf.storagePlaylistMode].getItem('watchItLater_playlist'));
  10890. if (list !== null) { importPlaylist(list); }
  10891. setTimeout(function() { resetView(); } , 3000);
  10892. } catch (e) {
  10893. console.log('プレイリストの復元に失敗!', e);
  10894. }
  10895. } else {
  10896. updatePos();
  10897. }
  10898.  
  10899.  
  10900. EventDispatcher.addEventListener('onScreenModeChange', function(sc) {
  10901. if ($('body').hasClass('full_with_browser')) {
  10902. // フル画面時プレイリストを閉じる
  10903. if (conf.autoClosePlaylistInFull) { $('#content').find('.browserFullPlaylistClose:visible').click(); }
  10904. }
  10905. });
  10906.  
  10907. EventDispatcher.addEventListener('onVideoExplorerOpened', function() {
  10908. // 2013/09/26 本家側で開閉を記録するようになった -> 2014/03/03 また記憶しなくなった
  10909.  
  10910. // 通常画面でプレイリストを表示にしてるなら、開いた状態をデフォルトにする
  10911. if (conf.hidePlaylistInVideoExplorer === false) {
  10912. playlist.open();
  10913. //$('#playlist').find('.browserFullOption a:visible').click();
  10914. }
  10915. });
  10916. $('#playlist .browserFullOption a').on('click', function() {
  10917. if (WatchController.isSearchMode()) {
  10918. conf.setValue('hidePlaylistInVideoExplorer', !conf.hidePlaylistInVideoExplorer);
  10919. }
  10920. });
  10921.  
  10922. EventDispatcher.addEventListener('on.config.hashPlaylistMode', function(v) {
  10923. if (v === 0) {
  10924. LocationHashParser.deleteValue('playlist');
  10925. LocationHashParser.removeHash();
  10926. } else
  10927. if (v === 1 || v === 2) {
  10928. var msg = [
  10929. '【警告】「プレイリストが消えないモード」は実験中の機能です。',
  10930. '',
  10931. 'この機能を使うと、ページをリロードしたりブックマークしてもプレイリストが消えなくなりますが、',
  10932. 'データを力技で保持するため、ページのURLがものすごく長く(※)なります。',
  10933. '',
  10934. 'そのため、ブラウザのパフォーマンスが低下したり、未知の不具合が発生する可能性があります。',
  10935. 'それでもこの機能を使ってみたい!という方だけ「OK」を押してください。',
  10936. '',
  10937. '※ 数千~数万文字くらい!',
  10938. ''].join('\n');
  10939. if (confirm(msg)) {
  10940. LocationHashParser.setValue('playlist', exportPlaylist());
  10941. LocationHashParser.updateHash();
  10942. } else {
  10943. conf.setValue('hashPlaylistMode', 0);
  10944. ConfigPanel.refresh();
  10945. }
  10946. }
  10947. });
  10948.  
  10949. $('#playlist .browserFullOption').on('click.bugfix', resetView);
  10950.  
  10951. $('.generationMessage, .prevArrow, .nextArrow, .playbackOption').on('mouseover', function() {
  10952. AnchorHoverPopup.hidePopup();
  10953. });
  10954.  
  10955. playlist.addEventListener('changePlaybackMode', function(mode) {
  10956. console.log('changePlaybackMode', mode, conf.hashPlaylistMode);
  10957. if (mode === 'normal' && conf.hashPlaylistMode < 2) {
  10958. LocationHashParser.removeHash();
  10959. } else {
  10960. updatePos();
  10961. }
  10962. });
  10963.  
  10964. EventDispatcher.addEventListener('onFirstVideoInitialized', function() {
  10965. updatePos();
  10966. EventDispatcher.addEventListener('onScreenModeChange', function(sc) {
  10967. resetView();
  10968. });
  10969. EventDispatcher.addEventListener('onWatchInfoReset', function() {
  10970. updatePos();
  10971. });
  10972. playlist.addEventListener('reset', function() {
  10973. EventDispatcher.dispatch('onPlaylistReset');
  10974. updatePos();
  10975. });
  10976. playlist.addEventListener('update', function() {
  10977. EventDispatcher.dispatch('onPlaylistUpdate');
  10978. updatePos();
  10979. });
  10980. });
  10981.  
  10982. var togglePlaylistDisplay = function(v) {
  10983. var $playlist = $('#playlist');
  10984. if (!v) {
  10985. $playlist.addClass('w_show').removeClass('w_closing');
  10986. } else {
  10987. $playlist.addClass('w_closing');
  10988. setTimeout(function() { $playlist.removeClass('w_show');}, 500);
  10989. }
  10990. };
  10991. EventDispatcher.addEventListener('on.config.hidePlaylist', togglePlaylistDisplay);
  10992. togglePlaylistDisplay(conf.hidePlaylist);
  10993.  
  10994. // プレイリスト消えないモードの時はプレイリストを勝手におすすめに置き換える機能をキャンセル
  10995. (function() {
  10996. var ld = WatchApp.ns.init.VideoExplorerInitializer.videoExplorerController._videoExplorerPlaylistResetArgumentsLoader;
  10997. ld.load_org = ld.load;
  10998. ld.load = $.proxy(function(a, b, c) {
  10999. if (conf.storagePlaylistMode !== '') {
  11000. return;
  11001. }
  11002. this.load_org(a, b, c);
  11003. }, ld);
  11004. ld = null;
  11005. })();
  11006.  
  11007. } // end initPlaylist
  11008.  
  11009.  
  11010. function initPageHeader($, conf, w) {
  11011. $('.videoDetailExpand h2').addClass('videoDetailToggleButton');
  11012. } // end initPageHeader
  11013.  
  11014.  
  11015. function initVideoTagContainer($, conf, w) {
  11016. var $videoHeaderTagEditLinkArea = null, $toggleTagEditText = null, baseTagHeight = 72, currentHeight = 72;
  11017. var tagListView = watch.TagInitializer.tagViewController.tagListView, $videoHeader = $('.videoHeaderOuter');
  11018.  
  11019. tagListView.getCurrentDefaultHeight_org = tagListView.getCurrentDefaultHeight;
  11020. tagListView.getCurrentDefaultHeight = function() {
  11021. if ($('body').hasClass('full_with_browser')) {
  11022. return tagListView.getCurrentDefaultHeight_org();
  11023. }
  11024. return currentHeight;
  11025. };
  11026.  
  11027. $videoHeaderTagEditLinkArea = $('.toggleTagEditInner .videoHeaderTagEditLinkArea');
  11028. $('.toggleTagEdit').append($videoHeaderTagEditLinkArea);
  11029. $toggleTagEditText = $('<span class="toggleText">' + $('.toggleTagEditInner').text() + '</span>');
  11030. $('.toggleTagEditInner').empty().append($toggleTagEditText).append($videoHeaderTagEditLinkArea);
  11031.  
  11032. var onTagReset = function() {
  11033. try {
  11034. // タグが2行以下だったら自動的に狭くする処理
  11035. if (!conf.enableAutoTagContainerHeight) { return; }
  11036. currentHeight = Math.min(baseTagHeight, $('#videoTagContainer').find('.tagInner').innerHeight());
  11037.  
  11038. if (baseTagHeight !== currentHeight) {
  11039. var $toggle = $('#videoTagContainer').find('.toggleTagEdit');
  11040. $videoHeader.removeClass('tag1Line').removeClass('tag2Lines');
  11041.  
  11042. if (currentHeight < 36) { // 1行以下の時
  11043. $videoHeader.addClass('tag1Line');
  11044. } else {
  11045. if (currentHeight <= 60) { // 2行以下の時
  11046. $videoHeader.addClass('tag2Lines');
  11047. }
  11048. }
  11049. watch.TagInitializer.tagViewController.tagListView.fit();
  11050. } else {
  11051. $videoHeader.removeClass('tag1Line').removeClass('tag2Lines');
  11052. watch.TagInitializer.tagViewController.tagListView.fit();
  11053. }
  11054. } catch (e) {
  11055. console.log(e);
  11056. }
  11057. };
  11058.  
  11059. EventDispatcher.addEventListener('onFirstVideoInitialized', function() {
  11060. EventDispatcher.addEventListener('onVideoInitialized', onTagReset);
  11061. });
  11062. watch.TagInitializer.tagList.addEventListener('reset', onTagReset);
  11063. if (conf.enableAutoTagContainerHeight) {
  11064. watch.TagInitializer.tagViewController.tagViewPinStatus.changeStatus(true);
  11065. }
  11066. window.setTimeout(onTagReset, 1000);
  11067.  
  11068. $videoHeaderTagEditLinkArea = $toggleTagEditText = null;
  11069.  
  11070.  
  11071. WatchApp.ns.model.player.NicoPlayerConnector.onTagDataReceived_org = WatchApp.ns.model.player.NicoPlayerConnector.onTagDataReceived;
  11072. WatchApp.ns.model.player.NicoPlayerConnector.onTagDataReceived = function(a) {
  11073. //console.log('onTagDataReceived', a);
  11074. if (conf.disableTagReload) {
  11075. return;
  11076. }
  11077. WatchApp.ns.model.player.NicoPlayerConnector.onTagDataReceived_org(a);
  11078. };
  11079.  
  11080. } // end initVideoTagContainer
  11081.  
  11082.  
  11083.  
  11084. function initVideoReview($, conf, w) {
  11085. var __css__ = Util.here(function() {/*
  11086. .sidePanel #videoReview { margin: 0 auto; }
  11087. #outline.w_compact #videoReview { width: 300px; }
  11088. #outline.w_compact textarea.newVideoReview { width: 277px; }
  11089. #outline.w_compact #videoReviewHead { width: 283px; }
  11090. #outline.w_compact #videoReview .stream { width: 300px; }
  11091. #outline.w_compact #videoReview .inner { width: 300px; }
  11092. #outline.w_compact .commentContent { width: 278px; }
  11093. #outline.w_compact .commentContentBody { width: 232px; }
  11094. .sidePanel.w_review #videoReview { width: 308px; }
  11095. .sidePanel.w_review textarea.newVideoReview { width: 286px; }
  11096. .sidePanel.w_review #videoReviewHead { width: 291px; }
  11097. .sidePanel.w_review #videoReview .stream { width: 308px; }
  11098. .sidePanel.w_review #videoReview .inner { width: 308px; }
  11099. .sidePanel.w_review .commentContent { width: 286px; }
  11100. .sidePanel.w_review .commentContentBody { width: 240px; }
  11101. body:not(.full_with_browser) .w_wide .sidePanel.w_review #videoReview { width: 400px; }
  11102. body:not(.full_with_browser) .w_wide .sidePanel.w_review textarea.newVideoReview { width: 377px; }
  11103. body:not(.full_with_browser) .w_wide .sidePanel.w_review #videoReviewHead { width: 383px; }
  11104. body:not(.full_with_browser) .w_wide .sidePanel.w_review #videoReview .stream { width: 400px; }
  11105. body:not(.full_with_browser) .w_wide .sidePanel.w_review #videoReview .inner { width: 400px; }
  11106. body:not(.full_with_browser) .w_wide .sidePanel.w_review .commentContent { width: 378px; }
  11107. body:not(.full_with_browser) .w_wide .sidePanel.w_review .commentContentBody { width: 332px; }
  11108. body.videoExplorer .sidePanel.w_review #videoReview { width: 400px; }
  11109. body.videoExplorer .sidePanel.w_review textarea.newVideoReview { width: 377px; }
  11110. body.videoExplorer .sidePanel.w_review #videoReviewHead { width: 383px; }
  11111. body.videoExplorer .sidePanel.w_review #videoReview .stream { width: 400px; }
  11112. body.videoExplorer .sidePanel.w_review #videoReview .inner { width: 400px; }
  11113. body.videoExplorer .sidePanel.w_review .commentContent { width: 378px; }
  11114. body.videoExplorer .sidePanel.w_review .commentContentBody { width: 332px; }
  11115.  
  11116. body:not(.videoExplorer) .sidePanel .commentUserProfile, body:not(.videoExplorer) .sidePanel .panelTrigger {
  11117. display: none !important;
  11118. }
  11119. body.videoExplorer .sidePanel .commentUserProfile {
  11120. position: fixed;
  11121. top: 36px !important;
  11122. left: auto !important;
  11123. right: 0 !important;
  11124. z-index: 11000;
  11125. }
  11126.  
  11127. .sidePanel .getMoreReviewComment {
  11128. margin-bottom: 256px;
  11129. }
  11130. */});
  11131. var reviewCss = addStyle(__css__, 'videoReviewCss');
  11132.  
  11133. /*
  11134. EventDispatcher.addEventListener('onFirstVideoInitialized', function() { setTimeout(function() {
  11135. var elms = [
  11136. '#videoReview',
  11137. 'textarea.newVideoReview',
  11138. '#videoReviewHead',
  11139. '#videoReview .stream',
  11140. '#videoReview .inner',
  11141. '.commentContent',
  11142. '.commentContentBody'
  11143. ];
  11144. var css = [], $baseElement = $('#videoReview');
  11145. var makeCss = function (targetWidth, preSel) {
  11146. var px = targetWidth - $baseElement.outerWidth();
  11147. for (var v in elms) {
  11148. var $e = $(elms[v]), newWidth = $e.width() + px;
  11149. css.push([
  11150. preSel, elms[v], ' { width: ', newWidth,'px; }\n'
  11151. ].join(''));
  11152. }
  11153. };
  11154. makeCss(300, '#outline.w_compact ');
  11155. makeCss(308, '.sidePanel.w_review ');
  11156. makeCss(400, '.sidePanel.w_review.w_wide ');
  11157. makeCss(400, 'body.videoExplorer .sidePanel.w_review ');
  11158. console.log(css.join(''));
  11159. var reviewCss = addStyle(css.join(''), 'videoReviewCss');
  11160. }, 3000);});
  11161. */
  11162. } // end initVideoReview
  11163.  
  11164. function initNews() {
  11165. var stopNicoNewsPolling = function() {
  11166. window.WatchApp.ns.init.TextMarqueeInitializer.textMarqueeItemDispatcher.stop();
  11167. window.WatchApp.ns.init.TextMarqueeInitializer.textMarqueeItemList.list.length = 0;
  11168. };
  11169. var toggleNoNews = function() {
  11170. $('#content').toggleClass('noNews', conf.hideNicoNews || conf.customPlayerSize !== '');
  11171. if ($('#content').hasClass('noNews')) {
  11172. stopNicoNewsPolling();
  11173. }
  11174. };
  11175.  
  11176. EventDispatcher.addEventListener('on.config.hideNicoNews', toggleNoNews);
  11177. EventDispatcher.addEventListener('on.config.customPlayerSize', toggleNoNews);
  11178.  
  11179. toggleNoNews();
  11180.  
  11181. if (conf.enableNewsHistory) { NicoNews.initialize(w); }
  11182. if (conf.hideNewsInFull) { $('body').addClass('hideNewsInFull'); }
  11183. } //
  11184.  
  11185.  
  11186. function initEvents() {
  11187. var pac = watch.PlayerInitializer.playerAreaConnector;
  11188.  
  11189. pac.addEventListener("onVideoInitialized", onVideoInitialized);
  11190. pac.addEventListener("onVideoEnded", onVideoEnded);
  11191. pac.addEventListener("onVideoStopped", onVideoStopped);
  11192. // pac.addEventListener('onSystemMessageFatalErrorSended', onSystemMessageFatalErrorSended);
  11193. // watch.WatchInitializer.watchModel.addEventListener('error', function() {console.log(arguments);});
  11194.  
  11195. pac.addEventListener('updateSettingsPanelVisible', function(isVisible, panel) {
  11196. EventDispatcher.dispatch('onUpdateSettingPanelVisible', isVisible, panel);
  11197. });
  11198.  
  11199. watchInfoModel.addEventListener('reset', onWatchInfoReset);
  11200. watchInfoModel.addEventListener('beforeReset', function() {
  11201. window.console.time('watchInfoModelReset');
  11202. EventDispatcher.dispatch('onWatchInfoBeforeReset');
  11203. });
  11204. watchInfoModel.addEventListener('afterReset', function() {
  11205. window.console.timeEnd('watchInfoModelReset');
  11206. EventDispatcher.dispatch('onWatchInfoAfterReset');
  11207. });
  11208. watch.PlayerInitializer.playerScreenMode.addEventListener('change', onScreenModeChange);
  11209.  
  11210. var explorer = watch.VideoExplorerInitializer.videoExplorer;
  11211. explorer.addEventListener('openStart', onVideoExplorerOpening);
  11212. explorer.addEventListener('openEnd', onVideoExplorerOpened);
  11213. explorer.addEventListener('closeStart', onVideoExplorerClosing);
  11214. explorer.addEventListener('closeEnd', onVideoExplorerClosed);
  11215. explorer.addEventListener('refreshStart', onVideoExplorerRefreshStart);
  11216. explorer.addEventListener('refreshEnd', onVideoExplorerRefreshEnd);
  11217. explorer.addEventListener('changeCurrentPage', onVideoExplorerChangePage); //
  11218.  
  11219.  
  11220. $('body').dblclick(function(e){
  11221. var tagName = e.target.tagName, cls = e.target.className || '';
  11222. if (tagName === 'SELECT' || tagName === 'INPUT' || tagName === 'BUTTON' || cls.match(/mylistPopupPanel/)) {
  11223. return;
  11224. }
  11225. if (!WatchController.isFullScreen()) {
  11226. AnchorHoverPopup.hidePopup();
  11227. if (conf.doubleClickScroll) {
  11228. e.preventDefault();
  11229. EventDispatcher.dispatch('onScrollReset');
  11230. WatchController.scrollToVideoPlayer(true);
  11231. }
  11232. }
  11233. });
  11234.  
  11235. var bottomContentTabView = WatchApp.ns.view.BottomContentTabView.getInstance();
  11236. bottomContentTabView.addEventListener('changeContent', function(name) {
  11237. EventDispatcher.dispatch('onBottomContentTabViewReset', name);
  11238. });
  11239.  
  11240.  
  11241. Mylist.onDefMylistUpdate(function() {
  11242. //WatchController.clearDeflistCache();
  11243. });
  11244. Mylist.onMylistUpdate(function(info) {
  11245. WatchController.clearMylistCache(info.groupId);
  11246. });
  11247.  
  11248. $(window).on('beforeunload.watchItLater', function(e) {
  11249. conf.setValue('lastCommentVisibility', WatchController.commentVisibility() ? 'visible' : 'hidden');
  11250. }).on('resize', window._.debounce(function() {
  11251. AnchorHoverPopup.hidePopup();
  11252. EventDispatcher.dispatch('onWindowResizeEnd');
  11253. }, 1000));
  11254.  
  11255. //$(document).on('scroll', WatchApp.ns.event.EventDispatcher.throttle(function() {
  11256. $(document).on('scroll', function() {
  11257. if (document.body.style.pointerEvents !== 'none') {
  11258. document.body.style.pointerEvents = 'none';
  11259. }
  11260. });
  11261. $(document).on('scroll', window._.debounce(function() {
  11262. document.body.style.pointerEvents = '';
  11263. EventDispatcher.dispatch('onScrollEnd');
  11264. }, 500));
  11265.  
  11266. EventDispatcher.addEventListener('onFirstVideoInitialized', function() {
  11267. pac.addEventListener('onVideoChangeStatusUpdated', onVideoChangeStatusUpdated);
  11268. });
  11269.  
  11270. window.addEventListener('message', function(event) {
  11271. if (event.origin.indexOf('nicovideo.jp') < 0) return;
  11272. try {
  11273. var data = JSON.parse(event.data);
  11274. if (data.id !== 'WatchItLater') { return; }
  11275.  
  11276. EventDispatcher.dispatch('onMessage', data.body, data.type);
  11277.  
  11278. } catch (e) {
  11279. console.log(
  11280. '%cError: window.onMessage - ',
  11281. 'color: red; background: yellow',
  11282. e, event.origin, event.data);
  11283. }
  11284. });
  11285. } //
  11286.  
  11287. function initAdditionalButtons() {
  11288.  
  11289. var createPlaylistToggle = function() {
  11290. var $playlistToggle = $('<button title="プレイリスト表示/非表示" class="playlistToggle">プレイリスト</button>');
  11291.  
  11292. $playlistToggle.on('click', function() {
  11293. AnchorHoverPopup.hidePopup();
  11294. conf.setValue('hidePlaylist', !!!conf.hidePlaylist);
  11295. });
  11296.  
  11297. var togglePlaylistDisplay = function(v) {
  11298. $playlistToggle.toggleClass('w_show', !v);
  11299. };
  11300.  
  11301. EventDispatcher.addEventListener('on.config.hidePlaylist', togglePlaylistDisplay);
  11302. togglePlaylistDisplay(conf.hidePlaylist);
  11303.  
  11304. return $playlistToggle;
  11305. };
  11306. var createOpenExplorer = function() {
  11307. return $('<button class="openVideoExplorer">検索▼</button>').on('click', function() {
  11308. WatchController.openSearch();
  11309. if (!$('body').hasClass('content-fix')) {
  11310. WatchController.scrollToVideoPlayer(true);
  11311. }
  11312. });
  11313. };
  11314. var $div = $('<div class="bottomAccessContainer"/>').append(createPlaylistToggle()).append(createOpenExplorer());
  11315.  
  11316.  
  11317. var $headerMenu = $('<li class="watchItLaterSettingMenu"><a href="javascript:;" title="WatchItLaterの設定">WatchItLater設定</a></li>');
  11318. $('#siteHeaderRightMenuFix').after($headerMenu);
  11319.  
  11320. $('#outline .outer').before($div);
  11321. var $container = $('<div class="bottomConfButtonContainer" />'), $conf = $('<button title="WatchItLaterの設定">設定</button>');
  11322. var $explorerConf = $('<button><span class="open">`・ω・´</span><span class="close">´・ω・`</span></button>');
  11323. var toggleConf = function(e) {
  11324. e.stopPropagation();
  11325. AnchorHoverPopup.hidePopup();
  11326. ConfigPanel.toggle();
  11327. };
  11328. $container.append($conf);
  11329. $conf.addClass('openConfButton');
  11330. $conf.on('click', toggleConf);//.attr('accesskey', 'p');
  11331. $('#outline .outer').before($container);
  11332. $headerMenu.find('a').on('click', toggleConf);//.attr('accesskey', 'p');
  11333.  
  11334.  
  11335. $('.videoExplorerBody').append($explorerConf);
  11336. $explorerConf
  11337. .on('click',
  11338. function() { WatchItLater.config.set('videoExplorerHack', !WatchItLater.config.get('videoExplorerHack')); })
  11339. .addClass('videoExplorerConfig');
  11340.  
  11341. var $body = $('body'), $window = $(window);
  11342. EventDispatcher.addEventListener('onWindowResizeEnd', function() {
  11343. if (WatchController.isSearchMode() || WatchController.isFullScreen()) { return; }
  11344. var w = $div.outerWidth(), threshold = ($(window).innerWidth() - 960) / 2;
  11345. $('#outline').toggleClass('under960', w > threshold && !$('#footer').hasClass('noBottom'));
  11346. });
  11347. } // end initAdditionalButtons
  11348.  
  11349.  
  11350. function initSearchContent($, conf, w) {
  11351. var ContentType = WatchApp.ns.components.videoexplorer.model.ContentType;
  11352. var SearchSortOrder = WatchApp.ns.components.videoexplorer.model.SearchSortOrder;
  11353. var View = WatchApp.ns.components.videoexplorer.view.content.SearchContentView;
  11354. var vec = watch.VideoExplorerInitializer.videoExplorerController;
  11355. var explorer = vec.getVideoExplorer();
  11356. var content = explorer.getContentList().getContent(ContentType.SEARCH);
  11357. var relatedTag = new NicoSearchRelatedTag({});
  11358. var newSearch = new NewNicoSearch({});
  11359. var newSearchWrapper = new NewNicoSearchWrapper({search: newSearch});
  11360. var pager = content._pager;
  11361. var __css__ = Util.here(function() {/*
  11362. .newSearchOption {
  11363. text-align: center; margin-bottom: 16px; padding: 8px;
  11364. background: #eee;
  11365. display: none;
  11366. }
  11367. .newSearchOption select, .newSearchOption label{
  11368. margin-right: 32px;
  11369. }
  11370. .newSearchOption .reset{
  11371. cursor: pointer; background: #eee;
  11372. }
  11373. .newSearchOption p{
  11374. margin: 8px;
  11375. }
  11376. .newSearchOption .ownerName {
  11377. }
  11378. .w_sugoiSearch .newSearchOption {
  11379. display: block;
  11380. }
  11381.  
  11382. .relatedTagList {
  11383. }
  11384. .relatedTagList p{
  11385. display: inline-block; margin: 4px;
  11386. }
  11387. .relatedTagList li, .relatedTagList ul {
  11388. display: inline;
  11389. margin: 0 8px 0 0;
  11390. list-style: none;
  11391. word-break: break-all;
  11392. }
  11393. .relatedTagList li {
  11394. background: #f4f4f4; padding: 2px 4px;
  11395. border: solid 1px #999;
  11396. border-radius: 4px;
  11397. line-height: 180%;
  11398. }
  11399. .relatedTagList li:hover {
  11400. }
  11401. .relatedTagList li:hover a{
  11402. text-decoration: none;
  11403. }
  11404.  
  11405. .sugoiOption {
  11406. display: none;
  11407. }
  11408. .w_sugoiSearch .sugoiOption {
  11409. display: block; background: #eef;
  11410. }
  11411. .w_sugoiSearch optgroup.sugoiOption {
  11412. font-weight: bolder;
  11413. }
  11414.  
  11415. */});
  11416. addStyle(__css__, 'searchContent');
  11417.  
  11418. // 動画表示のテンプレート拡張
  11419. var $template = $('<div/>').html(watch.VideoExplorerInitializer.videoExplorerView._contentListView._$view.find('.searchContentTemplate').html());
  11420. $template.find('.searchSortOrder')
  11421. .append([
  11422. '<optgroup label="新検索専用" class="sugoiOption">',
  11423. '<option value="sort=_hot&amp;order=d"" class="sugoiOption">人気が高い順</option>',
  11424. '<option value="sort=_popular&amp;order=d" class="sugoiOption">並び順指定なし</option>',
  11425. '<option value="sort=_explore&amp;order=d" class="sugoiOption">新着優先</option>',
  11426. '</optgroup>'
  11427. ].join(''));
  11428. watch.VideoExplorerInitializer.videoExplorerView._contentListView._$view.find('.searchContentTemplate').html($template.html());
  11429. $template = null;
  11430.  
  11431.  
  11432.  
  11433. var RelatedTagView = function() { this.initialize.apply(this, arguments); };
  11434. RelatedTagView.prototype = {
  11435. _$view: null,
  11436. _relatedTag: null,
  11437. initialize: function(params) {
  11438. this._relatedTag = params.relatedTag;
  11439. this._$view = params.$view;
  11440. this._$list = this._$view.find('ul');
  11441. },
  11442. getView: function() {
  11443. return this._$view;
  11444. },
  11445. detach: function() {
  11446. this._$view.detach();
  11447. },
  11448. update: function(candidates) {
  11449. if (!candidates || candidates.length < 1) {
  11450. this.detach();
  11451. return;
  11452. }
  11453. if (candidates.length > 10) {
  11454. candidates = candidates
  11455. .map(function(a){return {weight:Math.random(), value:a};})
  11456. .sort(function(a, b){return a.weight - b.weight;})
  11457. .map(function(a){return a.value;});
  11458. }
  11459. var $ul = this._$list.empty();
  11460. for (var i = 0, len = Math.min(10, candidates.length); i < len; i++) {
  11461. $ul.append(this._create$tag(candidates[i].tag));
  11462. }
  11463. },
  11464. clear: function() {
  11465. this._$list.empty();
  11466. },
  11467. _create$tag: function(text) {
  11468. var
  11469. $a = $('<a/>')
  11470. .html(text)
  11471. .attr('href', 'http://search.nicovideo.jp/video/tag/' + encodeURIComponent(text))
  11472. .on('click', Util.Closure.openNicoSearch(text)),
  11473. $tag = $('<li/>').append($a);
  11474. return $tag;
  11475. }
  11476. };
  11477.  
  11478. var NewSearchOptionView = function() { this.initialize.apply(this, arguments); };
  11479. NewSearchOptionView.prototype = {
  11480. _content: null,
  11481. _$view: null,
  11482. _$startTimeRange: null,
  11483. _$lengthSecondsRange: null,
  11484. initialize: function(params) {
  11485. this._content = params.content;
  11486. this._$view = params.$view;
  11487. this._$startTimeRange = this._$view.find('.startTimeRange');
  11488. this._$lengthSecondsRange = this._$view.find('.lengthSecondsRange');
  11489. this._$musicDlFilter = this._$view.find('.musicDlFilter');
  11490. this._$ownerFilter = this._$view.find('.ownerFilter');
  11491. this._$ownerName = this._$view.find('.ownerName');
  11492. this._$resetButton = this._$view.find('.reset');
  11493.  
  11494. this._$startTimeRange .val(params.startTimeRange || '');
  11495. this._$lengthSecondsRange.val(params.lengthSecondsRange || '');
  11496. this._$musicDlFilter .attr('checked', !!params.musicDlFilter);
  11497.  
  11498. this._$startTimeRange .on('change', $.proxy(this._onStartTimeRangeSelect , this));
  11499. this._$lengthSecondsRange.on('change', $.proxy(this._onLengthSecondsRangeSelect, this));
  11500. this._$musicDlFilter .on('click', $.proxy(this._onMusicDlFilterChange , this));
  11501. this._$ownerFilter .on('click', $.proxy(this._onOwnerFilterChange , this));
  11502. this._$resetButton .on('click', $.proxy(this.reset , this));
  11503.  
  11504. EventDispatcher.addEventListener('onVideoOwnerChanged', $.proxy(this.onVideoOwnerChange, this));
  11505. this._$ownerName.text(WatchController.getOwnerName());
  11506. },
  11507. getView: function() {
  11508. return this._$view;
  11509. },
  11510. detach: function() {
  11511. this._$view.detach();
  11512. },
  11513. update: function() {
  11514. },
  11515. onVideoOwnerChange: function(ownerInfo) {
  11516. this._content.setOwnerFilter(false);
  11517. this._$ownerFilter.prop('checked', false);
  11518. this._$ownerName.text(ownerInfo.name);
  11519. },
  11520. _onStartTimeRangeSelect: function() {
  11521. this._content.setStartTimeRange(this._$startTimeRange.val());
  11522. this.contentRefresh();
  11523. },
  11524. _onLengthSecondsRangeSelect: function() {
  11525. this._content.setLengthSecondsRange(this._$lengthSecondsRange.val());
  11526. this.contentRefresh();
  11527. },
  11528. _onMusicDlFilterChange: function() {
  11529. this._content.setMusicDlFilter(!!this._$musicDlFilter.prop('checked'));
  11530. this.contentRefresh();
  11531. },
  11532. _onOwnerFilterChange: function() {
  11533. this._content.setOwnerFilter(!!this._$ownerFilter.prop('checked'));
  11534. this.contentRefresh();
  11535. },
  11536. contentRefresh: function() {
  11537. var params = this._content.getParams();
  11538. params.page = 1;
  11539. this._content.changeState(params);
  11540. this._content.refresh({page: 1});
  11541. },
  11542. refresh: function() {
  11543. //console.log('refresh!', this._content.getOwnerFilter(), this._content.getMusicDlFilter(false));
  11544. this._$startTimeRange .val(this._content.getStartTimeRange('') || '');
  11545. this._$lengthSecondsRange.val(this._content.getLengthSecondsRange('') || '');
  11546. this._$musicDlFilter .prop('checked', !!this._content.getMusicDlFilter(false));
  11547. this._$ownerFilter .prop('checked', !!this._content.getOwnerFilter());
  11548. },
  11549. reset: function() {
  11550. var v = this._$startTimeRange.val() + this._$lengthSecondsRange.val();
  11551. if (v !== '') {
  11552. this._content.setStartTimeRange('');
  11553. this._content.setLengthSecondsRange('');
  11554. this._content.setMusicDlFilter(false);
  11555. this._$startTimeRange.val('');
  11556. this._$lengthSecondsRange.val('');
  11557. this._$musicDlFilter.prop('checked', false);
  11558. this._content.changeState({ page: 1 });
  11559. //this._content.refresh({ page: 1 });
  11560. }
  11561. }
  11562. };
  11563.  
  11564. var relatedTagView = new RelatedTagView({
  11565. relatedTag: relatedTag,
  11566. $view: $('<div class="relatedTagList"><p>関連タグ: </p><ul></ul></div>')
  11567. });
  11568. var newSearchOptionView = new NewSearchOptionView({
  11569. content: content,
  11570. startTimeRange: conf.searchStartTimeRange,
  11571. lengthSecondsRange: conf.searchLengthSecondsRange,
  11572. musicDlFilter: conf.searchMusicDlFilter,
  11573. $view: $([
  11574. '<div class="newSearchOption">',
  11575. '<span>投稿日時: </span>',
  11576. '<select class="startTimeRange" name="u">',
  11577. '<option selected="selected" value="" >指定なし</option>',
  11578. '<option value="24h">24時間以内</option>',
  11579. '<option value="1w" >1週間以内</option>',
  11580. '<option value="1m" >1ヶ月(30日)以内</option>',
  11581. '<option value="3m" >3ヶ月(90日)以内</option>',
  11582. '<option value="6m" >6ヶ月(180日)以内</option>',
  11583. '</select>',
  11584. '<span>再生時間: </span>',
  11585. '<select class="lengthSecondsRange" name="l">',
  11586. '<option selected="selected" value="" >指定なし</option>',
  11587. '<option value="short">5分以内</option>',
  11588. '<option value="long" >20分以上</option>',
  11589. '</select>',
  11590. '<p>',
  11591. '<label>',
  11592. '<input type="checkbox" name="m" class="musicDlFilter">音楽DL対応のみ</input>',
  11593. '</label>',
  11594. '<label>',
  11595. '<input type="checkbox" name="owner" class="ownerFilter"><span class="ownerName">この投稿者</span>&nbsp;の動画のみ</input>',
  11596. '</label>',
  11597. '</p>',
  11598. '</div>',
  11599. ''].join(''))
  11600. });
  11601.  
  11602.  
  11603.  
  11604. content._originalWord = '';
  11605. content.changeState_org = content.changeState;
  11606. content.changeState = $.proxy(function(params, callback) {
  11607. var word = WatchApp.get(params, 'searchWord', 'string', '');
  11608. var type = WatchApp.get(params, 'searchType', 'string', this.getSearchType());
  11609. if (typeof word === 'string' && word.length > 0) {
  11610. this._originalWord = word;
  11611.  
  11612. if (conf.defaultSearchOption && conf.defaultSearchOption !== '') {
  11613. if (word.indexOf(conf.defaultSearchOption) < 0 && !word.match(/(sm|nm|so)\d+/)) {
  11614. params.searchWord += " " + conf.defaultSearchOption;
  11615. }
  11616. }
  11617. }
  11618. AnchorHoverPopup.hidePopup();
  11619.  
  11620. EventDispatcher.dispatch('onSearchStart', this._originalWord, type);
  11621. this.changeState_org(params, callback);
  11622. }, content);
  11623.  
  11624. // ニコニコ新検索エンジンを使うための布石
  11625. content._searchEngineType = conf.searchEngine;
  11626. content._lastSearchEngineType = conf.searchEngine;
  11627. content.setSearchEngineType = $.proxy(function(type) {
  11628. this._searchEngineType = type;
  11629. this.updateSearchPageItemCount();
  11630. }, content);
  11631. content.updateSearchPageItemCount = $.proxy(function() {
  11632. this._pager._pageItemCount = this._searchEngineType === 'sugoi' ? conf.searchPageItemCount : 32;
  11633. }, content);
  11634. content.getSearchEngineType = $.proxy(function() {
  11635. return this._searchEngineType === 'sugoi' ? 'sugoi' : 'normal';
  11636. }, content);
  11637. content.setLastSearchEngineType = $.proxy(function(type) { this._lastSearchEngineType = type; }, content);
  11638. content.getLastSearchEngineType = $.proxy(function() { return this._lastSearchEngineType; }, content);
  11639. content._newSearchWrapper = newSearchWrapper;
  11640.  
  11641. content._startTimeRange = conf.searchStartTimeRange;
  11642. content._lengthSecondsRange = conf.searchLengthSecondsRange;
  11643. content._musicDlFilter = conf.searchMusicDlFilter;
  11644. content._ownerFilter = false;
  11645.  
  11646. content.getStartTimeRange = $.proxy(function() { return this._startTimeRange; }, content);
  11647. content.getLengthSecondsRange = $.proxy(function() { return this._lengthSecondsRange; }, content);
  11648. content.getMusicDlFilter = $.proxy(function() { return this._musicDlFilter; }, content);
  11649. content.getOwnerFilter = $.proxy(function() { return this._ownerFilter; }, content);
  11650. content.setStartTimeRange = $.proxy(function(value) {
  11651. this._startTimeRange = value;
  11652. conf.setValue('searchStartTimeRange', value);
  11653. }, content);
  11654. content.setLengthSecondsRange = $.proxy(function(value) {
  11655. this._lengthSecondsRange = value;
  11656. conf.setValue('searchLengthSecondsRange',value);
  11657. }, content);
  11658. content.setMusicDlFilter = $.proxy(function(value) {
  11659. this._musicDlFilter = !!value;
  11660. conf.setValue('searchMusicDlFilter', !!value);
  11661. }, content);
  11662. content.setOwnerFilter = $.proxy(function(value) {
  11663. this._ownerFilter = !!value;
  11664. }, content);
  11665.  
  11666. // 新検索独自のソート順への対応
  11667. content._searchSortOrder._flush_org = content._searchSortOrder._flush;
  11668. content._searchSortOrder._flush = $.proxy(function() {
  11669. var sort = this._sort[WatchApp.ns.components.videoexplorer.model.SearchType.KEYWORD];
  11670. if (sort === '_hot' || sort === '_popular' || sort === '_explore') { // 新検索にしかないパラメータは保存しない
  11671. return;
  11672. }
  11673. this._flush_org();
  11674. }, content._searchSortOrder);
  11675.  
  11676.  
  11677. EventDispatcher.addEventListener('on.config.searchPageItemCount', function() {
  11678. content.updateSearchPageItemCount();
  11679. });
  11680.  
  11681. content.getParams_org = content.getParams;
  11682. content.getParams = $.proxy(function() {
  11683. var params = this.getParams_org();
  11684. params = $.extend(true, {
  11685. l: this.getLengthSecondsRange(),
  11686. u: this.getStartTimeRange(),
  11687. m: this.getMusicDlFilter(),
  11688. size: this._pager._pageItemCount
  11689. }, params);
  11690. if (this.getOwnerFilter()) {
  11691. if (WatchController.isChannelVideo()) {
  11692. params.channelId = WatchController.getOwnerId();
  11693. } else {
  11694. params.userId = WatchController.getOwnerId();
  11695. }
  11696. }
  11697. return params;
  11698. }, content);
  11699.  
  11700. // タグ検索だけ毎回ソート順がデフォルトにリセットされるようになったので、
  11701. // デフォルト値を書き換えるという力技で対抗
  11702. SearchSortOrder.TAG_DEFAULT_SORT = conf.searchSortType;
  11703. SearchSortOrder.TAG_DEFAULT_ORDER = conf.searchSortOrder;
  11704. content._searchSortOrder.getSortFromCookie = function() { return conf.searchSortType; };
  11705. content._searchSortOrder.getOrderFromCookie = function() { return conf.searchSortOrder; };
  11706.  
  11707. content.load_org = content.load;
  11708. content.load = $.proxy(function(params, callback) {
  11709. var word = this.getSearchWord();
  11710. if (this.getSearchEngineType() !== 'sugoi' || word.length <= 0 || word.match(/(sm|nm|so)\d+/)) {
  11711. // 新検索ではもしかして~が取得できないため、検索ワードに動画IDっぽい文字列が含まれてる場合は旧タグ検索を使う。
  11712. this.setLastSearchEngineType('normal');
  11713. params.sort = 'n';
  11714. params.order = 'd';
  11715. this.load_org(params, callback);
  11716. } else {
  11717. this.setLastSearchEngineType('sugoi');
  11718. params = this.getParams();
  11719.  
  11720.  
  11721. this._newSearchWrapper.load(params, function(err, result) {
  11722. console.log('%cNewNicoSearchWrapper result', 'color: green;', result);
  11723. callback(err, result);
  11724. });
  11725. }
  11726. }, content);
  11727. content.setSearchEngineType(conf.searchEngine);
  11728.  
  11729. EventDispatcher.addEventListener('on.config.searchEngine', function(type) {
  11730. content.setSearchEngineType(type);
  11731. });
  11732.  
  11733.  
  11734. var
  11735. overrideSearchSortOrder = function(proto) { // ソート順を記憶するためのフック
  11736. proto.getSort_org = proto.getSort;
  11737. proto.getSort = function() {
  11738. var sort = conf.searchSortType;
  11739. if ((sort === '_hot' || sort === '_popular' || sort === '_explore') && content.getLastSearchEngineType() !== 'sugoi') {
  11740. // 通常検索で新検索にしかないソート順だったらデフォルトのnを返す
  11741. return 'n';
  11742. }
  11743. return conf.searchSortType;
  11744. };
  11745.  
  11746. proto.setSort_org = proto.setSort;
  11747. proto.setSort = function(type, sort) {
  11748. conf.setValue('searchSortType', sort);
  11749. SearchSortOrder.TAG_DEFAULT_SORT = sort;
  11750. this.setSort_org(type, sort);
  11751. };
  11752.  
  11753. proto.getOrder_org = proto.getOrder;
  11754. proto.getOrder = function() {
  11755. return conf.searchSortOrder;
  11756. };
  11757.  
  11758. proto.setOrder_org = proto.setOrder;
  11759. proto.setOrder = function(type, order) {
  11760. if (content.getLastSearchEngineType() === 'sugoi') { // 新検索の時だけソート順を記憶
  11761. SearchSortOrder.TAG_DEFAULT_ORDER = order;
  11762. conf.setValue('searchSortOrder', order);
  11763. }
  11764. this.setOrder_org(type, order);
  11765. };
  11766. },
  11767. overrideSearchContentView = function(proto, relatedTag) {
  11768. proto._updateRelatedTag = function() {
  11769. if (!conf.enableRelatedTag) { return; }
  11770. var word = this._content._originalWord;
  11771. relatedTagView.clear();
  11772.  
  11773. if (typeof word === 'string' && word.length > 0) {
  11774. this._$header.append(relatedTagView.getView());
  11775. relatedTag.load(word, function(err, result) {
  11776. console.log('SearchContentView._updateRelatedTag', err, result);
  11777. if (err) {
  11778. console.log('load suggest fail', err, result);
  11779. } else {
  11780. relatedTagView.update(result.values);
  11781. }
  11782. });
  11783. }
  11784. };
  11785.  
  11786. proto.detach_org = proto.detach;
  11787. proto.detach = function() {
  11788. this.detach_org();
  11789. newSearchOptionView.detach();
  11790. relatedTagView.detach();
  11791. };
  11792.  
  11793. proto.onUpdate_org = proto.onUpdate;
  11794. proto.onUpdate = function() {
  11795. this.onUpdate_org();
  11796. this._$content.find('.searchBox').after(newSearchOptionView.getView());
  11797. this._updateRelatedTag();
  11798. var engine = this._content.getLastSearchEngineType();
  11799. newSearchOptionView.refresh();
  11800. $('.videoExplorerBody')
  11801. .toggleClass('w_sugoiSearch', engine === 'sugoi')
  11802. .toggleClass('w_normalSearch', engine !== 'sugoi');
  11803. };
  11804.  
  11805. proto.onError_org = proto.onError;
  11806. proto.onError = function() {
  11807. this.onError_org();
  11808. this._$header.append(newSearchOptionView.getView());
  11809. this._updateRelatedTag();
  11810. var engine = this._content.getLastSearchEngineType();
  11811. $('.videoExplorerBody')
  11812. .toggleClass('w_sugoiSearch', engine === 'sugoi')
  11813. .toggleClass('w_normalSearch', engine !== 'sugoi');
  11814. };
  11815.  
  11816. };
  11817.  
  11818. overrideSearchSortOrder(SearchSortOrder.prototype);
  11819. overrideSearchContentView(View.prototype, relatedTag);
  11820.  
  11821. } // end initSearchContent
  11822.  
  11823. function initUserVideoContent($, conf, w) {
  11824. var ContentType = WatchApp.ns.components.videoexplorer.model.ContentType;
  11825. var vec = watch.VideoExplorerInitializer.videoExplorerController;
  11826. var explorer = vec.getVideoExplorer();
  11827. var content = explorer.getContentList().getContent(ContentType.USER_VIDEO);
  11828. var pager = content._pager;
  11829.  
  11830. pager._pageItemCount = conf.searchPageItemCount;
  11831. pager._displayPageCount = 5;
  11832. EventDispatcher.addEventListener('on.config.searchPageItemCount', function(v) {
  11833. pager._pageItemCount = v;
  11834. });
  11835.  
  11836. }
  11837.  
  11838. function initUploadedVideoContent($, conf, w) {
  11839. var ContentType = WatchApp.ns.components.videoexplorer.model.ContentType;
  11840. var vec = watch.VideoExplorerInitializer.videoExplorerController;
  11841. var explorer = vec.getVideoExplorer();
  11842. var content = explorer.getContentList().getContent(ContentType.UPLOADED_VIDEO);
  11843. var pager = content._pager;
  11844.  
  11845.  
  11846. // 本家のマイナーバグ修正
  11847. content.setUserId_org = content.setUserId;
  11848. content.setUserId = $.proxy(function(id) {
  11849. var currentId = this.getUserId();
  11850. if (currentId !== id) {
  11851. this.setPage(1);
  11852. }
  11853. this.setUserId_org(id);
  11854. }, content);
  11855.  
  11856. pager._pageItemCount = conf.searchPageItemCount;
  11857. pager._displayPageCount = 5;
  11858. EventDispatcher.addEventListener('on.config.searchPageItemCount', function(v) {
  11859. pager._pageItemCount = v;
  11860. });
  11861. }
  11862.  
  11863.  
  11864. var isSquareCssInitialized = false;
  11865. function initSquareThumbnail() {
  11866. var isSquare = true;// !!conf.squareThumbnail;
  11867. if (isSquare && !isSquareCssInitialized) {
  11868. var __css__ = Util.here(function() {/*
  11869. {* 元のCSSを打ち消すためにやや冗長 *}
  11870. #videoExplorer .noImage,
  11871. #videoExplorer.w_adjusted .item .thumbnail {
  11872. display: none !important;
  11873. }
  11874. #videoExplorer .thumbnailContainer {
  11875. background-size: contain;
  11876. background-repeat: no-repeat;
  11877. background-position: center center;
  11878. }
  11879.  
  11880. #videoExplorer.w_adjusted .column4 .thumbnailContainer,
  11881. #videoExplorer.w_adjusted .smallThumbnail .thumbnailContainer {
  11882. width: 130px; height: 100px;
  11883. margin-right: 7px;
  11884. border: 1px solid #888;
  11885. }
  11886.  
  11887. #videoExplorer.w_adjusted .column4 .uadFrame,
  11888. #videoExplorer.w_adjusted .smallThumbnail .uadFrame,
  11889. #videoExplorer.w_adjusted .uadTagRelated .uadFrame {
  11890. width: 130px; height: 100px;
  11891. background-size: 100% 100%;
  11892. }
  11893. #videoExplorer.w_adjusted .uadTagRelated .default .itemList .item .imageContainer {
  11894. width: 130px; height: 100px;
  11895. }
  11896.  
  11897. #videoExplorer.w_adjusted .column1 .item .thumbnailContainer {
  11898. border: 1px solid #888;
  11899. margin-right: 8px;
  11900. }
  11901. #videoExplorer.w_adjusted .column1 .item.smallThumbnail .thumbnailContainer {
  11902. border-width: 0px 15px 0px 15px;
  11903. border-color: #888;
  11904. }
  11905. */});
  11906.  
  11907. addStyle(__css__, 'squareThumbnailCss');
  11908. isSquareCssInitialized = true;
  11909. }
  11910. //$('#videoExplorer').toggleClass('squareThumbnail', isSquare);
  11911. } //
  11912.  
  11913. function initPageBottom($, conf, w) {
  11914. function updateHideVideoExplorerExpand(v) {
  11915. $('#content, #outline').toggleClass('w_hideSearchExpand', v === true);
  11916. }
  11917. function updateIchibaVisibility(v) {
  11918. $('#outline').toggleClass('noIchiba', v === 'hidden');
  11919. }
  11920. function updateReviewVisibility(v) {
  11921. $('#outline').toggleClass('noReview', v === 'hidden');
  11922. }
  11923. function updateBottomContentsVisibility(v) {
  11924. $('#bottomContentTabContainer, #footer').toggleClass('noBottom', v === 'hidden');
  11925. }
  11926.  
  11927. EventDispatcher.addEventListener('on.config.hideVideoExplorerExpand', updateHideVideoExplorerExpand);
  11928. EventDispatcher.addEventListener('on.config.ichibaVisibility', updateIchibaVisibility);
  11929. EventDispatcher.addEventListener('on.config.reviewVisibility', updateReviewVisibility);
  11930. EventDispatcher.addEventListener('on.config.bottomContentsVisibility', updateBottomContentsVisibility);
  11931. if (conf.hideVideoExplorerExpand === true) { updateHideVideoExplorerExpand(true); }
  11932. if (conf.ichibaVisibility !== 'visible') { updateIchibaVisibility(conf.ichibaVisibility); }
  11933. if (conf.reviewVisibility !== 'visible') { updateReviewVisibility(conf.reviewVisibility); }
  11934. if (conf.bottomContentsVisibility !== 'visible') { updateBottomContentsVisibility(conf.bottomContentsVisibility); }
  11935.  
  11936. var $bottomToggle = $('<div class="toggleBottom"><div class="openBottom">▽</div><div class="closeBottom">△</div></div>');
  11937. $bottomToggle.on('click', function() {
  11938. var v = conf.bottomContentsVisibility;
  11939. conf.setValue('bottomContentsVisibility', v === 'hidden' ? 'visible' : 'hidden');
  11940. //ConfigPanel.refresh();
  11941. }).attr('title', '市場・レビューの開閉');
  11942. $('#footer').append($bottomToggle);
  11943. } //
  11944.  
  11945.  
  11946.  
  11947. function initShortcutKey() {
  11948. var list = [
  11949. {name: 'shortcutTogglePlay', exec: function(e) {
  11950. WatchController.togglePlay();
  11951. }},
  11952. {name: 'shortcutDefMylist', exec: function(e) {
  11953. WatchController.addDefMylist();
  11954. }},
  11955. {name: 'shortcutMylist', exec: function(e) {
  11956. $('#mylist_add_frame').find('.mylistAdd').click();
  11957. }},
  11958. {name: 'shortcutOpenDefMylist', exec: function(e) {
  11959. WatchController.showDeflist();
  11960. WatchController.scrollToVideoPlayer(true);
  11961. }},
  11962. {name: 'shortcutOpenSearch', exec: function(e) {
  11963. WatchController.openSearch();
  11964. if (!$('body').hasClass('content-fix')) {
  11965. WatchController.scrollToVideoPlayer(true);
  11966. }
  11967. }},
  11968. {name: 'shortcutOpenRecommend', exec: function(e) {
  11969. WatchController.openRecommend();
  11970. if (!$('body').hasClass('content-fix')) {
  11971. WatchController.scrollToVideoPlayer(true);
  11972. }
  11973. }},
  11974. {name: 'shortcutScrollToNicoPlayer', exec: function(e) {
  11975. WatchController.scrollToVideoPlayer(true);
  11976. }},
  11977. {name: 'shortcutCommentVisibility', exec: function(e) {
  11978. WatchController.commentVisibility('toggle');
  11979. }},
  11980. {name: 'shortcutShowOtherVideo', exec: function(e) {
  11981. WatchController.openVideoOwnersVideo();
  11982. }},
  11983. {name: 'shortcutMute', exec: function(e) {
  11984. WatchController.mute('toggle');
  11985. }},
  11986. {name: 'shortcutDeepenedComment', exec: function(e) {
  11987. WatchController.deepenedComment('toggle');
  11988. }},
  11989. {name: 'shortcutToggleStageVideo', exec: function(e) {
  11990. WatchController.toggleStageVideo();
  11991. }},
  11992. {name: 'shortcutInvisibleInput', exec: function(e) {
  11993. $('.invisibleCommentInput').focus();
  11994. }}
  11995. ];
  11996. for (var v in list) {
  11997. var n = list[v].name;
  11998. list[v].keyMatch = KeyMatch.create(conf[n]);
  11999. }
  12000.  
  12001. ConfigPanel.addChangeEventListener(function(name, newValue, oldValue) {
  12002. for (var v in list) {
  12003. var n = list[v].name;
  12004. if (n === name) {
  12005. list[v].keyMatch = KeyMatch.create(newValue);
  12006. }
  12007. }
  12008. });
  12009.  
  12010. $('body').on('keydown.watchItLater', function(e) {
  12011. // 一部のキーボードについているMusic Key(正式名称不明)に対応 Chromeしか拾えない?
  12012. if (e.keyCode === 178) { // 停止
  12013. WatchController.togglePlay();
  12014. } else
  12015. if (e.keyCode === 179) { // 一時停止
  12016. WatchController.togglePlay();
  12017. } else
  12018. if (e.keyCode === 177) { // 前の曲
  12019. if (WatchController.vpos() > 2000) {
  12020. WatchController.vpos(0);
  12021. } else {
  12022. WatchController.prevVideo();
  12023. }
  12024. } else
  12025. if (e.keyCode === 176) { // 次の曲
  12026. WatchController.nextVideo();
  12027. }
  12028. if (e.target.tagName === 'SELECT' || e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
  12029. return;
  12030. }
  12031. // 全画面時はFlashにフォーカスがなくてもショートカットキーが効くようにする
  12032.  
  12033. for (var v in list) {
  12034. var n = list[v].name;
  12035. if (list[v].keyMatch.test(e)) {
  12036. list[v].exec(e);
  12037. }
  12038. }
  12039. });
  12040. } //
  12041.  
  12042. function initNicoS($, conf, w) {
  12043. WatchJsApi.nicos.addEventListener('nicoSJump', function(e) {
  12044. if (conf.ignoreJumpCommand) {
  12045. e.cancel();
  12046. Popup.show('「@ジャンプ」コマンドをキャンセルしました');
  12047. }
  12048. });
  12049. var seekCount = 0;
  12050. WatchApp.ns.model.player.NicoSClientConnector.addEventListener('nicoSSeek', function(e) {
  12051. seekCount++;
  12052. if (conf.nicoSSeekCount < 0) return;
  12053. if (seekCount > conf.nicoSSeekCount) {
  12054. e.cancel();
  12055. }
  12056. });
  12057. // 動画が切り替わったか、最後まで視聴したらカウンターリセット
  12058. EventDispatcher.addEventListener('onVideoInitialized', function() {
  12059. seekCount = 0;
  12060. });
  12061. EventDispatcher.addEventListener('onVideoEnded', function() {
  12062. seekCount = 0;
  12063. });
  12064. } //
  12065.  
  12066. function initMouse() {
  12067. ConfigPanel.addChangeEventListener(function(name, newValue, oldValue) {
  12068. if (name === 'mouseClickWheelVolume') {
  12069. if (oldValue === 0) {
  12070. initWheelWatch();
  12071. } else
  12072. if (newValue === 0) {
  12073. $(document)
  12074. .unbind('mousewheel.watchItLaterWheelWatch')
  12075. .unbind('mousedown.watchItLaterWheelWatch')
  12076. .unbind('mouseup.watchItLaterWheelWatch');
  12077. }
  12078. }
  12079. });
  12080.  
  12081. function initWheelWatch() {
  12082. var leftDown = false, rightDown = false, isVolumeChanged = false;
  12083. var event = {
  12084. cancel: false,
  12085. reset: function() { this.cancel = false; return this; },
  12086. preventDefault: function() { this.cancel = true;}
  12087. };
  12088. $(document).on('mousewheel.watchItLaterWheelWatch', function(e, delta) {
  12089. var button = -1;
  12090. // TODO: マジックナンバーを
  12091. if (typeof e.buttons === 'number') { // firefox
  12092. button = e.buttons;
  12093. } else { // chrome
  12094. if (leftDown) { button = 1; }
  12095. else
  12096. if (rightDown) { button = 2; }
  12097. }
  12098. if (button < 1) {
  12099. EventDispatcher._dispatch('onWheelNoButton', e, delta);
  12100. return;
  12101. }
  12102. EventDispatcher.dispatch('onWheelAndButton', event.reset(), delta, button);
  12103. if (event.cancel) {
  12104. e.preventDefault();
  12105. return;
  12106. }
  12107. if (conf.mouseClickWheelVolume !== button) {
  12108. return;
  12109. }
  12110.  
  12111. var v = WatchController.volume(), r;
  12112. isVolumeChanged = true;
  12113. // 音量を下げる時は「うわ音でけぇ!」
  12114. // 音量を上げる時は「ちょっと聞こえにくいな」…というパターンが多いので、変化の比率が異なる
  12115. if (delta > 0) {
  12116. v = Math.max(v, 1);
  12117. r = (v < 5) ? 1.3 : 1.1;
  12118. v = WatchController.volume(v * r);
  12119. } else {
  12120. v = WatchController.volume(Math.floor(v / 1.2));
  12121. }
  12122. e.preventDefault();
  12123. }).on('mousedown.watchItLaterWheelWatch', function(e) { // chromeはホイールイベントでe.buttonsが取れないため
  12124. if (e.which === 1) leftDown = true;
  12125. if (e.which === 3) rightDown = true;
  12126. }).on('mouseup.watchItLaterWheelWatch', function(e) {
  12127. if (e.which === 1) leftDown = false;
  12128. if (e.which === 3) rightDown = false;
  12129. }).on('contextmenu.watchItLaterWheelWatch', function(e) {
  12130. if (isVolumeChanged) {
  12131. e.preventDefault();
  12132. }
  12133. isVolumeChanged = false;
  12134. });
  12135. }
  12136. window.setTimeout(function() { initWheelWatch(); }, 5000);
  12137. } // end initMouse
  12138.  
  12139. function initTouch() {
  12140. var touchInitialized = false;
  12141. TouchEventDispatcher.onflick(function(e) {
  12142. var se = e.startEvent;
  12143. if (!conf.enableQTouch) {return; }
  12144. if (e.direction === 'right') {
  12145. if (se.target.id === 'playerTabWrapper') {
  12146. $(se.target).addClass('w_active');
  12147. }
  12148. if (!touchInitialized) {
  12149. $('#mylist_add_frame, #leftPanelTabContainer, .videoExplorerMenu, #playerTabWrapper').addClass('w_touch');
  12150. $('.userProfile, .resultPagination, #searchResultContainer select, .playlistMenuPopup').addClass('w_touch');
  12151. isTouchActive = true;
  12152. touchInitialized = true;
  12153. }
  12154. } else
  12155. if (e.direction === 'left') {
  12156. if (se.target.tagName === 'DIV' &&
  12157. $.contains('#playerTabWrapper', se.target)) {
  12158. $('#playerTabWrapper').removeClass('w_active');
  12159. }
  12160. }
  12161. });
  12162. } //
  12163.  
  12164. function initOtherCss() {
  12165. var __dynamic_css_template__ = Util.here(function() {/*
  12166. .full_with_browser.w_fullScreenMenu #nicoHeatMap {
  12167. transform: scaleX($scale); -webkit-transform: scaleX($scale); display: block;
  12168. }
  12169. */});
  12170. var exStyle = null;
  12171. var updateDynamicCss = function() {
  12172. var css = __dynamic_css_template__;
  12173. var innerWidth = $('body').innerWidth();
  12174. css = css.split('$scale').join($('body').innerWidth() / 100);
  12175. if (exStyle) {
  12176. exStyle.innerHTML = css;
  12177. return exStyle;
  12178. } else {
  12179. return addStyle(css, 'expression');
  12180. }
  12181. };
  12182. exStyle = updateDynamicCss();
  12183.  
  12184. EventDispatcher.addEventListener('onWindowResizeEnd', function() {
  12185. updateDynamicCss();
  12186. });
  12187.  
  12188. var __gpuLayer__ = (function() {/*
  12189. body.videoExplorer.content-fix #playerTabWrapper,
  12190. body.videoExplorer.content-fix .videoExplorerMenu,
  12191. body.videoExplorer.content-fix #playlist,
  12192. {*#playerTabWrapper .playerCommentPanel*}
  12193. body:not(.full_with_browser) .mylistPopupPanel.fixed
  12194. {
  12195. -moz-transform: translateZ(0);
  12196. -webkit-transform: translateZ(0);
  12197. transform: translateZ(0);
  12198. }
  12199. {* Firefoxだと問題がある要素はこちら *}
  12200. body.videoExplorer.content-fix #leftPanel,
  12201. body.videoExplorer.content-fix #content,
  12202. #popupMarquee
  12203. {
  12204. -webkit-transform: translateZ(0);
  12205. }
  12206. */}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1]
  12207. .replace(/\{\*/g, '/*').replace(/\*\}/g, '*/');
  12208. if (conf.enableGpuLayer) {
  12209. addStyle(__gpuLayer__, 'watchItLaterGpuLayer');
  12210. }
  12211.  
  12212. var __debug_css__ = Util.here(function() {/*
  12213. .videoExplorer #playerContainerWrapper, .videoExplorer #external_nicoplayer,
  12214. .videoExplorerOpening #playerContainerWrapper, .videoExplorerOpening #external_nicoplayer
  12215. {
  12216. {*transition: width 0.4s ease, height 0.4s ease;*}
  12217. }
  12218. #playerAlignmentArea .toggleCommentPanel {
  12219. {*transition: 0.4s ease-in-out;*}
  12220. }
  12221. {*
  12222. body:not(.videoExplorer):not(.full_with_browser) #playerNicoplayer,
  12223. body:not(.videoExplorer):not(.full_with_browser) #playerAlignmentArea{
  12224. transition: width 0.4s ease;
  12225. }
  12226. body:not(.videoExplorer):not(.full_with_browser) #nicoplayerContainer{
  12227. transition: height 0.4s ease 0.4s;
  12228. }
  12229. *}
  12230.  
  12231. */});
  12232. if (conf.debugMode) addStyle(__debug_css__, 'watchItLater_debug_css');
  12233. } // end initOtherCss
  12234.  
  12235. function initCustomPlayerSize($, conf, w) {
  12236. var tpl = Util.here(function() {/*
  12237. body.size_normal.w_size_custom:not(.videoExplorer):not(.full_with_browser) #playerAlignmentArea
  12238. { width: {$alignmentAreaWidth}px; }
  12239. body.size_normal.w_size_custom:not(.videoExplorer):not(.full_with_browser) .w_wide #playerAlignmentArea
  12240. { width: {$alignmentAreaWideWidth}px; }
  12241.  
  12242. body.size_normal.w_size_custom:not(.videoExplorer):not(.full_with_browser) #nicoplayerContainer {
  12243. height: {$playerHeight}px !important;
  12244. }
  12245. body.size_normal.w_size_custom:not(.videoExplorer):not(.full_with_browser) #playerNicoplayer
  12246. { width: {$playerWidth}px !important;}
  12247.  
  12248. body.size_normal.w_size_custom:not(.videoExplorer):not(.full_with_browser) #external_nicoplayer
  12249. { width: {$playerWidth}px !important; height: {$playerHeight}px !important; }
  12250.  
  12251. body.size_normal.w_size_custom:not(.videoExplorer):not(.full_with_browser) #nicoHeatMapContainer {
  12252. width: {$playerWidth}px !important;
  12253. }
  12254. body.size_normal.w_size_custom:not(.videoExplorer):not(.full_with_browser) #nicoHeatMap {
  12255. transform: scaleX({$heatMapScale}); -webkit-transform: scaleX({$heatMapScale});
  12256. }
  12257. body.size_normal.w_size_custom:not(.videoExplorer):not(.full_with_browser) #smart_music_kiosk {
  12258. -webkit-transform: scaleX({$songriumScale}); -webkit-transform-origin: 0 0;
  12259. }
  12260.  
  12261. body.size_normal.w_size_custom:not(.videoExplorer):not(.full_with_browser) #inspire_category {
  12262. left: {$songriumCategoryLeft}px !important;
  12263. }
  12264.  
  12265. */});
  12266. var PROFILE_SET = {
  12267. 'WQHD': [2560, 1440],
  12268. '1080p': [1920, 1080],
  12269. 'HD+': [1600, 900],
  12270. 'WXGA+': [1400, 810],
  12271. 'WXGA': [1366, 768],
  12272. '720p': [1280, 720],
  12273. 'WSVGA+': [1152, 648],
  12274. 'WSVGA': [1024, 576],
  12275. 'QHD': [ 960, 540]//, // 元より小さいパターンもサポートする?
  12276. // 'WVGA': [ 854, 480],
  12277. // 'NORMAL': [ 640, 360],
  12278. // 'SMALL': [ 512, 288],
  12279. // 'ECO': [ 352, 200],
  12280. };
  12281. var CONTROL_HEIGHT = 46, INPUT_HEIGHT = 30, PLAYER_TAB_WIDTH = 326 + 10, PLAYER_TAB_WIDTH_WIDE = 420 + 10, TAB_MARGIN = 0;
  12282. var SONGRIUM_WIDTH = 898;
  12283. var HORIZONTAL_MARGIN = 1.05; // 両端に 2.5% x 2 のマージンがある
  12284. var $videoHeader = $('#videoHeader');
  12285.  
  12286. var getTargetSize = function(targetWidth, targetHeight) {
  12287. var plWidth = Math.round(targetWidth * HORIZONTAL_MARGIN);
  12288. var plHeight = targetHeight + CONTROL_HEIGHT + INPUT_HEIGHT;
  12289. var alWidth = plWidth + PLAYER_TAB_WIDTH;
  12290. var alWidthW = plWidth + PLAYER_TAB_WIDTH_WIDE;
  12291. return {
  12292. playerWidth: plWidth,
  12293. playerHeight: plHeight,
  12294. alignmentAreaWidth: alWidth,
  12295. alignmentAreaWideWidth: alWidthW,
  12296. heatMapScale: plWidth / 100,
  12297. songriumScale: plWidth / SONGRIUM_WIDTH,
  12298. songriumCategoryLeft: plWidth + 32
  12299. };
  12300. };
  12301. var suggestProfile = function() {
  12302. var iw = $(window).innerWidth(), ih = $(window).innerHeight();
  12303. var hh = (WatchController.isFixedHeader() ? $("#siteHeader").outerHeight() : 0);
  12304. iw -= (conf.wideCommentPanel ? PLAYER_TAB_WIDTH_WIDE : PLAYER_TAB_WIDTH);
  12305. iw -= TAB_MARGIN;
  12306.  
  12307. ih -= hh;
  12308. for (var v in PROFILE_SET) {
  12309. var w = PROFILE_SET[v][0], h = PROFILE_SET[v][1];
  12310. if (w * HORIZONTAL_MARGIN <= iw && h <= ih) {
  12311. return {w: w, h: h, name: v};
  12312. }
  12313. }
  12314. return null;
  12315. };
  12316. var generateCss = function() {
  12317. var profile = '';
  12318. if (PROFILE_SET[conf.customPlayerSize]) {
  12319. var s = PROFILE_SET[conf.customPlayerSize];
  12320. profile = {w: s[0], h: s[1], name: conf.customPlayerSize};
  12321. } else {
  12322. profile = suggestProfile();
  12323. }
  12324. if (!profile) return {css: '', profile: ''};
  12325. var ts = getTargetSize(profile.w, profile.h);
  12326. var css = tpl;
  12327. for (var v in ts) {
  12328. css = css.split('{$' + v + '}').join(ts[v]);
  12329. }
  12330. return {css: css, profile: profile};
  12331. };
  12332. var customStyleElement = null, lastCssName = '';
  12333. var updateStyle = function() {
  12334. if (WatchController.isFullScreen() || WatchController.isSearchMode()) {
  12335. return;
  12336. }
  12337.  
  12338. var result = generateCss();
  12339. var css = result.css, profile = result.profile, name = profile.name;
  12340.  
  12341. if (lastCssName === name) {
  12342. return;
  12343. }
  12344. lastCssName = name;
  12345. if (customStyleElement) {
  12346. customStyleElement.innerHTML = css;
  12347. } else {
  12348. customStyleElement = addStyle(css, 'customPlayerSize');
  12349. }
  12350. EventDispatcher.dispatch('onPlayerAlignmentAreaResize');
  12351. };
  12352. var toggleCustomSize = function(v) {
  12353. if (typeof v === 'boolean') {
  12354. $('body').toggleClass('w_size_custom', v);
  12355. } else {
  12356. $('body').toggleClass('w_size_custom');
  12357. }
  12358. };
  12359. var clearStyle = function() {
  12360. if (customStyleElement) {
  12361. customStyleElement.innerHTML = '';
  12362. toggleCustomSize(false);
  12363. }
  12364. lastCssName = '';
  12365. };
  12366. if (conf.customPlayerSize !== '') {
  12367. updateStyle();
  12368. toggleCustomSize();
  12369. }
  12370. EventDispatcher.addEventListener('on.config.customPlayerSize', function(v) {
  12371. if (v === '') {
  12372. clearStyle();
  12373. } else {
  12374. updateStyle();
  12375. toggleCustomSize(true);
  12376. }
  12377. });
  12378. EventDispatcher.addEventListener('on.config.wideCommentPanel', function(v) {
  12379. if (conf.customPlayerSize !== '') {
  12380. updateStyle();
  12381. }
  12382. });
  12383. EventDispatcher.addEventListener('onWindowResizeEnd', function() {
  12384. if (conf.customPlayerSize !== '') {
  12385. updateStyle();
  12386. }
  12387. });
  12388.  
  12389. if (conf.debugMode) {
  12390. WatchItLater.debug.customSize = {
  12391. suggestProfile: suggestProfile,
  12392. getTargetSize: getTargetSize,
  12393. generateCss: generateCss,
  12394. updateStyle: updateStyle,
  12395. toggleCustomSize: toggleCustomSize
  12396. };
  12397. }
  12398.  
  12399. } //
  12400.  
  12401. function initStageVideo($, conf, w) {
  12402. var onStageVideoAvailabilityUpdated = function(v) {
  12403. $('#nicoplayerContainerInner').toggleClass('stageVideo', v);
  12404. };
  12405.  
  12406. EventDispatcher.addEventListener('onFirstVideoInitialized', function() {
  12407. onStageVideoAvailabilityUpdated(WatchController.isStageVideoAvailable());
  12408. if (conf.forceEnableStageVideo) {
  12409. try {$('#external_nicoplayer')[0].setIsForceUsingStageVideo(true); } catch (e) { console.log(e);}
  12410. }
  12411. });
  12412.  
  12413. var pac = watch.PlayerInitializer.playerAreaConnector;
  12414.  
  12415. pac.addEventListener('onStageVideoAvailabilityUpdated', onStageVideoAvailabilityUpdated);
  12416.  
  12417. // console.log('StageVideo', $('#external_nicoplayer')[0].isStageVideoSupported() ? 'supported' : 'not supported');
  12418. // console.log('ColorSpaces', $('#external_nicoplayer')[0].getStageVideoSupportedColorSpaces());
  12419.  
  12420. } //
  12421.  
  12422. /**
  12423. * Chromeなら ALT+C
  12424. * Firefoxなら ALT+SHIFT+C で召喚
  12425. *
  12426. * どこでもコメント入力開始する隠し機能
  12427. *
  12428. * :m[0-9a-p] xxxxx でマイリストする機能もある
  12429. *
  12430. **/
  12431. function initInvisibleCommentInput($, conf, w) {
  12432. var $view = $(Util.here(function() {/*
  12433. <div class="invisibleInput">
  12434. <form action="javascript: void(0);">
  12435. <input type="text" value="" autocomplete="on" name="chat" accesskey="c"
  12436. list="myMylist" placeholder="コメント入力" class="invisibleCommentInput"
  12437. maxlength="75"
  12438. ></form>
  12439. <label><input type="checkbox" class="autoPause" checked="checked">動画を自動で一時停止する</label>
  12440. </div>
  12441. */}));
  12442. var css = Util.here(function() {/*
  12443. .invisibleInput {
  12444. position: fixed; z-index: 10000;
  12445. left: 100px; bottom: -100px; width: 300px;
  12446. padding: 16px;
  12447. border: 1px solid black;
  12448. background: #eee;
  12449.  
  12450. transition: bottom 0.4s ease 0.1s;
  12451. }
  12452. .invisibleInput.active {
  12453. bottom: 50px;
  12454. transition: bottom 0.4s ease;
  12455. box-shadow: 2px 2px 2px #333;
  12456. }
  12457.  
  12458. .invisibleInput .invisibleCommentInput {
  12459. width: 100%; font-size: 140%;
  12460. }
  12461. */});
  12462. var $dataList = $('<datalist id="myMylist"></datalist>');
  12463. var $form = $view.find('form');
  12464. var $input = $view.find('input');
  12465. var $autoPause = $view.find('.autoPause').prop('checked', conf.autoPauseInvisibleInput);
  12466.  
  12467. var prevent = function(e) { e.stopPropagation(); e.preventDefault(); };
  12468. var preventEsc = function(e) { if (e.keyCode === 27) { prevent(e); } };
  12469. var isAutoPause = function() { return !!$autoPause.prop('checked'); };
  12470.  
  12471. var mylistList = [];
  12472. var
  12473. onMylistUpdate = function(status, result) {
  12474. if (status === "ok") {
  12475. Popup.show('マイリストに追加しました');
  12476. } else {
  12477. Popup.alert('マイリスト追加に失敗: ' + result.error.description);
  12478. }
  12479. }, addMylist = function(n, d) {
  12480. var num = parseInt(n, 36);
  12481. var description = d || '';
  12482. if (num === 0) {
  12483. Mylist.addDefListItem(WatchController.getWatchId(), onMylistUpdate, description);
  12484. } else
  12485. if (mylistList[num]) {
  12486. Mylist.addMylistItem (WatchController.getWatchId(), mylistList[num].id, onMylistUpdate, description);
  12487. }
  12488. }, showMylist = function(n) {
  12489. var num = parseInt(n, 36);
  12490. if (num === 0) {
  12491. WatchController.showDeflist();
  12492. } else
  12493. if (mylistList[num]) {
  12494. WatchController.showMylist(mylistList[num].id);
  12495. }
  12496. }, seekVideo = function(v) {
  12497. var vpos = WatchController.vpos(), currentVpos = vpos;
  12498. if (v.match(/^([\-+]\d+)/)) {
  12499. vpos += parseInt(RegExp.$1, 10) * 1000;
  12500. } else
  12501. if (v.match(/^\d+$/)) {
  12502. vpos = parseInt(v, 10) * 1000;
  12503. } else
  12504. if (v.match(/^(\d+):(\d+)$/)) {
  12505. vpos = parseInt(RegExp.$1, 10) * 60 * 1000 + parseInt(RegExp.$2, 10) * 1000;
  12506. }
  12507.  
  12508. vpos = Math.max(vpos, 0);
  12509.  
  12510. if (vpos != currentVpos) {
  12511. console.log('seek video', vpos / 1000);
  12512. WatchController.vpos(vpos);
  12513. }
  12514. };
  12515.  
  12516. var isPlaying = false;
  12517. $input
  12518. .on('focus', function(e) {
  12519. isPlaying = WatchController.isPlaying();
  12520. if (isAutoPause()) {
  12521. WatchController.pause();
  12522. }
  12523. $view.addClass('active');
  12524. }).on('blur', function(e) {
  12525. if (isAutoPause() && isPlaying) {
  12526. WatchController.play();
  12527. }
  12528. $view.removeClass('active');
  12529. }).on('keyup', function(e) {
  12530. if (e.keyCode === 27) { // ESC
  12531. prevent(e);
  12532. $input.blur();
  12533. }
  12534. }).on('keydown', preventEsc).on('keyup', preventEsc);
  12535.  
  12536. $autoPause.on('click', function() {
  12537. conf.setValue('autoPauseInvisibleInput', !!$autoPause.prop('checked'));
  12538. });
  12539.  
  12540. $form
  12541. .on('submit', function(e) {
  12542. //prevent(e);
  12543. var val = $.trim($input.val());
  12544.  
  12545. if (val.match(/^:m([0-9a-p])(.*)/)) {
  12546. addMylist(RegExp.$1, RegExp.$2);
  12547. } else
  12548. if (val.match(/^:o([0-9a-p])/)) {
  12549. showMylist(RegExp.$1);
  12550. } else
  12551. if (val.match(/^:s[  \s](.+)/)) {
  12552. WatchController.nicoSearch(RegExp.$1, 'keyword');
  12553. } else
  12554. if (val.match(/^:t[  \s](.+)/)) {
  12555. WatchController.nicoSearch(RegExp.$1, 'tag');
  12556. } else
  12557. if (val.match(/^:v[  \s]([0-9:+\-]+)$/)) {
  12558. seekVideo(RegExp.$1);
  12559. } else {
  12560. WatchController.postComment(val);
  12561. }
  12562.  
  12563. setTimeout(function() { $input.val(''); } , 100);
  12564. $input.blur();
  12565. });
  12566.  
  12567. Mylist.loadMylistList(function(list) {
  12568. mylistList = list.concat();
  12569. mylistList.unshift({description: '', id: '', name: 'とりあえずマイリスト'});
  12570. var isFx = Util.Browser.isFx();
  12571.  
  12572. var tmp = [];
  12573. for (var i = 0, len = mylistList.length; i < len; i++) {
  12574. var c = i.toString(36);
  12575. // それぞれのブラウザで補完しやすい形式に
  12576. if (isFx) { // Fx
  12577. tmp.push('<option value=":m' + c + '">:m'+c+'\t 「' + mylistList[i].name + '」に追加</option>');
  12578. tmp.push('<option value=":o' + c + '">:o'+c+'\t 「' + mylistList[i].name + '」を開く</option>');
  12579. } else { // Chrome
  12580. tmp.push('<option value=":m' + c + '">「' + mylistList[i].name + '」に追加</option>');
  12581. tmp.push('<option value=":o' + c + '">「' + mylistList[i].name + '」を開く</option>');
  12582. }
  12583. }
  12584. tmp.sort();
  12585. if (isFx) {
  12586. tmp.push('<option value=":s ">:s [キーワード検索]</option>');
  12587. tmp.push('<option value=":t ">:t [タグ検索]</option>');
  12588. tmp.push('<option value=":v ">:v [シーク(秒)]</option>');
  12589. } else {
  12590. tmp.push('<option value=":s ">キーワード検索</option>');
  12591. tmp.push('<option value=":t ">タグ検索</option>');
  12592. tmp.push('<option value=":v ">シーク(秒)</option>');
  12593. }
  12594. $dataList.html(tmp.join('\n'));
  12595. });
  12596.  
  12597.  
  12598. addStyle(css, 'invisibleInput');
  12599. $('body').append($view).append($dataList);
  12600. } //
  12601.  
  12602. function initHeatMap($, conf, w) {
  12603. if (!conf.enableHeatMap) return;
  12604. //if (!w.Worker) return;
  12605. //
  12606. // TODO: Web Workers
  12607. var canvasWidth = 100, canvasHeight = 12;
  12608. var comments = [], duration = 0, canvas = null, context = null;
  12609. var commentReady = false, videoReady = false, updated = false, palette = [];
  12610. var __css__ = Util.here(function(){/*
  12611. #nicoHeatMapContainer {
  12612. position: absolute; z-index: 200;
  12613. bottom: 0px; left: 0;
  12614. width: 672px;
  12615. background: #000; height: 6px;
  12616. overflow: hidden;
  12617. }
  12618. .setting_panel #nicoHeatMapContainer { display: none; opacity: 0; }
  12619. .size_normal #nicoHeatMapContainer {
  12620. width: 898px;
  12621. }
  12622. .oldTypeCommentInput #nicoHeatMapContainer {
  12623. bottom: 29px;
  12624. display: none;
  12625. }
  12626. #nicoHeatMap {
  12627. position: absolute; top: 0; left: 0;
  12628. transform-origin: 0 0 0;-webkit-transform-origin: 0 0 0;
  12629. transform: scaleX(6.72);-webkit-transform: scaleX(6.72);
  12630. }
  12631. {* パズルみたいになってきた *}
  12632. body.size_normal:not(.full_with_browser) #content:hover #nicoHeatMapContainer,
  12633. body.size_medium:not(.full_with_browser) #content:hover #nicoHeatMapContainer,
  12634. body.videoExplorer #content.w_adjusted:hover #nicoHeatMapContainer,
  12635. body:not(.full_with_browser) #nicoHeatMapContainer.displayAlways {
  12636. display: block;
  12637. }
  12638. #nicoHeatMapContainer.displayAlways {
  12639. cursor: pointer;
  12640. }
  12641. .size_normal #nicoHeatMap {
  12642. transform: scaleX(8.98); -webkit-transform: scaleX(8.98);
  12643. }
  12644. .setting_panel #nicoHeatMapContainer, .full_with_browser #nicoHeatMapContainer, .size_small #content:not(.w_adjusted) #nicoHeatMapContainer {
  12645. display: none;
  12646. }
  12647. body.full_with_browser.w_fullScreenMenu.trueBrowserFull #nicoHeatMapContainer {
  12648. bottom: 0; position: fixed;
  12649. }
  12650. .full_with_browser.w_fullScreenMenu #nicoHeatMapContainer {
  12651. display: block;
  12652. }
  12653. .full_with_browser.w_fullScreenMenu .oldTypeCommentInput #nicoHeatMapContainer {
  12654. bottom: 29px; height: 6px;
  12655. }
  12656. .full_with_browser.w_fullScreenMenu #nicoHeatMapContainer {
  12657. width: 100%;
  12658. }
  12659. */});
  12660. addStyle(__css__, 'NicoHeatMapCss');
  12661.  
  12662. watch.PlayerInitializer.playerAreaConnector.addEventListener('onCommentListInitialized', function() {
  12663. w.setTimeout(function() {
  12664. commentReady = true;
  12665. update();
  12666. }, 1000);
  12667. });
  12668. EventDispatcher.addEventListener('onVideoInitialized', function() {
  12669. videoReady = true;
  12670. update();
  12671. });
  12672. EventDispatcher.addEventListener('onVideoChangeStatusUpdated', function() {
  12673. commentReady = videoReady = updated = false;
  12674. clearCanvas();
  12675. });
  12676.  
  12677. var update = function() {
  12678. if (!commentReady || !videoReady || updated) return;
  12679. updated = true;
  12680. initCanvas();
  12681. getComments();
  12682. getDuration();
  12683. if (comments.length < 1 || duration < 1) {
  12684. return;
  12685. }
  12686. getHeatMap(function(map) {
  12687. var scale = duration >= canvasWidth ? 1 : (canvasWidth / duration);
  12688. var blockWidth = (canvasWidth / map.length) * scale;
  12689. for (i = map.length - 1; i >= 0; i--) {
  12690. context.fillStyle = palette[map[i]] || palette[0];
  12691. context.beginPath();
  12692. context.fillRect(i * scale, 0, blockWidth, canvasHeight);
  12693. }
  12694. });
  12695. };
  12696.  
  12697. var getComments = function() {
  12698. comments = [];
  12699.  
  12700. var list = watch.PlayerInitializer.commentPanelViewController.commentLists;
  12701. for (var i = 0; i < list.length; i++) {
  12702. if (list[i].listName === 'commentlist:main' && list[i].comments.length > 0) {
  12703. comments = list[i].comments;
  12704. break;
  12705. }
  12706. var ct = list[i].comments;
  12707. comments = (comments.length < ct.length) ? ct : comments;
  12708. }
  12709. list = null;
  12710. };
  12711. var getDuration = function() {
  12712. var exp = document.getElementById('external_nicoplayer');//$('#external_nicoplayer')[0];
  12713. duration = exp.ext_getTotalTime(); //
  12714. };
  12715. var initCanvas = function() {
  12716. if (!canvas) {
  12717. var $container = $('<div id="nicoHeatMapContainer" />');
  12718. $container.on('dblclick', function(e) {
  12719. e.preventDefault();
  12720. e.stopPropagation();
  12721. var $this = $(this).toggleClass('displayAlways');
  12722. conf.setValue('heatMapDisplayMode', $this.hasClass('displayAlways') ? 'always' : 'hover');
  12723. });
  12724. canvas = document.createElement('canvas');
  12725. canvas.id = 'nicoHeatMap';
  12726. canvas.width = canvasWidth;
  12727. canvas.height = canvasHeight;
  12728. $container.append(canvas);
  12729. $('#nicoplayerContainerInner').append($container);
  12730. context = canvas.getContext('2d');
  12731. if (conf.heatMapDisplayMode === 'always') {
  12732. $container.addClass('displayAlways');
  12733. }
  12734.  
  12735. initPalette();
  12736. }
  12737. clearCanvas();
  12738. };
  12739. var initPalette = function() {
  12740. for (var c = 0; c < 256; c++) {
  12741. var
  12742. r = Math.floor((c > 127) ? (c / 2 + 128) : 0),
  12743. g = Math.floor((c > 127) ? (255 - (c - 128) * 2) : (c * 2)),
  12744. b = Math.floor((c > 127) ? 0 : (255 - c * 2));
  12745. palette.push('rgb(' + r + ', ' + g + ', ' + b + ')');
  12746. }
  12747. };
  12748. var clearCanvas = function() {
  12749. if (!context) return;
  12750. context.fillStyle = palette[0];
  12751. context.beginPath();
  12752. context.fillRect(0, 0, canvasWidth, canvasHeight);
  12753. };
  12754.  
  12755. var getHeatMap = function(callback) {
  12756. var map = new Array(100), i = map.length; while(i > 0) map[--i] = 0;
  12757. var exp = $('#external_nicoplayer')[0];
  12758. var ratio = duration > map.length ? (map.length / duration) : 1;
  12759.  
  12760. for (i = comments.length - 1; i >= 0; i--) {
  12761. var pos = comments[i].vpos , mpos = Math.min(Math.floor(pos * ratio / 1000), map.length -1);
  12762. map[mpos]++;
  12763. }
  12764.  
  12765. var max = 0;
  12766. for (i = map.length - 4; i >= 0; i--) max = Math.max(map[i], max); // 末尾は固まってる事があるので参考にしない
  12767. if (max > 0) {
  12768. var rate = 255 / max;
  12769. for (i = map.length - 1; i >= 0; i--) {
  12770. map[i] = Math.min(255, Math.floor(map[i] * rate));
  12771. }
  12772. }
  12773. if (typeof callback === 'function') {
  12774. callback(map);
  12775. }
  12776. };
  12777. } // end of initHeatMap
  12778.  
  12779. /**
  12780. * 既存のポップアップの難点
  12781. *
  12782. * ・閉じる機能がなく、邪魔でも消えるまで待つしかない
  12783. * ・消えるまでの時間が毎回違う?
  12784. * ・クリックしたら消えるのかなと思ったらマイページに飛ばされる
  12785. * ・Chrome以外では動画プレイヤーの上に表示できない (半透明の部分が欠ける)
  12786. * ・↑によってプレイヤー上でフェードイン・アウトが出来ないため、まったく見えない状態から突然出現したようになる
  12787. * ・タイマー処理がバグっていて、一個目の表示中に2個目を連続表示すると2個目がすぐ消える
  12788. *
  12789. * … という所があんまりなので、パッチをあてて直す。
  12790. * ・Chrome以外は半透明をやめて画面外からのスライドにする
  12791. * ・CSS3アニメーションを使う(jQueryより軽い)
  12792. * ・クリックでマイページに飛ぶのをやめて、クリックで消えるようにする
  12793. * ・マウスオーバーしてる間は引っ込まない
  12794. * ・消えるまでの時間を4秒に固定
  12795. *
  12796. *
  12797. * このパッチでも直らない問題
  12798. * ・自分が動画投稿やレビューをしたという情報がなぜか自分にも通知される
  12799. *
  12800. */
  12801. function initPopupMarquee() {
  12802. if (!conf.replacePopupMarquee) { return; }
  12803. var
  12804. marquee = watch.PopupMarqueeInitializer.popupMarqueeViewController,
  12805. itemList = marquee.itemList,
  12806. $popup = $('#popupMarquee'),
  12807. $inner = $popup.find('.popupMarqueeContent'),
  12808. closeTimer = null,
  12809. popupDuration = 6000;
  12810.  
  12811. var
  12812. resetCloseTimer = function() {
  12813. if (closeTimer) {
  12814. clearTimeout(closeTimer);
  12815. closeTimer = null;
  12816. }
  12817. },
  12818. setCloseTimer = function() {
  12819. resetCloseTimer();
  12820. closeTimer = setTimeout(function() {
  12821. disappear();
  12822. closeTimer = null;
  12823. }, popupDuration);
  12824. },
  12825. onData = function(data) {
  12826. $inner.html(data);
  12827.  
  12828. $popup.removeClass('hide').removeClass('show');
  12829. setTimeout(function() {
  12830. $popup.removeClass('hide').addClass('show');
  12831. }, 100);
  12832. setCloseTimer();
  12833. },
  12834. disappear = function() {
  12835. $popup.removeClass('show');
  12836. resetCloseTimer();
  12837. setTimeout(function() {
  12838. if (!$popup.hasClass('show')) $popup.addClass('hide');
  12839.  
  12840. setTimeout(function() {
  12841. itemList.next();
  12842. }, Math.random() * 5000 + 5000);
  12843.  
  12844. }, 500);
  12845. },
  12846. __css__ = Util.here(function() {/*
  12847. #popupMarquee {
  12848. -webkit-filter: opacity( 0%); {* chrome以外はflashの上に半透明要素を置けない *}
  12849. background: #000 !important;
  12850. transition: -webkit-filter 0.25s ease-in, top 0.5s ease-in, bottom 0.5s ease-in; display: block;
  12851. }
  12852. #popupMarquee.show {
  12853. -webkit-filter: opacity(100%);
  12854. transition: -webkit-filter 1.00s ease-out, top 0.5s ease-out, bottom 0.5s ease-out; display: block;
  12855. }
  12856.  
  12857. #popupMarquee.hide {
  12858. opacity: 0; z-index: -1;
  12859. }
  12860.  
  12861. #popupMarquee.popupMarqueeTopRight:not(.show), #popupMarquee.popupMarqueeTopLeft:not(.show) { top: -600px; }
  12862. #popupMarquee.popupMarqueeBottomRight:not(.show), #popupMarquee.popupMarqueeBottomLeft:not(.show) { bottom: -600px; }
  12863. */});
  12864.  
  12865. addStyle(__css__, 'popupMarqueeFix');
  12866.  
  12867. itemList.eventTypeListenerMap.popup = []; //itemList.removeEventListener('popup', marquee.onData);
  12868. $popup
  12869. .css({opacity: ''})
  12870. .off('click').off('mouseover').off('mouseleave').off('mousemove')
  12871. .on('mouseover', resetCloseTimer)
  12872. .on('mouseout', setCloseTimer)
  12873. .on('click', disappear);
  12874.  
  12875. marquee.onData = $.proxy(onData, marquee);
  12876. marquee.disappear = $.proxy(disappear, marquee);
  12877. itemList.addEventListener('popup', $.proxy(onData, marquee));
  12878. } //
  12879.  
  12880.  
  12881. function initScroll($, conf, w) {
  12882. // 動画切り換え時にページの一番上までスクロールするようになったのを強引に阻止する
  12883. window.WatchApp.ns.model.state.WatchPageRouter.getInstance()._scroll = function() {};
  12884.  
  12885. var beforePlayerOffsetTop = 0, $playerAlignmentArea = $('#playerAlignmentArea');
  12886. var $window = $(window);
  12887. var beforeReset = function() {
  12888. beforePlayerOffsetTop = $playerAlignmentArea.offset().top;
  12889. };
  12890. var afterReset = function() {
  12891. var diff = $playerAlignmentArea.offset().top - beforePlayerOffsetTop;
  12892. var scrollTop = $window.scrollTop();
  12893. $window.scrollTop(scrollTop + diff);
  12894. };
  12895. var watchInfoModel = WatchApp.ns.model.WatchInfoModel.getInstance();
  12896. watchInfoModel.addEventListener('beforeReset', beforeReset);
  12897. watchInfoModel.addEventListener('afterReset', afterReset);
  12898.  
  12899.  
  12900. // 動画選択画面閉じた時にページの一番上までスクロールするようになったのを強引に阻止する
  12901. window.WatchApp.ns.util.WindowUtil.scroll_org = window.WatchApp.ns.util.WindowUtil.scroll;
  12902. var no_thanks = function() {
  12903. window.WatchApp.ns.util.WindowUtil.scroll = function() {};
  12904. };
  12905. var restore = function() {
  12906. window.WatchApp.ns.util.WindowUtil.scroll = window.WatchApp.ns.util.WindowUtil.scroll_org;
  12907. };
  12908.  
  12909. var vv = window.WatchApp.ns.init.BottomContentInitializer.videoExplorerModeViewController;
  12910. vv.onVideoExplorerClose_org = vv.onVideoExplorerClose;
  12911. vv.onVideoExplorerClose = $.proxy(function() {
  12912. no_thanks();
  12913. this.onVideoExplorerClose_org();
  12914. restore();
  12915. window.WatchApp.ns.util.WindowUtil.scrollFit('#playerContainerWrapper');
  12916. }, vv);
  12917.  
  12918. $ = conf = w = null;
  12919. } //
  12920.  
  12921. function initOther() {
  12922. if (conf.headerViewCounter) $('#siteHeaderInner').width($('#siteHeaderInner').width() + 200);
  12923.  
  12924. initAdditionalButtons();
  12925. initSquareThumbnail();
  12926.  
  12927. ConfigPanel.addChangeEventListener(function(name, newValue, oldValue) {
  12928. if (name === 'squareThumbnail') {
  12929. initSquareThumbnail();
  12930. } else
  12931. if (name === 'enableAutoTagContainerHeight') {
  12932. if (newValue) { watch.TagInitializer.tagViewController.tagViewPinStatus.changeStatus(true); }
  12933. } else
  12934. if (name === 'enableMylistDeleteButton') {
  12935. $('.videoExplorerBody').toggleClass('enableMylistDeleteButton', newValue);
  12936. } else
  12937. if (name === 'enableYukkuriPlayButton') {
  12938. newValue ? Yukkuri.show() : Yukkuri.hide();
  12939. } else
  12940. if (name === 'noNicoru') {
  12941. $('body').toggleClass('w_noNicoru', newValue);
  12942. } else
  12943. if (name === 'playerBgStyle') {
  12944. $('#content')
  12945. .toggleClass('w_flat_gray', newValue === 'gray')
  12946. .toggleClass('w_flat_white', newValue === 'white');
  12947. } else
  12948. if (name === 'compactVideoInfo') {
  12949. $('#content, #outline').toggleClass('w_compact', newValue);
  12950. } else
  12951. if (name === 'disableHorizontalScroll') {
  12952. $('body').toggleClass('w_disableHorizontalScroll', newValue);
  12953. } else
  12954. if (name === 'hideCommentPanelSocialButtons') {
  12955. $('#playerTabContainer').toggleClass('w_noSocial', newValue);
  12956. }
  12957. });
  12958.  
  12959. if (conf.enableMylistDeleteButton) $('.videoExplorerBody').addClass('enableMylistDeleteButton');
  12960.  
  12961. if (conf.noNicoru) $('body').addClass('w_noNicoru');
  12962.  
  12963. if (conf.playerBgStyle !== '') $('#content').addClass('w_flat_' + conf.playerBgStyle);
  12964.  
  12965. if (conf.compactVideoInfo) $('#content, #outline').addClass('w_compact');
  12966. onWatchInfoReset(watchInfoModel);
  12967.  
  12968. if (conf.enableYukkuriPlayButton) { Yukkuri.show(); }
  12969.  
  12970. if (conf.disableHorizontalScroll) $('body').addClass('w_disableHorizontalScroll');
  12971.  
  12972. if (conf.hideCommentPanelSocialButtons) $('#playerTabContainer').addClass('w_noSocial');
  12973.  
  12974. $('#videoHeaderMenu .searchText input').attr({'accesskey': '@'}).on('focus', function() {
  12975. WatchController.scrollTop(0, 400);
  12976. });
  12977.  
  12978. watch.PlayerInitializer.commentPanelViewController.commentPanelContentModel.addEventListener('change', function(name) {
  12979. if (name === 'log_comment') {
  12980. $('.logDateSelect .logTime input')[0].setAttribute('type', 'time');
  12981. }
  12982. });
  12983.  
  12984. if (!w.Ads) {
  12985. // hostsに 0.0.0.0 ads.nicovideo.jp してるとスクリプトエラーがうるさいのでダミーを入れる
  12986. w.Ads = {
  12987. Advertisement: function() { return {set: function() {}}; }
  12988. };
  12989. }
  12990.  
  12991. var overrideGenerateURL = function() {
  12992. var wpc = WatchApp.ns.init.WatchPageInitializer.watchPageController;
  12993. wpc.generateWatchURL_org = wpc.generateWatchURL;
  12994. wpc.generateWatchURL = $.proxy(function(s) {
  12995. var ret = this.generateWatchURL_org(s);
  12996. // これのせいで既読リンクの色が変わらないので除去
  12997. ret = ret.replace(/\/(videoExplorer|ichiba)/, '');
  12998. return ret;
  12999. }, wpc);
  13000. };
  13001. overrideGenerateURL();
  13002.  
  13003. // 再現性不明のエラーをとりあえず握りつぶしつつ自動再生を3/2までの仕様に戻す
  13004. var overrrideWindowUtil = function() {
  13005. var wu = WatchApp.ns.util.WindowUtil;
  13006. wu.checkInview_org = wu.checkInview;
  13007. wu.checkInview = function() { return true; };
  13008. //wu.checkInview = $.proxy(function(a) {
  13009. // if (a.length < 0) { return true; }
  13010. // try {
  13011. // this.checkInview_org(a);
  13012. // } catch (e) {
  13013. // console.log('%cerror in WindowUtil.checkInview', 'color: red; ', e, a);
  13014. // console.trace();
  13015. // }
  13016. //}, wu);
  13017. };
  13018. overrrideWindowUtil();
  13019.  
  13020. // ニコる数を取得するためにコメントパネルがめちゃくちゃ重くなってるのを改善
  13021. WatchApp.ns.model.player.NicoPlayerConnector.getCommentNicoruCount = function(name, num) {
  13022. if (conf.noNicoru) {
  13023. return 0;
  13024. }
  13025. return window.PlayerApp.ns.player.Nicoplayer.getInstance().getCommentNicoruCount(name, num);
  13026. };
  13027.  
  13028. var playerConfig = watch.PlayerInitializer.nicoPlayerConnector.playerConfig;
  13029. if (conf.autoPlayIfWindowActive === 'yes') {
  13030. playerConfig.set({autoPlay: false});
  13031. }
  13032. if (conf.autoPlay2ndVideo) {
  13033. playerConfig.set({autoPlay: false});
  13034. EventDispatcher.addEventListener('onFirstVideoInitialized', function() {
  13035. WatchController.pause();
  13036. WatchController.vpos(0);
  13037. setTimeout(function() {
  13038. playerConfig.set({autoPlay: true});
  13039. }, 3000);
  13040. });
  13041. $(window).on('beforeunload.watchItLater.autoPlay2ndVideo', function(e) {
  13042. playerConfig.set({autoPlay: false});
  13043. });
  13044. }
  13045.  
  13046. if (conf.debugMode) {
  13047. watch.PopupMarqueeInitializer.popupMarqueeViewController.itemList.addEventListener('popup', function(body) {
  13048. console.log('%c popup: ' + body, 'background: #0ff');
  13049. });
  13050. console.log(JSON.parse($('#watchAPIDataContainer').text()));
  13051.  
  13052. //WatchApp.ns.util.WindowUtil.shake = function() { console.log('%cshake', 'background: lightgreen;');};
  13053. //NicoPlayerConnector.getCommentNicoruCount_org = NicoPlayerConnector.getCommentNicoruCount;
  13054. }
  13055. }
  13056.  
  13057. // ?ref=等がついてるせいで未読既読のリンクの色が変わらなくなる問題の対策
  13058. // ShinjukuWatchと違いこっちはプレイリスト消えないモードがあるので、マイリスト等からの遷移でも遠慮無く全部消す
  13059. if (location.href.indexOf('?') >= 0) {
  13060. window.history.replaceState('', '', location.href.split('?')[0]);
  13061. }
  13062.  
  13063.  
  13064. function initTest(test) {
  13065. var console = window.console;
  13066. var expect = test.expect;
  13067. WatchApp.mixin(WatchItLater.test.spec, {
  13068. testChannelVideo: function(def) {
  13069. ChannelVideoList.load(function(result) {
  13070. console.log('ChannelVideoList.load', result);
  13071. expect(result.name).toEqual('ニコニコアプリちゃんねるの動画', 'チャンネル名');
  13072. expect(result.list.length >= 30).toBeTrue('2013/08/28時点で33件');
  13073. def.resolve();
  13074. }, {id: '55', ownerName: 'ニコニコアプリちゃんねる'});
  13075. },
  13076. testNewNicoSearch: function(def) {
  13077. var size = 15;
  13078. var search = new NewNicoSearch({});
  13079. search.load({query: 'vocaloid', size: size}, function(err, result) {
  13080. console.log('testNewNicoSearch.load', err, result);
  13081. expect(err).toBeNull('err === null');
  13082. expect(result[0].dqnid) .toBeTruthy('先頭にdqnidが含まれる(なんの略?)');
  13083. expect(typeof result[0].values[0].total).toEqual('number', 'ヒット件数');
  13084. expect(result[0].values[0].service) .toEqual('video', '検索の種類');
  13085.  
  13086. expect(result[1].type).toEqual('stats', 'type === stats'); // データの開始?
  13087.  
  13088. expect(result[2].type ).toEqual ('hits', 'type === hits');
  13089. expect(result[2].values ).toBeTruthy('ヒットした内容');
  13090. expect(result[2].values.length ).toEqual (size, 'sizeで指定した件数が返る');
  13091. expect(result[2].values[0].cmsid).toBeTruthy('ヒットした内容にデータが含まれる');
  13092.  
  13093. expect(result[3].type).toEqual('hits', 'type === stats'); // データの終了?
  13094. def.resolve();
  13095. });
  13096. },
  13097. testNewNicoSearchWrapperQuery: function(def) {
  13098. var wrapper = new NewNicoSearchWrapper({search: {}});
  13099. var params = {
  13100. searchWord: 'VOCALOID',
  13101. searchType: 'tag',
  13102. u: '1m',
  13103. l: 'short',
  13104. sort: 'l',
  13105. order: 'a',
  13106. page: 3
  13107. };
  13108. var query = wrapper._buildSearchQuery(params);
  13109.  
  13110. console.log(params, query);
  13111. expect(query.query).toEqual(params.searchWord, '検索ワードのセット');
  13112. expect(query.from).toEqual(params.page * 32 - 32, 'ページ番号 -> fromの変換');
  13113. expect(query.sort_by).toEqual('length_seconds', 'l -> length_seconds');
  13114. expect(query.order).toEqual('asc', 'a -> asc');
  13115.  
  13116. // TODO:
  13117. expect(JSON.stringify(query.search).indexOf('["tags"]') >= 0).toBeTrue('タグ検索');
  13118. var filters = JSON.stringify(query.filters);
  13119. //console.log(filters);
  13120. expect(query.filters.length >= 2).toBeTrue('filters.lengthが2以上');
  13121. expect(filters.indexOf('"field":"start_time"') >= 0).toBeTrue('filtersにstart_timeが含まれる');
  13122. expect(filters.indexOf('"field":"length_seconds"') >= 0).toBeTrue('filtersにlength_secondsが含まれる');
  13123. def.resolve();
  13124. },
  13125. testNewNicoSearchWrapper: function(def) {
  13126. console.log('testNewNicoSearchWrapper');
  13127. var search = new NewNicoSearch({});
  13128. var wrapper = new NewNicoSearchWrapper({search: search});
  13129. wrapper.load({searchWord: 'ぬこぬこ動画', size: 100}, function(err, result) {
  13130. console.log('testNewNicoSearchWrapper.load', err, result);
  13131. expect(err).toBeNull('err === null');
  13132. expect(typeof result.count).toEqual('number', '件数がnumber');
  13133. expect(result.count > 0).toBeTrue('件数が入っている');
  13134. expect(result.list.length).toBeTruthy('データが入っている');
  13135. expect(result.list.length).toEqual(100, 'sizeで指定した件数が入っている');
  13136. expect(result.list[0].type).toEqual(0, 'type === 0');
  13137. expect(/^\d+:\d+/.test(result.list[0].length)).toBeTrue('動画長がmm:dd形式で入ってる');
  13138. def.resolve();
  13139.  
  13140. });
  13141. },
  13142. testNicoSearchRelatedTag: function(def) {
  13143. var related = new NicoSearchRelatedTag({});
  13144. related.load('voiceroid', function(err, result) {
  13145. console.log('testNicoSearchRelatedTag.load', err, result);
  13146. console.log(expect(err));
  13147. expect(err).toBeNull('err === null');
  13148. expect(result.type).toEqual('tags', 'type === "tags"');
  13149. expect(result.values).toBeTruthy('データが入っている');
  13150. expect(typeof result.values[0]._rowid).toEqual('number', 'データに_rowidが入っている');
  13151. expect(typeof result.values[0].tag) .toEqual('string', 'データにtagが入っている');
  13152. def.resolve();
  13153. });
  13154. },
  13155. testSearchSuggest: function(def) {
  13156. var suggest = new NicoSearchSuggest({});
  13157. suggest.load('MMD', function(err, result) {
  13158. console.log('testSearchSuggest.load', err, result);
  13159. console.log(expect(err));
  13160. expect(err).toBeNull('err === null');
  13161. expect(result.candidates).toBeTruthy('suggestの中身がある');
  13162. expect(result.candidates.length).toBeTruthy('suggestのlengthがある');
  13163. def.resolve();
  13164. });
  13165. },
  13166. testUpdateMylistComment: function(def) {
  13167. // 一個以上マイリストがあって先頭のマイリストになにか登録されている必要がある
  13168. var Mylist = WatchItLater.mylist;
  13169. var randomMessage = 'RND: ' + Math.random();
  13170.  
  13171. var d = new $.Deferred();
  13172. d.promise()
  13173. .then(function() {
  13174. var d = new $.Deferred();
  13175. Mylist.loadMylistList(function(mylistList) {
  13176. expect(mylistList.length > 0).toBeTruthy('マイリスト一覧が1件以上');
  13177. console.log('先頭のマイリスト', mylistList[0].id, mylistList[0].name);
  13178. var groupId = mylistList[0].id;
  13179. if (mylistList.length <= 0) {
  13180. d.reject();
  13181. return;
  13182. }
  13183. d.resolve(groupId);
  13184. });
  13185. return d.promise();
  13186. })
  13187. .then(function(groupId) {
  13188. var d = new $.Deferred();
  13189. Mylist.reloadMylist(groupId, function(mylist) {
  13190. expect(mylist.length > 0).toBeTruthy('マイリストアイテムが一個以上');
  13191. var item = mylist[0];
  13192. var watchId = item.item_data.watch_id;
  13193. console.log('マイリスト先頭のアイテム', watchId, item.item_data.title);
  13194. d.resolve(watchId, groupId);
  13195. });
  13196. return d.promise();
  13197. })
  13198. .then(function(watchId, groupId) {
  13199. var d = new $.Deferred();
  13200. Mylist.updateMylistItem(watchId, groupId, function(result) {
  13201. expect(result).toEqual('ok', 'updateMylistItem() result=ok');
  13202. d.resolve(watchId, groupId);
  13203. }, randomMessage);
  13204. return d.promise();
  13205. })
  13206. .then(Util.Deferred.wait(500))
  13207. .then(function(watchId, groupId) {
  13208. var d = new $.Deferred();
  13209. Mylist.reloadMylist(groupId, function(newlist) {
  13210. console.log('reloadMylist', groupId, newlist);
  13211. expect(newlist[0].description)
  13212. .toEqual(randomMessage, 'マイリストコメントが更新できている => ' + newlist[0].description);
  13213. d.resolve();
  13214. });
  13215. return d.promise();
  13216. }).then(function() {
  13217. def.resolve();
  13218. });
  13219. d.resolve();
  13220. },
  13221. testVideoRanking: function(def) {
  13222. VideoRanking.load(null, {id: -4000})
  13223. .then(function(result) {
  13224. console.log('VideoRanking.load result:', result);
  13225. expect(result.name).toEqual('カテゴリ合算', 'ダミーマイリストの名前が一致');
  13226. expect(result.list.length).toEqual(300, 'カテゴリ合算ランキングは300件');
  13227. expect(result.list[ 0].title.indexOf('第001位')).toEqual(0,'ランキング1位のタイトル');
  13228. expect(result.list[299].title.indexOf('第300位')).toEqual(0,'ランキング300位のタイトル');
  13229. def.resolve();
  13230. },
  13231. function() {
  13232. def.reject();
  13233. });
  13234. },
  13235. testNicorepoVideo: function(def) {
  13236. NicorepoVideo.loadAll(null, null)
  13237. .then(function(result) {
  13238. console.log('NicorepoVideo.loadAll result:', result);
  13239. expect(result.name).toEqual('【ニコレポ】すべての動画', 'ダミーマイリストの名前が一致');
  13240. expect(result.list).toBeTruthy('ニコレポがある');
  13241. def.resolve();
  13242. },
  13243. function() {
  13244. def.reject();
  13245. });
  13246. },
  13247. testVideoInfoLoader: function(def) {
  13248. var loader = new VideoInfoLoader({});
  13249. $.when(
  13250. loader.load('sm9').then(function(result) {
  13251. expect(result.id).toEqual('sm9', '存在する動画ID');
  13252. expect(result.length).toEqual('5:19', 'length');
  13253. return this.done();
  13254. }, function(err) {
  13255. return this.fail();
  13256. }),
  13257.  
  13258. loader.load('sm1').then(function(result) {
  13259. return new $.Deferred().reject().promise();
  13260. }, function(resp) {
  13261. expect(resp.status).toEqual('fail', '存在しない動画ID');
  13262. return new $.Deferred().resolve().promise();
  13263. })
  13264.  
  13265. ).then(function() { def.resolve(); }, function() { def.reject(); });
  13266. },
  13267. testRelatedVideo: function(def) {
  13268. var loader = new RelatedVideo({});
  13269. loader.load('sm9').then(function(result) {
  13270. console.log('RelatedVideo', result);
  13271. expect(result.list).toBeTruthy('関連動画がある');
  13272. expect(result.list[0].title).toBeTruthy('タイトルがある');
  13273. expect(result.list[0].title.length >= 0).toBeTrue('タイトル長がある');
  13274. expect(typeof result.list[0].type === 'number').toBeTrue('type属性がある');
  13275. def.resolve();
  13276. });
  13277. },
  13278. testVideoArray: function(def) {
  13279. window.WatchItLater.loader.videoArrayAPILoader.load(['sm9', 'sm13']).then(function(result) {
  13280. console.log('VideoArrayAPILoader', result);
  13281. expect(result['sm9']).toBeTruthy('動画情報');
  13282. expect(result['sm13'].title).toBeTruthy('タイトルがある');
  13283. expect(result['sm13'].title.length >= 0).toBeTrue('タイトル長がある');
  13284. window.WatchItLater.loader.videoArrayAPILoader.load(['sm13', '1394785382']).then(function(result) {
  13285. console.log('VideoArrayAPILoader', result);
  13286. expect(result['1394785382']).toBeTruthy('スレッドIDでも引ける');
  13287. expect(result['1394785382'].title).toEqual('鬼灯の冷徹 第10話「十王の晩餐」「ダイエットは地獄みたいなもの」', '動画タイトル一致');
  13288. def.resolve();
  13289. });
  13290. });
  13291. }
  13292.  
  13293.  
  13294. }); // end WatchApp.mixin
  13295.  
  13296. } // end initTest
  13297.  
  13298. window.console.time('init WatchItLater');
  13299. // window.console.profile('init WatchItLater');
  13300. LocationHashParser.initialize();
  13301. initNews();
  13302. initShortcutKey();
  13303. initMouse();
  13304. initTouch();
  13305. initEvents();
  13306.  
  13307. initSearchContent($, conf, w);
  13308. initUserVideoContent($, conf, w);
  13309. initMylistContent($, conf, w);
  13310. initUploadedVideoContent($, conf, w);
  13311. initDeflistContent($, conf, w);
  13312. initVideoExplorer($, conf, w);
  13313.  
  13314. initRightPanel($, conf, w);
  13315. initLeftPanel($, conf, w);
  13316. initVideoReview($, conf, w);
  13317.  
  13318. initHidariue();
  13319. initVideoCounter();
  13320. initScreenMode();
  13321. initPlaylist($, conf, w);
  13322.  
  13323. initPageBottom($, conf, w);
  13324. initPageHeader($, conf, w);
  13325. initVideoTagContainer($, conf, w);
  13326.  
  13327. initNicoS($, conf, w);
  13328. initInvisibleCommentInput($, conf, w);
  13329. initOtherCss();
  13330. initCustomPlayerSize($, conf, w);
  13331.  
  13332. initStageVideo($, conf, w);
  13333. initHeatMap($, conf, w);
  13334. initPopupMarquee();
  13335. initMylistPanel($, conf, w);
  13336. initScroll($, conf, w);
  13337. initOther();
  13338. // window.console.profileEnd('init WatchItLater');
  13339. window.console.timeEnd('init WatchItLater');
  13340.  
  13341. onWindowResizeEnd();
  13342.  
  13343. if (conf.debugMode) {
  13344. initTest(WatchItLater.test);
  13345. }
  13346. };
  13347.  
  13348. if (window.PlayerApp) {
  13349. (function() {
  13350. var watchInfoModel = WatchApp.ns.model.WatchInfoModel.getInstance();
  13351. if (watchInfoModel.initialized) {
  13352. window.WatchItLater.WatchController =
  13353. window.WatchController =
  13354. _watchController(window);
  13355. ZeroFunc(window);
  13356. } else {
  13357. var onReset = function() {
  13358. watchInfoModel.removeEventListener('reset', onReset);
  13359. window.setTimeout(function() {
  13360. window.WatchItLater.WatchController =
  13361. window.WatchController =
  13362. _watchController(window);
  13363.  
  13364. ZeroFunc(window);
  13365. }, 0);
  13366. };
  13367. watchInfoModel.addEventListener('reset', onReset);
  13368. }
  13369. })();
  13370. } else
  13371. if (location.host === 'www.nicovideo.jp' && location.pathname ==='/stamp') {
  13372. niconicodoRedirect();
  13373. }
  13374.  
  13375.  
  13376. /**
  13377. * 原宿プレイヤーでのあれこれ
  13378. *
  13379. * マイリストパネルだけ追加
  13380. *
  13381. */
  13382. (function() {
  13383. if (!w.Video) return;
  13384. if (!location.href.match(/\/watch\/(sm\d+|nm\d+|so\d+|\d+)/)) return;
  13385. var watchId = undefined, videoId = undefined;
  13386. if (w.Video === null) {
  13387. watchId = RegExp.$1;
  13388. w.Video = {id: watchId};
  13389. } else {
  13390. Video = w.Video;
  13391. watchId = Video.v;
  13392. videoId = Video.id;
  13393. }
  13394. var watchId = RegExp.$1;
  13395. var iframe = Mylist.getPanel('');
  13396. iframe.id = "mylist_add_frame";
  13397. iframe.setAttribute('style', 'position: fixed; right: 0; bottom: 0;');
  13398.  
  13399. document.body.appendChild(iframe);
  13400. iframe.watchId(watchId, videoId);
  13401. })();
  13402.  
  13403.  
  13404. /**
  13405. * キーボードイベント他
  13406. *
  13407. */
  13408. (function() {
  13409. w.document.body.addEventListener('keydown', function(e) {
  13410. if (e.keyCode === 27 || e.keyCode === 88) { // ESC or x
  13411. AnchorHoverPopup.hidePopup();
  13412. Popup.hide();
  13413. }
  13414. }, false);
  13415. w.document.body.addEventListener('click', function(e) {
  13416. var tagName = e.target.tagName, className = e.target.className;
  13417. //console.log(tagName, className);
  13418. if (tagName !== 'BUTTON' && tagName !== 'SELECT' && tagName !== 'OPTION' && className !== 'popupTagItem' && className.indexOf('mylistPopupPanel') < 0) {
  13419. AnchorHoverPopup.hidePopup();
  13420. }
  13421.  
  13422. }, false);
  13423. var touchInitialized = false;
  13424. TouchEventDispatcher.onflick(function(e) {
  13425. if (e.direction === 'right') {
  13426. if (!touchInitialized) {
  13427. document.getElementById('videoTagPopupContainer').className += ' w_touch';
  13428. touchInitialized = true;
  13429. }
  13430. }
  13431. }, false);
  13432. // w.document.body.addEventListener('dblclick', function(e) {var tagName = e.target.tagName, className = e.target.className;console.log(tagName, className);});
  13433.  
  13434. })(w);
  13435.  
  13436. //===================================================
  13437. //===================================================
  13438. //===================================================
  13439.  
  13440. }); // end of monkey();
  13441.  
  13442. /**
  13443. * スマートフォン用APIを利用して動画情報を取得する
  13444. */
  13445. var spapi = (function() {
  13446. if (window.name.indexOf('watchItLaterAPILoader') < 0 ) { return; }
  13447. var resp = document.getElementsByTagName('nicovideo_video_response');
  13448. var session = location.hash.length > 1 ? location.hash.substr(1) : location.search;
  13449. var origin = 'http://' + location.host.replace(/^.*?\./, 'www.');
  13450. var xml = '';
  13451. if (resp.length > 0) {
  13452. xml = resp[0].outerHTML;
  13453. }
  13454.  
  13455. try {
  13456. parent.postMessage(JSON.stringify({
  13457. id: 'WatchItLater',
  13458. type: 'VideoArrayAPILoader',
  13459. body: {
  13460. session: session,
  13461. xml: xml
  13462. }
  13463. }),
  13464. origin);
  13465. } catch (e) {
  13466. console.log('err', e);
  13467. }
  13468. });
  13469.  
  13470. try {
  13471. if (location.host === 'flapi.nicovideo.jp') {
  13472. return;
  13473. } else
  13474. if (location.host === 'i.nicovideo.jp') {
  13475. spapi();
  13476. } else
  13477. if (location.host.indexOf('smile-') >= 0) {
  13478. return;
  13479. } else
  13480. if (location.host.indexOf('localhost.') === 0 || location.host.indexOf('www.') === 0 || !this.GM_getValue || this.GM_getValue.toString().indexOf("not supported")>-1) {
  13481. isNativeGM = false;
  13482. var inject = document.createElement("script");
  13483. inject.id = "monkey";
  13484. inject.setAttribute("type", "text/javascript");
  13485. inject.setAttribute("charset", "UTF-8");
  13486.  
  13487. inject.appendChild(document.createTextNode("(" + monkey + ")(false)"));
  13488. // inject.appendChild(document.createTextNode("try {(" + monkey + ")(false) } catch(e) { console.log(e); }"));
  13489.  
  13490. if (document.body) {
  13491. document.body.appendChild(inject);
  13492. } else {
  13493. document.documentElement.appendChild(inject);
  13494. }
  13495. } else {
  13496. // やや古いFirefoxはここらしい
  13497. monkey(true);
  13498. }
  13499.  
  13500. } catch(e) {
  13501. // 最近のFirefoxはここに飛んでくる
  13502. monkey(true);
  13503. }
  13504. })();
  13505.  
  13506.