- // ==UserScript==
- // @name Nicorenizer
- // @namespace https://github.com/segabito/
- // @description 動画クリックで一時停止/再開 ダブルクリックでフルスクリーン切換え
- // @include http://www.nicovideo.jp/watch/*
- // @version 0.2.1
- // @grant none
- // ==/UserScript==
-
- // TODO:
- // ダブルクリック時にフルスクリーンにする設定を無効・ブラウザ全体・モニター全体から選べるようにする
-
- // ver 0.1.2
- // - Watch It Laterと併用時、動画選択画面でのダブルクリックでフルスクリーンにならないのを修正
-
- // ver 0.1.0 最初のバージョン
-
- (function() {
- var monkey = (function(){
- 'use strict';
-
-
- if (!window.WatchApp || !window.WatchJsApi) {
- return;
- }
-
- var FullScreen = {
- now: function() {
- if (document.fullScreenElement || document.mozFullScreen || document.webkitIsFullScreen) {
- return true;
- }
- return false;
- },
- request: function(target) {
- var elm = typeof target === 'string' ? document.getElementById(target) : target;
- if (!elm) { return; }
- if (elm.requestFullScreen) {
- elm.requestFullScreen();
- } else if (elm.webkitRequestFullScreen) {
- elm.webkitRequestFullScreen();
- } else if (elm.mozRequestFullScreen) {
- elm.mozRequestFullScreen();
- }
- },
- cancel: function() {
- if (!this.now()) { return; }
-
- if (document.cancelFullScreen) {
- document.cancelFullScreen();
- } else if (document.webkitCancelFullScreen) {
- document.webkitCancelFullScreen();
- } else if (document.mozCancelFullScreen) {
- document.mozCancelFullScreen();
- }
- }
- };
-
-
- window.Nicorenizer = {};
-
- window.WatchApp.mixin(window.Nicorenizer, {
- initialize: function() {
- this._watchInfoModel = require('watchapp/model/WatchInfoModel').getInstance();//window.WatchApp.ns.init.CommonModelInitializer.watchInfoModel;
- var PlayerInitializer = require('watchapp/init/PlayerInitializer');
- this._playerAreaConnector = PlayerInitializer.playerAreaConnector;
- this._nicoPlayerConnector = PlayerInitializer.nicoPlayerConnector;
- this._playerScreenMode = PlayerInitializer.playerScreenMode;
- this._playlist = require('watchapp/init/PlaylistInitializer').playlist;
- this._videoExplorer = require('watchapp/init/VideoExplorerInitializer').videoExplorer;
- this._watchInfoModel = require('watchapp/model/WatchInfoModel').getInstance();
-
- this._vastStatus = this._nicoPlayerConnector.vastStatus;
-
- this.initializeUserConfig();
- this.initializeSettingPanel();
- this.initializeShield();
- this.initializePlayerEvent();
-
- this.initializePlayerApp();
-
- this.initializeCss();
- },
- addStyle: function(styles, id) {
- var elm = document.createElement('style');
- elm.type = 'text/css';
- if (id) { elm.id = id; }
-
- var text = styles.toString();
- text = document.createTextNode(text);
- elm.appendChild(text);
- var head = document.getElementsByTagName('head');
- head = head[0];
- head.appendChild(elm);
- return elm;
- },
- initializeCss: function() {
- var __css__ = (function() {/*
-
- #nicorenaiShield {
- display: none;
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 85px;
- z-index: 9950;
- cursor: none;
- }
-
- #nicorenaiShield.disable, #nicorenaiShield.vast, #nicorenaiShield.disableTemp {
- display: none !important;
- }
-
- #nicorenaiShield.debug {
- background: red; opacity: 0.5;
- }
-
- #nicorenaiShield.initialized {
- display: block;
- }
-
- #nicorenaiShield.showCursor {
- cursor: crosshair; {* 現在有効である事をわかりやすくするためにcrosshair。本当はオリジナルのカーソルを用意したいところ *}
- }
-
- body.setting_panel #nicorenaiShield, body.videoErrorOccurred #nicorenaiShield,
- body.setting_panel #nicorenaiShield, body.videoErrorOccurred #nicorenaiShieldToggle {
- display: none;
- }
-
- body.videoExplorer #content:not(.w_adjusted) #nicorenaiShield {
- {* 動画選択画面ではクリックで解除させるために邪魔なので消す *}
- {* ただしWatch It Lterの検索モードでは有効にする *}
- display: none;
- }
-
- #nicorenaiShieldToggle {
- position: absolute;
- z-index: 9951;
- top: 10px;
- left: 10px;
- border-color: blue;
- opacity: 0;
- cursor: pointer;
- transition: opacity 0.5s ease;
- padding: 4px 8px;
- }
-
- #nicorenaiShieldToggle.disable, #nicorenaiShieldToggle.disableTemp {
- border-color: black;
- }
-
- #nicorenaiShieldToggle.debug {
- opacity: 1 !important;
- }
-
- #nicorenaiShieldToggle.initialized:hover, #nicorenaiShieldToggle.show, #nicorenaiShieldToggle.disableTemp {
- opacity: 1;
- transition: none;
- }
-
- #nicorenaiShieldToggle:after {
- content: ':ON';
- }
- #nicorenaiShieldToggle.disable:after, #nicorenaiShieldToggle.disableTemp:after {
- content: ':OFF';
- }
-
- #nicorenizerSettingPanel {
- position: fixed;
- bottom: 2000px; right: 8px;
- z-index: -1;
- width: 500px;
- background: #f0f0f0; border: 1px solid black;
- padding: 8px;
- transition: bottom 0.4s ease-out;
- }
- #nicorenizerSettingPanel.open {
- display: block;
- bottom: 8px;
- box-shadow: 0 0 8px black;
- z-index: 10000;
- }
- #nicorenizerSettingPanel .close {
- position: absolute;
- cursor: pointer;
- right: 8px; top: 8px;
- }
- #nicorenizerSettingPanel .panelInner {
- background: #fff;
- border: 1px inset;
- padding: 8px;
- min-height: 300px;
- overflow-y: scroll;
- max-height: 500px;
- }
- #nicorenizerSettingPanel .panelInner .item {
- border-bottom: 1px dotted #888;
- margin-bottom: 8px;
- padding-bottom: 8px;
- }
- #nicorenizerSettingPanel .panelInner .item:hover {
- background: #eef;
- }
- #nicorenizerSettingPanel .windowTitle {
- font-size: 150%;
- }
- #nicorenizerSettingPanel .itemTitle {
- }
- #nicorenizerSettingPanel label {
-
- }
- #nicorenizerSettingPanel small {
- color: #666;
- }
- #nicorenizerSettingPanel .expert {
- margin: 32px 0 16px;
- font-size: 150%;
- background: #ccc;
- }
-
- {* Chromeの不具合対策 *}
- body.full_with_browser {
- width: 100%;
- }
-
- #nicorenaiShield .previous,
- #nicorenaiShield .next {
- position: absolute;
- display: none;
- top: 0;
- width: 48px;
- height: 100%;
- opacity: 0.8;
- }
-
- #nicorenaiShield.hasPrevious .previous,
- #nicorenaiShield.hasNext .next {
- display: block;
- }
-
-
- #nicorenaiShield .previous .button,
- #nicorenaiShield .next .button{
- position: absolute;
- top: 0;
- bottom: 0;
- left: 0;
- width: 40px;
- height: 64px;
- margin: auto;
- box-sizing: border-box;
- font-size: 28px;
- cursor: pointer;
- background-color: #333;
- color: #fff;
- border: 2px solid #ccc;
- opacity: 0;
- transition: opacity 0.4s linear;
- user-select: none;
- }
-
- #nicorenaiShield .previous:hover .button,
- #nicorenaiShield .next:hover .button {
- opacity: 1;
- }
-
- #nicorenaiShield .previous .button:active,
- #nicorenaiShield .next .button:active {
- background: #888;
- }
-
-
- #nicorenaiShield .previous {
- left: 0;
- }
- #nicorenaiShield .next {
- right: 0;
- }
-
- #nicorenaiShield .previous .button {
- }
- #nicorenaiShield .next .button {
- right: 0;
- left: auto;
- }
-
-
- body.full_with_browser #nicoplayerContainer #nicoplayerContainerInner {
- margin-top: 0 !important;
- margin-bottom: -76px !important;
- }
- body.full_with_browser.showControl #nicoplayerContainer #nicoplayerContainerInner {
- margin-top: 0 !important;
- margin-bottom: 0 !important;
- }
-
-
-
- */}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1].replace(/\{\*/g, '/*').replace(/\*\}/g, '*/');
-
- this.addStyle(__css__, 'NicorenizerCss');
- },
- initializeUserConfig: function() {
- var prefix = 'Nicorenizer_';
- var conf = {
- fullScreenType: 'browser', // none, browser, monitor
- togglePlay: true
- };
- this.config = {
- get: function(key) {
- try {
- if (window.localStorage.hasOwnProperty(prefix + key)) {
- return JSON.parse(window.localStorage.getItem(prefix + key));
- }
- return conf[key];
- } catch (e) {
- return conf[key];
- }
- },
- set: function(key, value) {
- window.localStorage.setItem(prefix + key, JSON.stringify(value));
- }
- };
- },
- initializeSettingPanel: function() {
- var $menu = $('<li class="nicorenizerSettingMenu"><a href="javascript:;" title="Nicorenizerの設定変更">Nicorenizer設定</a></li>');
- var $panel = $('<div id="nicorenizerSettingPanel" />');//.addClass('open');
- var $button = $('<button class="toggleSetting playerBottomButton">設定</botton>');
-
- $button.on('click', function(e) {
- e.stopPropagation(); e.preventDefault();
- $panel.toggleClass('open');
- });
-
- var config = this.config;
- $menu.find('a').on('click', function() { $panel.toggleClass('open'); });
-
- var __tpl__ = (function() {/*
- <div class="panelHeader">
- <h1 class="windowTitle">Nicorenizerの設定</h1>
- <button class="close" title="閉じる">×</button>
- </div>
- <div class="panelInner">
- <div class="item" data-setting-name="togglePlay" data-menu-type="radio">
- <h3 class="itemTitle">画面クリックで一時停止/再生</h3>
- <label><input type="radio" value="true" >する</label>
- <label><input type="radio" value="false">しない</label>
- </div>
-
- <div class="item" data-setting-name="fullScreenType" data-menu-type="radio">
- <h3 class="itemTitle">ダブルクリック時のフルスクリーン</h3>
- <label><input type="radio" value=""browser"" >ブラウザ全体</label>
- <label><input type="radio" value=""monitor"">モニター全体</label>
- <label><input type="radio" value=""none"">切り換えない</label>
- </div>
- </div>
- */}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1].replace(/\{\*/g, '/*').replace(/\*\}/g, '*/');
- $panel.html(__tpl__);
- $panel.find('.item').on('click', function(/* e */) {
- var $this = $(this);
- var settingName = $this.attr('data-setting-name');
- var value = JSON.parse($this.find('input:checked').val());
- console.log('seting-name', settingName, 'value', value);
- config.set(settingName, value);
- }).each(function(/* e */) {
- var $this = $(this);
- var settingName = $this.attr('data-setting-name');
- var value = config.get(settingName);
- $this.addClass(settingName);
- $this.find('input').attr('name', settingName).val([JSON.stringify(value)]);
- });
- $panel.find('.close').click(function() {
- $panel.removeClass('open');
- });
-
-
- $('#playerAlignmentArea').append($button);
- $('#siteHeaderRightMenuFix').after($menu);
- $('body').append($panel);
-
-
- },
- _initializeDom: function() {
- var $prev = $('<div class="previous"><button class="button" data-click-command="previous" title="前の動画"><</button></div>');
- var $next = $('<div class="next"><button class="button" data-click-command="next" title="次の動画">></button></div>');
- this._$shield = $('<div id="nicorenaiShield" class="hasPrevious hasNext"></div>');
- this._$toggle = $('<button id="nicorenaiShieldToggle" title="クリックで無効化ON/OFF">シールド</botton>');
- this._$shield.append($prev).append($next);
-
- $('#external_nicoplayer').after(this._$shield).after(this._$toggle);
- return this._$shield;
- },
- initializeShield: function() {
- var videoExplorer = this._videoExplorer;
- var nicoPlayer = $("#external_nicoplayer")[0];
- var toggleMonitorFull = $.proxy(this.toggleMonitorFull, this);
- var toggleDisable = $.proxy(this.toggleDisable, this);
- var execCommand = $.proxy(this.execCommand, this);
- var config = this.config;
-
- this._initializeDom();
- var $shield = this._$shield;
- var $toggle = this._$toggle;
-
- var click = function(e) {
- // TODO: YouTubeみたいに中央に停止/再生マーク出す?
- if (e.button !== 0) { return; }
- if (!config.get('togglePlay')) { return; }
- var $target = $(e.target);
- var cmd = $target.attr('data-click-command');
- if (cmd) {
- e.preventDefault();
- e.stopPropagation();
- execCommand(cmd);
- return;
- }
- var status = nicoPlayer.ext_getStatus();
- if (status === 'playing') {
- execCommand('stop');
- } else {
- execCommand('play');
- }
- };
-
- var dblclick = function(e) {
- if (e.button !== 0) return;
- e.preventDefault(); e.stopPropagation();
- var fullScreenType = config.get('fullScreenType');
- if (fullScreenType === 'none') { return; }
-
- if (videoExplorer.isOpen()) {
- videoExplorer.changeState(false);
- if (fullScreenType === 'monitor') {
- window.WatchJsApi.player.changePlayerScreenMode('browserFull');
- toggleMonitorFull(true);
- } else {
- window.WatchJsApi.player.changePlayerScreenMode('browserFull');
- }
- } else
- if ($('body').hasClass('full_with_browser')) {
- window.WatchJsApi.player.changePlayerScreenMode('notFull');
- toggleMonitorFull(false);
- } else {
- if (fullScreenType === 'monitor') {
- window.WatchJsApi.player.changePlayerScreenMode('browserFull');
- toggleMonitorFull(true);
- } else {
- window.WatchJsApi.player.changePlayerScreenMode('browserFull');
- }
- }
- };
-
- var cursorHideTimer = null;
- var mousemove = function() {
- $shield.addClass('showCursor');
- if (cursorHideTimer) {
- window.clearTimeout(cursorHideTimer);
- cursorHideTimer = null;
- }
- cursorHideTimer = window.setTimeout(function() {
- $shield.off('mousemove', mousemove);
- $shield.removeClass('showCursor');
- window.setTimeout(function() { $shield.on('mousemove', mousemove); }, 500);
- }, 1500);
- };
-
- var mousedown = function(e) {
- if (e.button === 0) return;
- // 左ボタン以外でクリックされたら5秒間だけシールドを解除するよ
- e.preventDefault(); e.stopPropagation();
-
- $shield.addClass('disableTemp');
- $toggle.addClass('disableTemp');
-
- $toggle
- .css('opacity', 1)
- .animate({'opacity': 0.3}, 5000, function() { $toggle.css('opacity', ''); });
- window.setTimeout(function() {
- $toggle.removeClass('disableTemp');
- $shield.removeClass('disableTemp');
- $toggle.css('opacity', '');
- }, 5000);
- };
-
- $shield
- .on('click' , click)
- .on('dblclick', dblclick)
- .on('mousedown', mousedown)
- .on('mousemove', mousemove);
-
- $toggle
- .on('click', toggleDisable);
-
- },
- _initializeScreenMode: function() {
- var playerScreenMode = this._playerScreenMode;
- var lastScreenMode = '';
- var $nicoplayerContainer = $('#nicoplayerContainer');
- var toggleMonitorFull = $.proxy(this.toggleMonitorFull, this);
-
- var onPlayerContainerMouseMove = _.debounce(function(e) {
- var bottom = $nicoplayerContainer.innerHeight() - e.clientY;
- $('body').toggleClass('showControl', bottom < 100);
- }, 100);
-
- var onScreenModeChange = function(sc) {
- var mode = sc.mode;
- if (lastScreenMode === 'browserFull' && mode !== 'browserFull') {
- toggleMonitorFull(false);
- }
- lastScreenMode = mode;
-
- if (mode === 'browserFull') {
- $nicoplayerContainer.on('mousemove', onPlayerContainerMouseMove);
- } else {
- $nicoplayerContainer.off('mousemove', onPlayerContainerMouseMove);
- }
- };
-
- playerScreenMode.addEventListener('change', onScreenModeChange);
- },
- initializePlayerEvent: function() {
- var playerAreaConnector = this._playerAreaConnector;
- var watchInfoModel = this._watchInfoModel;
- var playlist = this._playlist;
- var $shield = this._$shield;
- var $toggle = this._$toggle;
- var toggleDisable = $.proxy(this.toggleDisable, this);
-
- this._initializeScreenMode();
-
- // 最初に再生開始されるまでは表示しない。 ローカルストレージ~が出たときにクリックできるようにするため。
- // でも自動再生にしてると詰む。
- playerAreaConnector.addEventListener(
- 'onVideoStarted', function() {
- $shield.addClass('initialized');
- $toggle.addClass('initialized');
- toggleDisable(false);
- }
- );
-
- // 再生後メニューがクリックできないのも困るので無効化する
- playerAreaConnector.addEventListener(
- 'onVideoEnded', function() {
- toggleDisable(true, true);
- }
- );
-
- playerAreaConnector.addEventListener(
- 'onVideoSeeked', function(vpos/*, b, c*/) {
- // もう一度再生する場合など
- if (parseInt(vpos, 10) === 0) toggleDisable(false);
- }
- );
-
- var hasPreviousVideo = function() {
- console.log('%chasPreviousVideo', 'background: cyan;', playlist.getPlayingIndex(), 0);
- return playlist.getPlayingIndex() > 0;
- };
-
- var hasNextVideo = function() {
- console.log('%chasNextVideo', 'background: cyan;', playlist.getPlayingIndex(), playlist.getItems().length);
- return playlist.getPlayingIndex() < playlist.getItems().length - 1;
- };
-
- var onWatchInfoModelReset = function() {
- $shield
- .toggleClass('hasPrevious', hasPreviousVideo())
- .toggleClass('hasNext', hasNextVideo());
- };
- watchInfoModel.addEventListener('reset', onWatchInfoModelReset);
- if (watchInfoModel.initialized) {
- window.setTimeout(function() { onWatchInfoModelReset(); }, 0);
- }
-
- // 動画広告まわり
- var vastStatus = this._vastStatus;
- vastStatus.addEventListener('linearStart', function() {
- $shield.addClass('vast');
- });
- vastStatus.addEventListener('linearEnd', function() {
- $shield.removeClass('vast');
- });
-
- },
- initializePlayerApp: function() {
- // 実装が漏れててエラーが出てるっぽいのを修正
- // フルスクリーン時に動画プレイヤー以外にフォーカスがある時に出る
- var np = window.PlayerApp.ns.player.Nicoplayer.getInstance();
- var ep = $('#external_nicoplayer')[0];
- if (!np.ext_getVolume) {
- np.ext_getVolume = function() { return ep.ext_getVolume(); };
- }
- if (!np.ext_setVolume) {
- np.ext_setVolume = function(v) { ep.ext_setVolume(v); };
- }
- if (!np.ext_getStatus) {
- np.ext_getStatus = function() { return ep.ext_getStatus(); };
- }
- },
- toggleMonitorFull: function(v) {
- var now = FullScreen.now();
- if (now || v === false) {
- FullScreen.cancel();
- } else if (!now || v === true) {
- FullScreen.request(document.body);
- }
- },
- toggleDisable: function(f, showButtonTemporary) {
- var $shield = this._$shield;
- var $toggle = this._$toggle;
-
- var isDisable = $toggle.toggleClass('disable', f).hasClass('disable');
- $shield.toggleClass('disable', isDisable);
-
- if (showButtonTemporary) { // 状態が変わった事を通知するために一時的に表示する
- $toggle.addClass('show');
- window.setTimeout(function() { $toggle.removeClass('show'); }, 2000);
- }
- },
- execCommand: function(cmd) {
- var nicoPlayerConnector = this._nicoPlayerConnector;
- console.log('%cexec command: %s', 'background: cyan;', cmd);
- switch (cmd) {
- case 'previous':
- nicoPlayerConnector.playPreviousVideo();
- break;
- case 'next':
- nicoPlayerConnector.playNextVideo();
- break;
- case 'stop':
- nicoPlayerConnector.stopVideo();
- break;
- case 'play':
- nicoPlayerConnector.playVideo();
- break;
- default:
- break;
- }
- }
- });
-
- if (window.PlayerApp) {
- (function() {
- var watchInfoModel = require('watchapp/model/WatchInfoModel').getInstance();
- if (watchInfoModel.initialized) {
- window.Nicorenizer.initialize();
- } else {
- var onReset = function() {
- watchInfoModel.removeEventListener('reset', onReset);
- window.setTimeout(function() {
- watchInfoModel.removeEventListener('reset', onReset);
- window.Nicorenizer.initialize();
- }, 0);
- };
- watchInfoModel.addEventListener('reset', onReset);
- }
- })();
- }
-
-
- });
-
- var script = document.createElement("script");
- script.id = "NicorenizerLoader";
- script.setAttribute("type", "text/javascript");
- script.setAttribute("charset", "UTF-8");
- script.appendChild(document.createTextNode("(" + monkey + ")()"));
- document.body.appendChild(script);
-
- })();