YouTube Preview

A userscript to play youtube videos by hovering over their thumbnails.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         YouTube Preview
// @author       sooqua
// @namespace    https://github.com/sooqua/
// @version      0.9
// @description  A userscript to play youtube videos by hovering over their thumbnails.
// @match        *://*.youtube.com/*
// @run-at       document-end
// @grant        none
// ==/UserScript==

var APIready = new Promise(function(resolve) {
    window.onYouTubeIframeAPIReady = resolve;
});

(function() {
    'use strict';

    function init() {
        // requesting api
        var scriptTag = document.createElement('script');
        scriptTag.src = "https://www.youtube.com/iframe_api";
        var firstScriptTag = document.getElementsByTagName('script')[0];
        firstScriptTag.parentNode.insertBefore(scriptTag, firstScriptTag);

        addGlobalStyle(
            ".watch-sidebar-section { " +
            "z-index: auto !important; " +
            "} " +

            ".yt-lockup-thumbnail:not(.yt-pl-thumb),.thumb-wrapper { " +
            "-webkit-transition: all 200ms ease-in !important; " +
            "-webkit-transform: scale(1) !important; " +
            "-moz-transition: all 200ms ease-in !important; " +
            "-moz-transform: scale(1) !important; " +
            "transition: all 200ms ease-in !important; " +
            "transform: scale(1) !important; " +
            "} " +

            ".yt-lockup-thumbnail:not(.yt-pl-thumb):hover,.thumb-wrapper:hover { " +
            "z-index: 9999999999 !important; " +
            "box-shadow: 0px 0px 100px #000000 !important; " +
            "-webkit-transition: all 200ms ease-in !important; " +
            "-webkit-transform: scale(2.0) !important; " +
            "-moz-transition: all 200ms ease-in !important; " +
            "-moz-transform: scale(2.0) !important; " +
            "transition: all 200ms ease-in !important; " +
            "transform: scale(2.0) !important; " +
            "} " +

            ".yt-thumb.video-thumb, .yt-uix-simple-thumb-wrap.yt-uix-simple-thumb-related { " +
            "background-color: black !important; " +
            "} " +

            ".xspinner { " +
            "pointer-events: none; " +
            "position: absolute; " +
            "top: 0; " +
            "right: 0; " +
            "bottom: 0; " +
            "left: 0; " +
            "background: rgba(255,255,255,0.5); " +
            "font-size: 14px; " +
            "text-align: center; " +
            "line-height: 2; " +
            "color: rgb(0,0,0); " +
            "font-weight: bold; " +
            "} ");

        initOn(document);
        var mo = new MutationObserver(function(muts) {
            muts.forEach(function(mut) {
                [].forEach.call(mut.addedNodes, function(node) {
                    if (node instanceof HTMLElement) {
                        initOn(node);
                    }
                });
            });
        });
        mo.observe(document.body, {childList: true, subtree: true});
    }

    function initOn(base) {
        [].forEach.call(base.querySelectorAll('.yt-lockup-thumbnail:not(.yt-pl-thumb) a[href^="/watch"], .thumb-wrapper a[href^="/watch"]'), function(thumbnail) {

            thumbnail.parentNode.addEventListener('mouseover', function() {
                if(thumbnail.overlocker) return;
                thumbnail.overlocker = new Promise(function (unlock) {
                    var rect = thumbnail.parentElement.getBoundingClientRect();
                    var farRight = (rect.right + thumbnail.parentElement.clientWidth/2.0) > window.innerWidth;
                    var farDown = (rect.bottom + thumbnail.parentElement.clientHeight/2.0) > window.innerHeight;
                    var farLeft = (rect.left - thumbnail.parentElement.clientWidth/2.0) < 0;
                    var farUp = (rect.top - thumbnail.parentElement.clientHeight/2.0) < 0;
                    if(farRight || farDown || farLeft || farUp) {
                        var transformOrig = (farRight ? 'right ':'') + (farDown ? 'bottom ':'') + (farLeft ? 'left ':'') + (farUp ? 'top ':'');
                        thumbnail.parentElement.style.webkitTransformOrigin = transformOrig;
                        thumbnail.parentElement.style.mozTransformOrigin = transformOrig;
                        thumbnail.parentElement.style.transformOrigin = transformOrig;
                    }

                    var spinner = document.createElement('div');
                    spinner.className = 'xspinner';
                    spinner.textContent = 'Loading...';

                    var childThumb = thumbnail.querySelector('.yt-thumb.video-thumb, .yt-uix-simple-thumb-wrap.yt-uix-simple-thumb-related');
                    childThumb.appendChild(spinner);

                    thumbnail.watchedContainer = [];
                    for (var el = thumbnail; el; el = el.parentElement) {
                        if (el.classList.contains('watched')) {
                            thumbnail.watchedContainer.push(el);
                            el.classList.remove('watched');
                        }
                    }
                    thumbnail.watchedBadgeContainer = [];
                    [].forEach.call(thumbnail.parentNode.querySelectorAll('.watched-badge'), function (watchedBadge) {
                        thumbnail.watchedBadgeContainer.push(watchedBadge);
                        watchedBadge.style.display = 'none';
                    });
                    thumbnail.imageContainer = [];
                    [].forEach.call(thumbnail.getElementsByTagName('img'), function(img) {
                        thumbnail.imageContainer.push(img);
                        img.style.opacity = 0;
                    });

                    var vidId = thumbnail.href.split('v=')[1];
                    thumbnail.PPlayer = new Promise(function (resolve) {
                        var playerTag = document.createElement('div');
                        playerTag.id = vidId;
                        playerTag.style.pointerEvents = 'none';
                        playerTag.style.position = 'absolute';
                        childThumb.insertBefore(playerTag, childThumb.firstChild);
                        APIready.then(function () {
                            var pplayer = new YT.Player(playerTag.id, {
                                width: childThumb.offsetWidth,
                                height: childThumb.offsetHeight,
                                videoId: vidId,
                                playerVars: {
                                    'enablejsapi': 1, 'autoplay': 1, 'showinfo': 0, 'controls': 0,
                                    'modestbranding': 1, 'ps': 'docs', 'iv_load_policy': 3, 'rel': 0,
                                    'vq': 'medium'
                                },
                                events: {
                                    'onReady': function() { resolve(pplayer); }
                                }
                            });
                        });
                    });
                    thumbnail.PPlayer.then(function () {
                        childThumb.removeChild(spinner);
                        unlock();
                    });
                });
            });

            thumbnail.parentNode.addEventListener('mousemove', function(evt) {
                if (thumbnail.spinner) return;

                if(thumbnail.lastX !== evt.screenX) {
                    var offsetX = evt.offsetX || evt.layerX - thumbnail.offsetLeft;

                    if(thumbnail.PPlayer) {
                        thumbnail.PPlayer.then(function (PPlayer) {
                            try {
                                PPlayer.seekTo(PPlayer.getDuration() * offsetX / thumbnail.parentElement.offsetWidth, true);
                            } catch (e){}
                        });
                    }
                }
                thumbnail.lastX = evt.screenX;
            });

            thumbnail.parentNode.addEventListener('mouseout', function(evt) {
                if(!thumbnail.overlocker) return;
                thumbnail.overlocker.then(function () {
                    if(evt.relatedTarget) {
                        if (thumbnail.parentElement.contains(evt.relatedTarget)) return;
                        if (evt.relatedTarget.className.indexOf('yt-uix-tooltip-tip') !== -1) return;
                    }

                    if(thumbnail.watchedContainer) {
                        for (var i = 0; i < thumbnail.watchedContainer.length; i++)
                            thumbnail.watchedContainer[i].classList.add('watched');
                        thumbnail.watchedContainer = null;
                    }
                    if(thumbnail.watchedBadgeContainer) {
                        for (var i = 0; i < thumbnail.watchedBadgeContainer.length; i++)
                            thumbnail.watchedBadgeContainer[i].style.display = null;
                        thumbnail.watchedBadgeContainer = null;
                    }
                    if(thumbnail.imageContainer) {
                        for (var i = 0; i < thumbnail.imageContainer.length; i++)
                            thumbnail.imageContainer[i].style.opacity = null;
                        thumbnail.imageContainer = null;
                    }

                    if(thumbnail.PPlayer) {
                        thumbnail.PPlayer.then(function(PPlayer) {
                            if(PPlayer.a.parentNode)
                                PPlayer.a.parentNode.removeChild(PPlayer.a);
                            thumbnail.PPlayer = null;
                            thumbnail.overlocker = null;
                        });
                    }
                    else {
                        thumbnail.overlocker = null;
                    }
                });
            });

        });
    }

    function addGlobalStyle(css) {
        var head = document.getElementsByTagName('head')[0];
        if (!head) { return; }
        var style = document.createElement('style');
        style.type = 'text/css';
        style.innerHTML = css;
        head.appendChild(style);
    }

    init();
})();