volkan-k / Vimeo Loop 2018

// ==UserScript==
// @name        Vimeo Loop 2018
// @namespace   volkan-k
// @description Adds a loop button to the video player.
// @include     https://vimeo.com/*
// @copyright   2018, volkan-k
// @license     MIT
// @version     1.6
// @grant       unsafeWindow
// @require 	https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js
// ==/UserScript==

function debugLog(message){
	console.log("USER-SCRIPT VIMEO-LOOP : "+message);
}

var timer1;
var timer2;
var window_focus;
var check_focus = false; // true= check document focus, false= dont check it

$(window.top).focus(function() {
	window_focus = true;
}).blur(function() {
	window_focus = false;
});

(function(){

    'use strict';

    // helper: find DOM element
    function find( selector ) { return document.querySelector( selector ); }

    // wait for player to be ready and set up periodic video check
    function setup()
    {
        // video element available?
        if ( find('.vp-sidedock') && find( '.player .vp-video video' ) )
        {
            // try to add loop button
            // (this can easily break if Vimeo updates their object tree)
            try
            {
                // make loop button
                var button = makeButton();

                // regularly check that button is in sidedock and loop is in sync
                // yes, that's dirty, but Vimeo replaces the player UI somewhen after loading
                setInterval( function()
                {
					// if video changed, loop attribute is lost. recover it.
					if ($("label.loop-label span:contains('Disable')").length>0 && $("video").length>0 && $("video[loop]").length==0){
						toggle_loop();
					}
                    // find control bar
                    var sideDock = find( '.vp-sidedock' )

                    // exit if button exists
                    var oldButton = find( '#loop-button' );
                    if (oldButton!==null){
						return;
					}
					// add new button
					sideDock.appendChild( button );
                }, 500 );
            }
            catch ( error )
            {
                // log the error
                console.error( "[Vimeo Loop] Error appending button:", error );
            }
        }
        else
        {
            // try again later
            setTimeout( setup, 500 );
        }
    }

    // create loop button
    function makeButton()
    {
        // create new button
        var button = document.createElement( 'div' );
        button.setAttribute( 'class', "box");
        button.setAttribute( 'id', "loop-button");
        button.innerHTML = '<label class="rounded-box loop-label invisible hidden" role="presentation" hidden=""><span>Toggle Loop</span></label><button type="button" class="loop-button rounded-box" aria-label="Toggle Loop"><img src="data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDUxMi4wMTYgNTEyLjAxNiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNTEyLjAxNiA1MTIuMDE2OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCI+CjxnPgoJPGc+CgkJPHBhdGggZD0iTTUwNy4zMzYsMTAwLjY5NmwtOTYtOTZjLTQuNTc2LTQuNTc2LTExLjQ1Ni01Ljk1Mi0xNy40NC0zLjQ1NmMtNS45ODQsMi40OTYtOS44ODgsOC4yODgtOS44ODgsMTQuNzUydjQ4aC0yMDggICAgYy05Ny4yMTYsMC0xNzYsNzguNzg0LTE3NiwxNzZjMCw4LjgzMiw3LjE2OCwxNiwxNiwxNmg2NGM4LjgzMiwwLDE2LTcuMTY4LDE2LTE2YzAtNDQuMTkyLDM1LjgwOC04MCw4MC04MGgyMDh2NDggICAgYzAsNi40NjQsMy45MDQsMTIuMzIsOS44ODgsMTQuNzg0YzUuOTg0LDIuNDk2LDEyLjg2NCwxLjEyLDE3LjQ0LTMuNDU2bDk2LTk2QzUxMy41NzYsMTE3LjA4LDUxMy41NzYsMTA2LjkzNiw1MDcuMzM2LDEwMC42OTZ6IiBmaWxsPSIjRkZGRkZGIi8+Cgk8L2c+CjwvZz4KPGc+Cgk8Zz4KCQk8cGF0aCBkPSJNNDk2LjAwOCwyNTUuOTkyaC02NGMtOC44MzIsMC0xNiw3LjE2OC0xNiwxNmMwLDQ0LjE5Mi0zNS44MDgsODAtODAsODBoLTIwOHYtNDhjMC02LjQ2NC0zLjkwNC0xMi4zMi05Ljg4OC0xNC43ODQgICAgcy0xMi44MzItMS4wODgtMTcuNDQsMy40ODhsLTk2LDk2Yy02LjI0LDYuMjQtNi4yNCwxNi4zODQsMCwyMi42MjRsOTYsOTZjNC41NzYsNC41NzYsMTEuNDU2LDUuOTUyLDE3LjQ0LDMuNDU2ICAgIHM5Ljg4OC04LjMyLDkuODg4LTE0Ljc4NHYtNDhoMjA4Yzk3LjIxNiwwLDE3Ni03OC43ODQsMTc2LTE3NkM1MTIuMDA4LDI2My4xNiw1MDQuODQsMjU1Ljk5Miw0OTYuMDA4LDI1NS45OTJ6IiBmaWxsPSIjRkZGRkZGIi8+Cgk8L2c+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPC9zdmc+Cg=="/></button>';

		button.addEventListener('click', toggle_loop, false);

        // apply mouseover effect
 		button.onmouseenter = function() {
			$("label.loop-label").removeClass("invisible hidden").removeAttr("hidden").addClass("visible");
		};
		button.onmouseleave = function() {
			$("label.loop-label").removeClass("visible").addClass("invisible hidden").attr("hidden", "");
		};

        // return DOM object
        return button;
    }

	function toggle_loop()
	{
		var vid=$("video");
		if (vid.length===0){
			return;
		}
		if (vid.attr("loop")==="loop") {
			vid.removeAttr("loop");
			$("label.loop-label span").text("Enable Loop");
			clearInterval(timer1);
		} else {
			vid.attr("loop", "loop");
			$("label.loop-label span").text("Disable Loop");
			timer1=setInterval(click_watch_again,500);
		}
	}

	function click_watch_again(){
		hide_outro();
		var w_a_b=$(".outro-secondary_actions button[data-fatal-attraction*='watch_again']"); // Watch Again Button
		if (w_a_b.length===0){
			return;
		}
		if (check_focus!==true || document.hasFocus() || window_focus===true){
			w_a_b.click();
		}
	}

	function hide_outro(){
		$(".player").removeClass("player-outroVisible");
		$(".vp-controls").removeClass("controls-outro");
		$(".vp-sidedock").removeClass("sidedock-outro");
		$(".vp-outro").hide();
	}
	
	$( ".player_outro_area" ).mouseenter(function() {
		clearTimeout(timer2);
		show_controls();
		timer2=setTimeout(hide_controls,3000);
	});
	$( ".player_outro_area" ).mousemove(function() {
		clearTimeout(timer2);
		show_controls();
		timer2=setTimeout(hide_controls,3000);
	});
	$( ".player_outro_area" ).mouseleave(function() {
		hide_controls();
	});
	function show_controls(){
		$(".vp-controls,.vp-sidedock").removeClass("invisible hidden").show();
	}
	function hide_controls(){
		$(".vp-controls,.vp-sidedock").addClass("invisible hidden").hide();
	}
    // start looking for video player
    setup();

})();