// ==UserScript==
// @name Convert Youtube Embeds to Image Links
// @description Tries to turn embedded Youtube videos into thumbnails - this is based on "Stop Overzealous Embedding" https://openuserjs.org/users/ConnorBehan
// @namespace http://elundmark.se/code/
// @version 0.2.8
// @date 2023-05-11
// @autor Erik Lundmark
// @contact elundmark@posteo.se
// @license MIT; http://opensource.org/licenses/MIT
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAA1VBMVEUAAAAAAAAFAQEGAQEHAQEIAQENAgIPAwMVBAQaBQUdBQUeBgYhBwcfCgosCQkyCgo3Cws8DAxADQ1GDg49FBRSEBBdExNhFBRwFxd5GBhAKCh6GRk4MjKMHR2PHR1DPT2nIyOtJCREQ0O2JiatKSm5JibBKCjIKirNKyvPKyvQKyvZLS3aLS3eLS3fLS3gLS3dLi7eLi7fLi7gLi7eLy/fLy/gLy/eNTXhNja0VFTkSkrkS0u5dHTpbW3pbm7vl5fwl5fks7PEv7/2wcH74+P++Pj///+XlX/1AAAAAXRSTlMAQObYZgAABERJREFUeAHtm2mT20QYhF8BCUeAbDh2uckSYGGJNLFm1IT7nv//kyirFrrsidc91UWRD34+pVIVPf0e8uFR4sR/y4kTJ4Zh+H/9W+z0jn/FSu/6V6z0hp9Y6Q0/sdIbfmKlN/zE+Nd330dGBsY55zmPUwKmksqIsWBl/WMqCSjTmOdNmr95fTAS5LdeHHZ4cI0+ntwfWt6dsEEcARnA+bDPvUv08Ojt4RncubjClryJQ4wAcEU/eXBp+5kAWIIQrFxdDESYgtB/co4byuaQHxd3BtJMwfMzAVBijxkr5/T39oB+KcEcO0wA52/0gPM/tgdY5PlzEy1/kyDvB7ji/PUpqH7y3mPcQH9q90+cQr//3tdoW4AtH9Kv90DdP/qvQXY38ImU/1KsX9gj3gn9V7DqJ4kBOhJcK/ULfs4g8yr39SnY/edrwaj3kT3w+88AIPom+vXzHcG4llE/l2B2q3HuHq4A5E3k9Xw/pohYmo3SExj9ZwtKu9NyTUb9fC207mqzfhQGIPoeePWvMIC6B3XglT92/QygJ6iVEe56/W8DKJtYKyPY9TOAvomVCZz6GWBGX4J6g18/A3QlqP9i+MkSE4hyN1Zi9n9l5ivxsU1kAGLWv3YgAWoPGIB49QOZOyD2oO5h1Q+U6P22VRscPxAZRJkCxcTwcwfUKVRCev1kYgCxB/XZ9PrZARCpB/UQXX4SY+fvTvUgHX5SooAoU6i3IPtJChBpCvVW6O/cAb0H9XY++/EpeigBlccvM8Bt/PmzEUDYw3qcP37wAxD65QD1r1+f+gFavx6Ac/AD8DNyX4Cf/ACtXw7w+y/+CNr+6wGGdy79JWz9agD+iuMHoF8OIPyman4iEvxdPYgi7R+R9HoPUoySn2h6+V2xhNZ/ovrVBJEEvxCg+5sBA0h+Iuu1PZgiSfMnul+7FyJL9RNNr/cgZslPJL3egxJJ8hPdL21ijkWffxvgI+eUZ2WJRfKTnXfdb/vPF5oAs9r/JsBv35snXeyA7meAT78zzplIRHT4GWD44HPjtKoJIM6fAYY3rt2TLgaQ/ITfv7yTriaA2tHadNTaRAboqcY6NWsCFPtKRoIlIjZ6/f7JKeGhlXH+Z5y1gcfXi1GFsT0cgV9/91kbiS1u/UYHGcCo30jAw2vDb21i+ifAl0b/rT2IFeCrl4z6jSnwMb6HZz257QRfYGXiYzy4esWvX96Ds0d7D7SBCVy/ss9nDzkBBuAUhP47m0g/9h9mOzPr5x5I/hJkYQ+s+oUErH8OIUHjdzeR/hQ7gAn6/fom0o/YozCB7ecmCvWTwgSEfn8K9I9LEILUJKDf7gH9JcdBZhQmoN/uAf0lzXGMV20/N5G8+cmUOh/vf4F/s8wZmNbn+zcTFpQ8oqQFqZQyAiWVhKVMI0rO8wjktGwLfc37Dwbh0+knpp/Q34nlJ34zfVy/D/3PJSdOnDhx4m//OuSgMioX6QAAAABJRU5ErkJggg==
// @include *
// @exclude http*://youtu.be/*
// @exclude http*://*.youtu.be/*
// @exclude http*://*.ytimg.com/*
// @exclude https://www.youtube.com/feed/*
// @exclude https://www.youtube.com/channel/*
// @exclude https://www.youtube.com/user/*
// @exclude https://www.youtube.com/@*
// @exclude https://www.youtube.com/watch?*
// @exclude https://www.youtube.com/playlist?*
// @exclude https://www.youtube.com/my_videos?*
// @exclude https://www.youtube.com/dashboard?*
// @exclude https://www.youtube.com/view_all_playlists*
// @grant none
// ==/UserScript==
// Change iframesLinkTarget / nonIframesLinkTarget to "_blank" if you want to open the videos in a new window
(function () {
"use strict";
if (!Boolean("getComputedStyle" in window)) return;
var ytWatchBaseHref = "https://www.youtube.com/watch?v=",
youtubeDomainPatt = /^(?:(?:[a-zA-Z0-9\-]+\.)?youtube(?:\-nocookie)?\.com)$/,
riskyTags = ["object", "param", "embed"],
youtubeIdPatt = /(?:\/v\/|\?v=|\/embed\/)([a-zA-Z0-9_\-]{11})/,
iframesLinkTarget = "_top",
nonIframesLinkTarget = "_self",
loadedPatt = /^loaded\d{13}$/,
replCounter = 0,
insideIframeId,
MutationObserver,
observer,
makeArray = function (o) {
// Turn array-like objects into Arrays
// http://cwestblog.com/2015/02/11/javascript-quirk-array-slicing-with-node-lists/
try {
return Array.prototype.slice.call(o);
} catch (e) {}
for (var i = 0, l = o.length, a = new Array(i); i < l; i+=1) {
if (i in o) {
a[i] = o[i];
}
}
return a;
},
getIframeTmpl = function (docTitle, th) {
// Inline hardcoded style attributes helps weird "leakage" of forced styles to apply to other elements
// can't replicate it but it happened before this, even to a top window from an iframe!
return [
"<!DOCTYPE html>"+
"<html style='margin:0!important;padding:0!important;overflow:hidden!important;'>"+
"<head>"+
"<meta charset='utf-8'>"+
"<meta name='viewport' content='width=device-width, initial-scale=1'>"+
"<title>"+docTitle+"</title>"+
"</head>"+
"<body style='margin:0!important;padding:0!important;background:#FFF;overflow:hidden!important;'>",
th,
"</body>"+
"</html>"
];
},
// New icon: https://www.iconfinder.com/icons/410520 (16% auto)
// Old icon: https://www.iconfinder.com/icons/669690 (20% auto)
youtubeIcon = "data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjUwMCIgd2lkdGg9IjUwMCIgeG1sbnM9Imh0dHA6Ly93"+
"d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMCAwdjUwMGg1MDBWMHoiIGZpbGw9IiNjYzE4MWUiLz48cGF0aCBkP"+
"SJNNTAwIDI0Ni4yNzJWNTAwSDE4NC4wMThsLTc1Ljc1Mi03NS43NTItLjQxLTE3NS42NzcgOTAuNjEzLTIyLjA0My0yMy"+
"44MjYtMjMuODI1IDMuNTctNzUuOTE2LTcuODU0LTY2LjgxIDI2Ljc5NyAyNi43OTYgMjQuNTg3LTI2LjgwNCA0My43Mzc"+
"gNDMuNzM3IDMuODItMS44IDMyLjA3MyAzMi4wNzMgMTIuNjg1LTM3LjY4NyAyMS44NDUgMjEuODQ1IDE0LjE2LTIxLjh6"+
"IiBzdHJva2U9IiMwMDAiIG9wYWNpdHk9Ii4zIi8+PHBhdGggZD0iTTE0Ny44NzUgNjBjNC41MDIgMTMuMjM1IDkuMjEyI"+
"DI2LjUxIDEzLjcyIDM5Ljc1IDYuODUgMTkuOTAyIDExLjEyNSAzNC44OTIgMTMuMDYgNDUuMTI1djU3LjgxM2gyMS4zNz"+
"Z2LTU3LjgxM0wyMjEuNzIgNjBoLTIxLjU5NWwtMTQuNTYzIDU2LjAzTDE3MC4zNDUgNjBoLTIyLjQ3em0xMDIuNDcgMzU"+
"uMDYzYy05LjYgMC0xNy4wODIgMy42MzQtMjIuNDA3IDEwLjkwNi00LjEgNS4zNy02LjAzMiAxMy42OTUtNi4wMzIgMjUu"+
"MDZ2MzcuMjJjMCAxMS4zIDEuOTMzIDE5LjY4IDYuMDMgMjUgNS4zMjggNy4yNSAxMi44MSAxMC45MDYgMjIuNDA4IDEwL"+
"jkwNiA5LjY1NSAwIDE3LjE3LTMuNjU1IDIyLjUtMTAuOTA2IDQuMDQtNS4zMiA1Ljk3LTEzLjcgNS45Ny0yNWwtLjAwMi"+
"0zNy4yMmMwLTExLjM2NC0xLjkzLTE5LjY5LTUuOTY4LTI1LjA2LTUuMzMtNy4yNzMtMTIuODQ1LTEwLjkwNy0yMi41LTE"+
"wLjkwN3ptNDQuMzEgMS4yOHY4NC4wOTRjMCA3LjUwNi42ODggMTIuNTkgMS43NSAxNS44MTMgMS45MjMgNS4zODYgNi4x"+
"NzUgNy45MDYgMTIuMzc2IDcuOTA2IDcuMDMgMCAxNC4zNjQtNC4yNyAyMi4wMzMtMTMuMDYydjExLjU5NGgxOS4yNVY5N"+
"i4zNDRoLTE5LjI1djgxLjI4Yy00LjI3IDYuMDM1LTguMzM2IDktMTIuMTg4IDktMi41ODYgMC00LjA3NS0xLjUxNi00Lj"+
"UtNC41LS4yMzItLjYzLS4yNS0yLjk4NC0uMjUtNy41di03OC4yOGgtMTkuMjJ6bS00NC4zMSAxNi4wNjNjNi4yMTIgMCA"+
"5LjE4NiA0LjkxMyA5LjE4NiAxNC43NXY0NC43NWMwIDkuODQtMi45NzQgMTQuNzItOS4xODYgMTQuNzJzLTkuMTg4LTQu"+
"ODgtOS4xODgtMTQuNzJ2LTQ0Ljc1YzAtOS44MzcgMi45NzYtMTQuNzUgOS4xODgtMTQuNzV6bS03LjcyIDEwNy42ODhjL"+
"TM3LjAxNSAwLTc0LjE2NC4yNzMtMTEwLjg3NSA0LjM3NS0xNi41MTggMS44NDUtMzAuMjM0IDE0LjI1LTM0LjA5NCAzMS"+
"4wMy01LjQ5IDIzLjg5Ny01LjU2MiA0OS45Ni01LjU2MiA3NC41NjN2MS43OGMuMDAyIDI0LjA4NS4xNCA0OS40NTMgNS4"+
"1IDcyLjc4MiAzLjg2IDE2Ljc4IDE3LjYwNyAyOS4xODYgMzQuMTI1IDMxLjAzIDM2Ljc1MiA0LjExIDczLjkxIDQuMzQ1"+
"IDExMC45NjggNC4zNDVIMjU3YzM3LjA1NyAwIDc0LjIxNy0uMjM2IDExMC45Ny00LjM0NCAxNi41MTctMS44NDUgMzAuM"+
"jY0LTE0LjI1MiAzNC4xMjQtMzEuMDMgNS40MTMtMjMuNTYgNS40OTgtNDkuMjM1IDUuNS03My41MzJ2LTEuMDNjMC0yNC"+
"42MDQuMDU0LTUwLjY2Ny01LjQzOC03NC41NjQtMy44NTgtMTYuNzgtMTcuNTc3LTI5LjE4NS0zNC4wOTQtMzEuMDMtMzY"+
"uNzEzLTQuMTAyLTczLjg1OC00LjM3Ni0xMTAuODc1LTQuMzc2SDI0Mi42MjV6bS0xMjcgMzkuMjVoNjYuNXYxOS44NzVI"+
"MTU5LjI4VjQwMC41NmgtMjEuMjE4VjI3OS4yMmgtMjIuNDM3di0xOS44NzZ6bTE0MC41OTQgMGgxOS4wNnY0Ni4xMjVjN"+
"i4xNTctNy41NzggMTIuNzA4LTExLjQwOCAxOS43Mi0xMS40MDggNy41OSAwIDEyLjY2IDMuOTk1IDE1LjIyIDExLjg3NS"+
"AxLjI2MiA0LjIzNSAxLjkzNiAxMS4yIDEuOTM2IDIxLjE1N3Y0MS44NzVjMCA5LjczOC0uNjc1IDE2Ljc1Ni0xLjkzNyA"+
"yMS4yMTdDMzA3LjY2IDM5OC4wMSAzMDIuNTkgNDAyIDI5NSA0MDJjLTYuNzc4IDAtMTMuMzUtMy43NDMtMTkuNzItMTEu"+
"NjI1djEwLjE4OGgtMTkuMDZ2LTE0MS4yMnptOTkuNDY3IDM0LjcyYzkuNTY2LS4wMDIgMTcuMTkyIDMuNTc3IDIyLjUzM"+
"iAxMC43OCAzLjk5MiA1LjMyIDUuODc0IDEzLjUzNiA1Ljg3NCAyNC43OHYyMS43ODJIMzQ1Ljk3djE4LjY1NmMwIDkuNz"+
"QgMy4xNjcgMTQuNjI1IDkuNzE3IDE0LjYyNSA0LjcgMCA3LjQ3NC0yLjU1NCA4LjU2My03LjY1Ni4xNzMtMS4wMzguNDA"+
"2LTUuMjkuNDA2LTEyLjkzNmgxOS40Mzh2Mi44MTJjMCA2LjEzMy0uMjI3IDEwLjM4LS40MDYgMTIuMjgtLjYzNiA0LjIz"+
"Ni0yLjExNSA4LjAzNi00LjQzOCAxMS40MDgtNS4yNyA3LjY0LTEzLjEwNSAxMS40MDYtMjMuMDYzIDExLjQwNi05Ljk3M"+
"yAwLTE3LjU4LTMuNTkzLTIzLjA5My0xMC43OC00LjA1Mi01LjI2Ny02LjA5NC0xMy41NC02LjA5NC0yNC43MnYtMzYuOD"+
"c1YzAtMTEuMjQ1IDEuODI2LTE5LjQ2IDUuODc1LTI0Ljc4IDUuNTE1LTcuMjA0IDEzLjA4Mi0xMC43ODMgMjIuODEzLTE"+
"wLjc4M3ptLTE3MC43OCAxLjI4aDE5LjAzdjc3LjQ3YzAgNC40NjcuMDEgNi43NzUuMTg4IDcuNDA1LjQ2NCAyLjk1MiAx"+
"LjkyIDQuNDM2IDQuNDcgNC40MzYgMy44MjUgMCA3LjgzLTIuOTAyIDEyLjA2LTguODc1di04MC40MzZoMTkuMDY0djEwN"+
"S4yMmwtMTkuMDY0LS4wMDJ2LTExLjQ2OGMtNy41OCA4LjY5NC0xNC43NzIgMTIuOTA2LTIxLjc4IDEyLjkwNi02LjE0My"+
"AwLTEwLjQwNi0yLjQ4NS0xMi4zMTQtNy44MTMtMS4wMzgtMy4xODctMS42NTYtOC4xOTgtMS42NTYtMTUuNjI1di04My4"+
"yMTh6bTk5Ljg3NCAxNS44NzVjLTMuMTMzIDAtNi4zMDggMS40NzgtOS41IDQuNjI0djY0LjEyNWMzLjE5MiAzLjE4NiA2"+
"LjM2NiA0LjY4NiA5LjUgNC42ODYgNS41IDAgOC4zMTQtNC42NSA4LjMxNC0xNC4xNTZ2LTQ0LjkzOGMwLTkuNTA2LTIuO"+
"DEzLTE0LjM0My04LjMxMy0xNC4zNDN6bTcwLjc4MyAwYy02LjM3OCAwLTkuNTk0IDQuODYtOS41OTQgMTQuNTkydjkuNz"+
"JoMTkuMDZ2LTkuNzJjLjAwMi05LjczMy0zLjE1NS0xNC41OTMtOS40NjctMTQuNTkzeiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg==",
isPartOfOBJECT = function (el) {
var parent;
if (!el) return parent;
while ( (parent=el.parentNode) ) {
if ( parent.nodeName === "BODY" || parent.nodeName === "HTML" ) {
parent = false;
break;
} else if (parent.nodeName === "OBJECT") {
break;
}
el = parent;
}
return parent;
},
removeBackgroundRules = (function () {
var patt = /^(?:[\-_][a-zA-Z0-9]+[\-_])?background(?:$|\-)/;
return function (a) {
// Remove all -x-|background|-
return !patt.test(a[0]);
};
}()),
validateStyleArray = function (a) {
a = Array.isArray(a) && a[0] && a[1] ? a : null;
return !!a;
},
sortMultiArray = function (a, b) {
// Sort so vendor prefixes goes at the top
// www.w3.org/TR/CSS2/syndata.html - "An initial dash or underscore [...]"
return a[0] > b[0] ? 1 : a[0] < b[0] ? -1 : 0;
},
copyAllStyles = function (element, psuedoSelector, property) {
// stackoverflow.com/questions/2558426
var styles = [],
cs;
// The DOM Level 2 CSS way
cs = getComputedStyle(element, psuedoSelector);
if (cs.length !== 0) {
for (var i = 0, len = cs.length; i < len; i+=1) {
if (!property || (property && cs.item(i) === property)) {
styles.push([cs.item(i), cs.getPropertyValue(cs.item(i))]);
if (property) break;
}
}
} else {
// Opera workaround. Opera doesn't support `item`/`length` on CSSStyleDeclaration.
for (var k in cs) {
if (cs.hasOwnProperty(k)) {
if (!property || (property && k === property)) {
styles.push([k, cs[k]]);
}
if (property) break;
}
}
}
// Return single rule
if (property) return styles[0];
// Remove falsies
styles = styles.filter(validateStyleArray);
// Link has its own background, leave psuedo-selectors as they are though
if (!psuedoSelector) styles = styles.filter(removeBackgroundRules);
styles.sort(sortMultiArray);
return styles;
},
getDim = (function () {
var numPatt = /^\d+$/;
return function (dim, el, y) {
var clientDim = "client"+(y[0].toUpperCase())+(y.substr(1));
dim = parseInt(dim, 10) === 0 ? (el.getAttribute(y) || el[clientDim]) : "auto";
return (numPatt.test(dim+"")) ? dim+"px" : dim+"";
};
}()),
makeStyles = function (o, type, psIds) {
o[type] = {};
if (psIds) {
// (Object) <A> Psuedo elements :before { rule: 1; } :after { rule: 1; }
o[type].styleTagContent = "a[data-cyetoil='loaded"+psIds[0]+"'][data-cyetoil-uid='"+psIds[1]+"']:"+type+" {\n";
o[type].styleTagContent += (copyAllStyles(o.node, ":"+type).map(function (a) {
return a ? a.join(":")+"!important;\n" : "";
}).join("")+"\n}\n");
o[type].styleTag = document.createElement("STYLE");
o[type].styleTag.textContent = o[type].styleTagContent;
o[type].styleTag.dataset.cyetoilStyleFor = type+psIds[1];
document.querySelector("head").appendChild(o[type].styleTag);
} else {
// (Object) <A> styles
o[type].styles = (copyAllStyles(o.node, null).concat([
[
"background",
"url('https://i.ytimg.com/vi/"+o.videoId+"/0.jpg') no-repeat scroll 50% 50% / 133% auto transparent"
// 320x180 widescreen version
//"url('https://img.youtube.com/vi/"+o.videoId+"/mqdefault.jpg') no-repeat scroll 50% 50% / 100% 100% transparent"
]
]).map(function (a) {
// Must be able to set an absolute positioned SPAN inside it
if (a[0] === "position" && a[1] !== "fixed" && a[1] !== "absolute") a[1] = "relative";
// It's an inline <A> tag, so it must be block-ish
if (a[0] === "display" && a[1] === "inline") a[1] = "inline-block";
// Force cursor back to pointer
if (a[0] === "cursor") a[1] = "pointer";
// <A> must have a set dimension
if (a[0] === "width") {
if (a[1] === "auto" || a[1] === "0px") a[1] = getDim(a[1], o.node, "width");
// Force a minimum width - usually the object hasn't loaded yet, hence the 200ms delays
if (a[1] === "auto" || a[1] === "0px") a[1] = "320px";
o.forceObjectWidth = a[1];
}
if (a[0] === "height") {
if (a[1] === "auto" || a[1] === "0px") a[1] = getDim(a[1], o.node, "height");
a[1] = (
(a[1] === "auto" || a[1] === "0px" ? (o.forceObjectWidth !== "auto" && o.forceObjectWidth !== "0px" ?
// forces a 16:9 resolution for objects with forced dimensions
Math.ceil((parseInt(o.forceObjectWidth, 10)/16)*9) : "180")+"px" : a[1])
);
}
return a ? a.join(":")+"!important;" : "";
}).join(""));
}
return o;
},
makeImageAnchor = (function () {
var linkCss = [
"margin:0",
"padding:0",
"position:absolute",
"top:0",
"bottom:0",
"left:0",
"right:0"
],
bannerCss = [
"position:absolute",
"bottom:0",
"left:0",
"height:30px",
"width:100%",
"box-sizing:border-box",
"padding:8px 0 0 12px",
"text-shadow:0 1px 0 #000000",
"font:13px/1 sans-serif",
"color:rgb(255,255,255)",
"background-color:rgba(0,0,0,0.75)",
"overflow:hidden",
"white-space:nowrap",
"text-overflow:ellipsis",
"border-bottom:1px solid #000000"
],
spanCss = [
"margin:0",
"padding:0",
"opacity:0.85",
"position:absolute",
"top:0",
"bottom:0",
"left:0",
"right:0"
],
makeinfoBar = function (first, middle, last, target) {
var firstEl = first ? document.createElement("EM") : null,
middleEl = document.createTextNode(middle),
lastEl = last ? document.createElement("EM") : null;
if (firstEl) {
firstEl.textContent = first;
firstEl.style.setProperty("font-style", "normal", "important");
firstEl.style.setProperty("color", "rgba(255,255,255,0.8)", "important");
target.appendChild(firstEl);
}
target.appendChild(middleEl);
if (lastEl) {
lastEl.textContent = last;
lastEl.style.setProperty("font-style", "normal", "important");
lastEl.style.setProperty("color", "rgba(255,255,255,0.8)", "important");
target.appendChild(lastEl);
}
return target;
};
return function (o, isIframe) {
var url = ytWatchBaseHref+o.videoId,
link = document.createElement("A"),
shield = document.createElement("SPAN"),
infoBar;
link.href = url;
link.title = (o.atitle ? o.atitle+" - "+o.videoId : url);
link.setAttribute(
"onmouseup",
"this.querySelector('span').style.setProperty('background-color','rgba(0,0,0,0.6)','important');"+
"this.querySelector('span').style.setProperty('opacity','1','important');"+
"this.querySelector('i').style.setProperty('background-color','rgb(0,0,0)','important');"
);
if (isIframe) {
infoBar = makeinfoBar(
(o.stats.time ? o.stats.time+" " : ""),
link.title,
(o.stats.views ? " - "+o.stats.views : ""),
document.createElement("I")
);
link.title = infoBar.textContent;
link.target = iframesLinkTarget;
infoBar.setAttribute("style", bannerCss.concat([""]).join("!important;"));
shield.setAttribute("style", spanCss.concat([
"background:url('"+youtubeIcon+"') no-repeat scroll 50% 50% / 16% auto rgba(0,0,0,0.08)",
"bottom:25px",
""
]).join("!important;"));
} else {
link.target = nonIframesLinkTarget;
shield.setAttribute("style", spanCss.concat([
"background:url('"+youtubeIcon+"') no-repeat scroll 50% 50% / 16% auto rgba(0,0,0,0.08)",
"bottom:0",
""
]).join("!important;"));
}
if (isIframe) {
link.setAttribute("style", linkCss.concat([
"background:url('https://i.ytimg.com/vi/"+o.videoId
+"/0.jpg') no-repeat scroll 50% 50% / 133% auto transparent",
""
]).join("!important;"));
link.appendChild(shield);
link.appendChild(infoBar);
} else {
link.setAttribute("style", o.main.styles);
shield.innerHTML = " ";
link.appendChild(shield);
}
return link;
};
}()),
linkClickHandler = function (event) {
var link = event.currentTarget;
if (link.nodeName === "A") {
link = link.querySelector("span");
}
link.style.setProperty("background-color", "rgba(0,0,0,0.6)", "important");
},
init = function (dynamicNodes, done) {
var nowNum = Date.now(),
risky = !dynamicNodes ? document.querySelectorAll(riskyTags.join(", ")) : dynamicNodes,
badElements = [],
selector,
paramParent,
videoLink,
riskyNode,
riskyAttributes,
fauxLink,
videoId,
linkHolder;
if (risky.length === 0) return;
// Loop bad elements
for (var j = 0, jLen = risky.length; j < jLen; j+=1) {
riskyAttributes = risky[j].attributes;
// Loop each elements attributes
for (var k = 0, kLen = riskyAttributes.length; k < kLen; k+=1) {
paramParent = null;
// http://alistapart.com/article/flashsatay
// EMBED is part of object, skip this EMBED entirely
if (risky[j].nodeName === "EMBED" && isPartOfOBJECT(risky[j])) {
break;
}
if (risky[j].nodeName === "PARAM") {
// Save parent OBJECT for later use as replace & css target
paramParent = isPartOfOBJECT(risky[j]);
// Break if PARAM is not part of an OBJECT (invalid html)
if (!paramParent) break;
}
riskyNode = riskyAttributes[k].value;
if (!fauxLink) fauxLink = document.createElement("A");
fauxLink.href = riskyNode;
// Get id
if ((videoId=(fauxLink.pathname+fauxLink.search).match(youtubeIdPatt)) === null) continue;
// Check domain
if (fauxLink.hostname.indexOf("youtube.com") >= 0
|| fauxLink.hostname.indexOf("youtube-nocookie.com") >= 0) {
badElements.push({
"node": paramParent || risky[j],
"videoId": videoId[1]
});
break;
}
}
}
if (badElements.length === 0) return;
// Create hidden placeholder
linkHolder = document.createElement("DIV");
linkHolder.style.setProperty("display", "none", "important");
linkHolder.setAttribute("hidden", true);
linkHolder.dataset.cyetoilPlaceholder = nowNum+"";
badElements.forEach(function (el) {
var content;
// This prevents collisions, and PARAMs inside OBJECTs from duplicating
if (loadedPatt.test(el.node.dataset.cyetoil+"")) return;
replCounter += 1;
// Copy all styles that the original video element had
el = makeStyles(el, "main");
// Skip pseudo styles if (not or) content is <none>
if ((content=copyAllStyles(el.node, ":before", "content")) && content[1] !== "none") {
el = makeStyles(el, "before", [nowNum, replCounter]);
// console.log(content, el.before.styleTagContent);
}
if ((content=copyAllStyles(el.node, ":after", "content")) && content[1] !== "none") {
el = makeStyles(el, "after", [nowNum, replCounter]);
}
videoLink = makeImageAnchor(el);
el.node.setAttribute("data-cyetoil", "loaded"+nowNum);
videoLink.setAttribute("data-cyetoil", "loaded"+nowNum);
el.node.setAttribute("data-cyetoil-uid", replCounter+"org");
videoLink.setAttribute("data-cyetoil-uid", replCounter+"");
videoLink.onmouseup = linkClickHandler;
// Dump in placeholder
linkHolder.appendChild(videoLink);
});
// Replace after collecting to preserve css styles
// Reason: Example: Removing any element causes rules such as :nth-of-type(x) to change
document.body.appendChild(linkHolder);
selector = "[data-cyetoil='loaded"+nowNum+"'][data-cyetoil-uid]";
makeArray(linkHolder.querySelectorAll(selector)).forEach(function (node) {
var moved = node.parentNode.removeChild(node), // Remove AND copy it
old = document.querySelector("[data-cyetoil-uid='"+(moved.dataset.cyetoilUid)+"org']");
old.parentNode.replaceChild(moved, old);
});
// Clean up
linkHolder.parentNode.removeChild(linkHolder);
if (typeof done === "function") {
return done();
}
};
// Start
if (youtubeDomainPatt.test(document.location.hostname)) {
if (top.window != self.window
// Are we inside a Youtube IFRAME embed?
&& (insideIframeId=(document.location.pathname+document.location.search).match(youtubeIdPatt)) !== null) {
// 2015-05-05 Make sure we're not an iframe on youtube.com, it _can_ happen
try { if (top.window.document.domain === "www.youtube.com") return; } catch (err) {}
(function () {
var iframeFlashvars = "",
iframeStats,
pageTitle = document.title,
insideIframeLink,
iframeHtml,
formatVideoLength = function (n, isMs) {
n = Number(n);
if (isMs) {
n = (n/1000).toFixed(0);
}
var sForm = (n%60),
hours = "",
minutes = ((n-sForm)/60),
seconds = sForm < 10 ? "0"+sForm : sForm;
if (minutes > 60) {
hours = ((minutes-(minutes%60))/60)+".";
minutes = (minutes%60);
if (minutes < 10) {
minutes = "0"+minutes;
}
}
return hours+minutes+":"+seconds;
};
makeArray(document.querySelectorAll("div")).forEach(function (node) {
iframeFlashvars += (node.outerHTML || "");
});
if (!iframeFlashvars || iframeFlashvars.indexOf("flashvars=") === -1) {
// Probably HTML5 player
iframeFlashvars = document.body.textContent;
iframeStats = {
"views": iframeFlashvars.match(/view_count\D*?(\d+)/),
"time": iframeFlashvars.match(/length_seconds\D*?(\d+)/)
};
} else {
// Probably not the HTML5 Player
iframeStats = {
"views": iframeFlashvars.match(/flashvars=.+?view_count=(\d+)/),
"time": iframeFlashvars.match(/flashvars=.+?length_seconds=(\d+)/)
};
}
if (iframeStats.views) {
iframeStats.views = (iframeStats.views[1]
.replace(/^(\d+)(\d{3})(\d{3})$/, "$1.$2,$3")
.replace(/^(\d+)(\d{3})$/, "$1,$2"))+" views";
}
if (iframeStats.time) {
iframeStats.time = formatVideoLength(iframeStats.time[1]);
}
if (!iframeStats.time && !iframeStats.views && document.querySelector("#player-unavailable")) {
// Private or removed video
pageTitle = "Unavailable / Private / Removed - YouTube";
}
insideIframeLink = makeImageAnchor({
"stats": iframeStats,
"videoId": insideIframeId[1],
"atitle": pageTitle.trim() /*.replace(/(?:\s|-)+Youtube$/i, "")*/
}, true);
iframeHtml = getIframeTmpl(pageTitle, insideIframeLink.outerHTML).join("");
// 2015-02-14 00:35 Removed base64 encoding which I used to safeguard the uri, but
// a bug in firefox makes it break for non ascii characters
// https://bugzilla.mozilla.org/show_bug.cgi?id=213047
document.location.replace("data:text/html;utf8,"+encodeURIComponent(iframeHtml));
}());
}
} else {
window.addEventListener("load", function () {
// Continue looking for OBJECTs and EMBEDs with Youtube videos
MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
// https://developer.mozilla.org/en/docs/Web/API/MutationObserver
// https://dev.opera.com/articles/mutation-observers-tutorial/
observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
var nodes = [];
if (!mutation.addedNodes || mutation.addedNodes.length === 0) return;
makeArray(mutation.addedNodes).forEach(function (node) {
if (node.nodeName && riskyTags.indexOf(node.nodeName.toLowerCase()) !== -1) {
nodes.push(node);
makeArray(node.querySelectorAll("*")).forEach(function (child) {
// Add all nodes inside the new one
nodes.push(child);
});
setTimeout(function () {
// Allow OBJECTs and EMBEDs to reflow the document
init(nodes);
}, 200);
}
});
});
});
init(null, function () {
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: false,
characterData: false
});
});
});
}
}());