// ==UserScript==
// @name Tabview Youtube
// @name:en Tabview Youtube
// @name:ja Tabview Youtube
// @name:zh-TW Tabview Youtube
// @name:zh-CN Tabview Youtube
// @namespace http://tampermonkey.net/
// @version 3.0.1
// @license MIT https://github.com/cyfung1031/Tabview-Youtube/blob/main/LICENSE
// @description Make comments and lists into tabs for YouTube Videos
// @description:en Make comments and lists into tabs for YouTube Videos
// @description:ja YouTube動画のコメントやリストなどをタブに作成します
// @description:zh-TW 把Youtube Videos中的評論及影片清單製作成Tabs
// @description:zh-CN 把Youtube Videos中的评论及视频列表制作成Tabs
// @author CY Fung
// @match https://www.youtube.com/*
// @icon https://github.com/cyfung1031/Tabview-Youtube/raw/main/images/icon128p.png
// @run-at document-start
// @grant GM_getResourceText
// @resource contentCSS https://raw.githubusercontent.com/cyfung1031/Tabview-Youtube/068d4dcc7d782dd59a288edeb758c77be9e656be/css/style_content.css
// @resource injectionJS1 https://raw.githubusercontent.com/cyfung1031/Tabview-Youtube/068d4dcc7d782dd59a288edeb758c77be9e656be/js/injection_script_1.js
// @require https://cdnjs.cloudflare.com/ajax/libs/cash/8.1.1/cash.min.js
// @noframes
// @downloadURL none
// ==/UserScript==
/* jshint esversion:8 */
function main($){
// MIT License
// https://github.com/cyfung1031/Tabview-Youtube/raw/main/js/content.js
-(function() {
function inIframe() {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
if (inIframe()) return;
if (!$) return;
/**
* SVG resources:
*
*/
const scriptVersionForExternal = '2022/05/07';
const isMyScriptInChromeRuntime = () => typeof GM === 'undefined' && typeof((((window || 0).chrome || 0).runtime || 0).getURL) == 'function'
const svgComments = ``.trim();
const svgVideos = ``.trim();
const svgInfo = ``.trim();
const svgPlayList = ``.trim();
// --- Youtube Video Testing :
// Square Video: https://www.youtube.com/watch?v=L0RXVnRbFg8
// Square Video: https://www.youtube.com/watch?v=bK_rKhMIotU
// ---
const DEBUG_LOG = false
const LAYOUT_VAILD = 1;
const LAYOUT_TWO_COLUMNS = 2;
const LAYOUT_THEATER = 4;
const LAYOUT_FULLSCREEN = 8;
const LAYOUT_CHATROOM = 16;
const LAYOUT_CHATROOM_COLLASPED = 32;
const LAYOUT_TAB_EXPANDED = 64;
const LAYOUT_ENGAGEMENT_PANEL_EXPAND = 128;
const LAYOUT_CHATROOM_EXPANDED = 256;
let pageType = null;
//let data_changed_list = [];
/*
yt-action yt-add-element-to-app yt-autonav-pause-blur yt-autonav-pause-focus
yt-autonav-pause-guide-closed yt-autonav-pause-guide-opened yt-autonav-pause-player
yt-autonav-pause-player-ended yt-autonav-pause-scroll yt-autoplay-on-changed
yt-close-tou-form yt-consent-bump-display-changed yt-focus-searchbox
yt-get-context-provider yt-guide-close yt-guide-hover yt-guide-toggle
yt-history-load yt-history-pop yt-load-invalidation-continuation
yt-load-next-continuation yt-load-reload-continuation yt-load-tou-form
yt-masthead-height-changed yt-navigate yt-navigate-cache yt-navigate-error
yt-navigate-finish yt-navigate-redirect yt-navigate-set-page-offset
yt-navigate-start yt-next-continuation-data-updated yt-open-hotkey-dialog
yt-open-tou-form-loading-state yt-page-data-fetched yt-page-data-updated
yt-page-data-will-update yt-page-manager-navigate-start yt-page-navigate-start
yt-page-type-changed yt-player-attached yt-player-detached yt-player-released
yt-player-requested yt-player-updated yt-popup-canceled yt-popup-closed
yt-popup-opened yt-preconnect-urls yt-register-action yt-report-form-closed
yt-report-form-opened yt-request-panel-mode-change yt-retrieve-location
yt-service-request-completed yt-service-request-error yt-service-request-sent
yt-set-theater-mode-enabled yt-show-survey yt-subscription-changed
yt-swatch-changed yt-theater-mode-allowed yt-unregister-action yt-update-title
yt-update-unseen-notification-count yt-viewport-scanned yt-visibility-refresh
*/
const nullFunc = function(){}
const _console = new Proxy(console, {
get(target, prop, receiver){
if(!DEBUG_LOG && prop==='log'){
return nullFunc
}
return Reflect.get(...arguments)
}
});
_console.log(38489)
const querySelectorFromAnchor = HTMLElement.prototype.querySelector; // nodeType==1 // since 2022/07/12
const querySelectorAllFromAnchor = HTMLElement.prototype.querySelectorAll; // nodeType==1 // since 2022/07/12
const closestDOM = HTMLElement.prototype.closest;
const elementRemove = HTMLElement.prototype.remove;
const elementInsertBefore = HTMLElement.prototype.insertBefore; // since 2022/07/12
const elementContains = HTMLElement.prototype.contains; // since 2022/07/12
const querySelectorFromAnchorFizzy = (root, id, selector)=>{
if(!root) return null;
if(root.id===id && root.matches(selector))return root;
return querySelectorFromAnchor.call(root, selector) || null;
}
function maxUInt(s, d){
let t = (d>s?d:s);
return t>0?t:0;
}
function scriptInjector(script_id, url_chrome, response_id){
let res={
script_id: script_id,
inject: function(){
let res = this, script_id = this.script_id;
if(!document.querySelector(`script#${script_id}`)){
if (res.runtime_url){
addScriptByURL(res.runtime_url).id = script_id;
} else {
addScript(`${res.injection_script}`).id = script_id;
}
}
}
}
res.script_id = script_id;
if (isMyScriptInChromeRuntime()){
res.runtime_url = window.chrome.runtime.getURL(url_chrome)
} else {
res.injection_script = GM_getResourceText(response_id);
}
return res;
}
/*
const script_inject_facp = scriptInjector(
'userscript-tabview-injection-facp',
'js/injectionScript_fixAutoComplete.js',
"injectionFixAutoComplete");
*/
const script_inject_js1 = scriptInjector(
'userscript-tabview-injection-1',
'js/injection_script_1.js',
"injectionJS1");
/** @type {(o: Object | null) => WeakRef | null} */
const mWeakRef = typeof WeakRef === 'function' ? (o => o ? new WeakRef(o) : null) : (o => o || null); // typeof InvalidVar == 'undefined'
/** @type {(wr: Object | null) => Object | null} */
const kRef = (wr => (wr && wr.deref) ? wr.deref() : wr);
const nonCryptoRandStr_base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
function nonCryptoRandStr(/** @type {number} */ n){
const result = new Array(n);
const baseStr = nonCryptoRandStr_base;
const bLen = baseStr.length;
for ( let i = 0; i < n; i++ ) {
let t = null
do {
t = baseStr.charAt(Math.floor(Math.random() * bLen));
}while(i===0 && 10-t>0)
result[i]=t;
}
return result.join('');
}
/**
* Class definition
* @property {string} propName - propriety description
* ...
*/
class ObserverRegister{
constructor(/** @type {()=>MutationObserver | IntersectionObserver} */ observerCreator){
let uid = null;
const uidStore = ObserverRegister.uidStore;
do{
uid = nonCryptoRandStr(5);
}while(uidStore[uid])
uidStore[uid] = true;
/**
* uid is the unique string for each observer
* @type {string}
* @public
*/
this.uid = uid;
/**
* observerCreator is a function to create the observer
* @type {Function}
* @public
*/
this.observerCreator = observerCreator
/**
* observer is the actual observer object
* @type {MutationObserver | IntersectionObserver}
* @public
*/
this.observer = null;
}
bindElement(/** @type {HTMLElement} */ elm, ...args){
if(elm.hasAttribute(`o3r-${this.uid}`))return false;
elm.setAttribute(`o3r-${this.uid}`,'')
if(this.observer===null){
this.observer=this.observerCreator();
}
this.observer.observe(elm, ...args)
return true
}
clear(/** @type {boolean} */ flag){
if(this.observer !== null){
//const uidStore = ObserverRegister.uidStore;
if(flag === true){
this.observer.takeRecords();
this.observer.disconnect();
}
this.observer = null;
for(const s of document.querySelectorAll(`[o3r-${this.uid}]`)) s.removeAttribute(`o3r-${this.uid}`)
//uidStore[this.uid]=false;
//this.uid = null;
}
}
}
/**
* 'uidStore' is the static store of strings used.
* @static
*/
ObserverRegister.uidStore = {}; //backward compatible with FireFox 55.
const mtoMutation_body = new ObserverRegister(()=>{
return new MutationObserver(FP.mtoBodyF)
});
const mtoFlexyAttr = new ObserverRegister(()=>{
return new MutationObserver(mtf_attrFlexy)
});
const mtoVisibility_EngagementPanel = new ObserverRegister(()=>{
return new MutationObserver(FP.mtf_attrEngagementPanel)
});
const sa_epanel = mtoVisibility_EngagementPanel.uid;
const mtoVisibility_Playlist = new ObserverRegister(()=>{
return new AttributeMutationObserver({
"hidden": FP.mtf_attrPlaylist
})
})
const sa_playlist = mtoVisibility_Playlist.uid;
const mtoVisibility_Comments = new ObserverRegister(()=>{
return new AttributeMutationObserver({
"hidden": FP.mtf_attrComments
})
})
const sa_comments = mtoVisibility_Comments.uid;
const mtoVisibility_Chatroom = new ObserverRegister(()=>{
return new AttributeMutationObserver({
"collapsed": FP.mtf_attrChatroom
})
})
const sa_chatroom = mtoVisibility_Chatroom.uid;
class ScriptEF {
constructor() {
this._id = scriptEC;
}
isValid() {
return this._id === scriptEC;
}
}
class Timeout {
set(f, d, repeatCount) {
if (this.cid > 0) return;
let sEF = new ScriptEF();
if (repeatCount > 0) {
let rc = repeatCount;
const g = () => {
this.cid = 0;
if (!sEF.isValid()) return;
let res = f();
if (--rc <= 0) return;
if (res === true) this.cid = timeline.setTimeout(g, d);
}
g();
} else {
const g = () => {
this.cid = 0;
if (!sEF.isValid()) return;
if (f() === true) this.cid = timeline.setTimeout(g, d);
}
this.cid = timeline.setTimeout(g, d);
}
}
clear() {
if (this.cid > 0) timeline.clearTimeout(this.cid);
}
isEmpty() {
return !this.cid
}
}
class Mutex {
constructor() {
this.p = Promise.resolve()
}
lockWith(f) {
this.p = this.p.then(() => {
return new Promise(f)
}).catch(console.warn)
}
}
function prettyElm(/** @type {Element} */ elm) {
if (!elm || !elm.nodeName) return null;
const eId = elm.id || null;
const eClsName = elm.className || null;
return [elm.nodeName.toLowerCase(), typeof eId == 'string' ? "#" + eId : '', typeof eClsName == 'string' ? '.' + eClsName.replace(/\s+/g, '.') : ''].join('').trim();
}
function extractTextContent(/** @type {Node} */ elm) {
return elm.textContent.replace(/\s+/g, '').replace(/[^\da-zA-Z\u4E00-\u9FFF\u00C0-\u00FF\u00C0-\u02AF\u1E00-\u1EFF\u0590-\u05FF\u0400-\u052F\u0E00-\u0E7F\u0600-\u06FF\u0750-\u077F\u1100-\u11FF\u3130-\u318F\uAC00-\uD7AF\u3040-\u30FF\u31F0-\u31FF]/g, '')
}
function addScript(/** @type {string} */ scriptText) {
const scriptNode = document.createElement('script');
scriptNode.type = 'text/javascript';
scriptNode.textContent = scriptText;
try {
document.documentElement.appendChild(scriptNode);
} catch (e) {
console.log('addScript Error', e)
}
return scriptNode;
}
function addScriptByURL(/** @type {string} */ scriptURL) {
const scriptNode = document.createElement('script');
scriptNode.type = 'text/javascript';
scriptNode.src = scriptURL;
try {
document.documentElement.appendChild(scriptNode);
} catch (e) {
console.log('addScriptByURL Error', e)
}
return scriptNode;
}
function addStyle(/** @type {string} */ styleText, /** @type {HTMLElement | Document} */ container) {
const styleNode = document.createElement('style');
//styleNode.type = 'text/css';
styleNode.textContent = styleText;
(container || document.documentElement).appendChild(styleNode);
return styleNode;
}
const stopIframePropagation = function(/** @type {Event} */ evt){
if(scriptEnable && ((evt||0).target||0).nodeName === 'IFRAME'){
evt.stopImmediatePropagation();
evt.stopPropagation();
}
}
document.addEventListener('mouseover', stopIframePropagation, true)
document.addEventListener('mouseout', stopIframePropagation, true)
document.addEventListener('mousedown', stopIframePropagation, true)
document.addEventListener('mouseup', stopIframePropagation, true)
document.addEventListener('keydown', stopIframePropagation, true)
document.addEventListener('keyup', stopIframePropagation, true)
document.addEventListener('mouseenter', stopIframePropagation, true)
document.addEventListener('mouseleave', stopIframePropagation, true)
function isDOMVisible(/** @type {HTMLElement} */ elem) {
// jQuery version : https://github.com/jquery/jquery/blob/a684e6ba836f7c553968d7d026ed7941e1a612d8/src/css/hiddenVisibleSelectors.js
return !!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length);
}
function isNonEmptyString(s) {
return typeof s == 'string' && s.length > 0;
}
async function nativeCall(/** @type {EventTarget} */ dom, /** @type {any[]} */ detail) {
//console.log(1231)
dom.dispatchEvent(new CustomEvent("userscript-call-dom", { detail: detail }))
//console.log(1232)
}
async function nativeFunc(/** @type {EventTarget} */ dom, /** @type {string} */ property, /** @type {any} */ args) {
dom.dispatchEvent(new CustomEvent("userscript-call-dom-func", { detail: { property, args } }))
}
// async function nativeValue(dom, property, args) {
// dom.dispatchEvent(new CustomEvent("userscript-call-dom-value", { detail: { property, args } }))
// }
// async function nativeFuncStacked(/** @type {string} */ selector, /** @type {string} */ property, /** @type {any} */ args){
// document.dispatchEvent(new CustomEvent("userscript-call-dom-func-stacked", { detail: { selector, property, args } }))
// }
// async function nativeValueStacked(selector, property, args){
// document.dispatchEvent(new CustomEvent("userscript-call-dom-value-stacked", { detail: { selector, property, args } }))
// }
// async function nativeConstStacked(selector, property, args){
// document.dispatchEvent(new CustomEvent("userscript-call-dom-const-stacked", { detail: { selector, property, args } }))
// }
function isCommentsK(ytdFlexyElm){
return (ytdFlexyElm.getAttribute('tabview-youtube-comments')||'').indexOf('K')>=0
}
function akAttr(/** @type {HTMLElement} */ cssElm, /** @type {String} */ attrName, /** @type {boolean} */ isNegative, /** @type {string | any} */ flag) {
// isNegative => incomplete loading
let u = parseInt(cssElm.getAttribute(attrName) || 0) || 0;
let ak = Math.abs(u);
if (ak > 100 && isNegative && u < 0) {
} else if (ak > 100 && !isNegative && u > 0) {
} else {
if (ak <= 100) {
ak = 101;
} else {
ak++;
if (ak >= 800) ak = 101;
}
// 101, 102, ... 799, 101
}
cssElm.setAttribute(attrName, `${ isNegative ? -ak : ak }${ flag || '' }`)
}
let timeout_resize_for_layout_change = new Timeout();
function dispatchWindowResize(){
// for youtube to detect layout resize for adjusting Player tools
return window.dispatchEvent(new Event('resize'));
}
function layoutStatusChanged(/** @type {number} */ old_layoutStatus, /** @type {number} */ new_layoutStatus) {
if (old_layoutStatus === new_layoutStatus) return;
const cssElm = kRef(ytdFlexy);
if (!cssElm) return;
const BF_TWOCOL_N_THEATER = LAYOUT_TWO_COLUMNS|LAYOUT_THEATER
let new_isExpandedChat = !!(new_layoutStatus & LAYOUT_CHATROOM_EXPANDED)
let new_isTabExpanded = !!(new_layoutStatus & LAYOUT_TAB_EXPANDED);
let new_isFullScreen = !!(new_layoutStatus & LAYOUT_FULLSCREEN);
let new_isExpandEPanel = !!(new_layoutStatus & LAYOUT_ENGAGEMENT_PANEL_EXPAND);
function showTabOrChat() {
layoutStatusMutex.lockWith(unlock => {
if (lstTab.lastPanel == '#chatroom') {
if (new_isTabExpanded) switchTabActivity(null)
if (!new_isExpandedChat) ytBtnExpandChat();
} else if (lstTab.lastPanel && lstTab.lastPanel.indexOf('#engagement-panel-') == 0) {
if (new_isTabExpanded) switchTabActivity(null)
if (!new_isExpandEPanel) ytBtnOpenEngagementPanel(lstTab.lastPanel);
} else {
if (new_isExpandedChat) ytBtnCollapseChat()
if (!new_isTabExpanded) {setToActiveTab();}
}
timeline.setTimeout(unlock, 40);
})
}
function hideTabAndChat() {
layoutStatusMutex.lockWith(unlock => {
if (new_isTabExpanded) switchTabActivity(null)
if (new_isExpandedChat) ytBtnCollapseChat()
if (new_isExpandEPanel) ytBtnCloseEngagementPanels();
timeline.setTimeout(unlock, 40);
})
}
const statusCollaspedFalse = !!(new_layoutStatus & (LAYOUT_TAB_EXPANDED|LAYOUT_ENGAGEMENT_PANEL_EXPAND|LAYOUT_CHATROOM_EXPANDED))
const statusCollaspedTrue = !statusCollaspedFalse
let changes = (old_layoutStatus & LAYOUT_VAILD) ? old_layoutStatus ^ new_layoutStatus : 0;
let chat_collasped_changed = !!(changes & LAYOUT_CHATROOM_COLLASPED)
let chat_expanded_changed = !!(changes & LAYOUT_CHATROOM_EXPANDED)
let tab_expanded_changed = !!(changes & LAYOUT_TAB_EXPANDED)
let theater_mode_changed = !!(changes & LAYOUT_THEATER)
let column_mode_changed = !!(changes & LAYOUT_TWO_COLUMNS)
let fullscreen_mode_changed = !!(changes & LAYOUT_FULLSCREEN)
let epanel_expanded_changed = !!(changes & LAYOUT_ENGAGEMENT_PANEL_EXPAND)
let BF_LayoutCh_Panel = (changes & (LAYOUT_TAB_EXPANDED|LAYOUT_CHATROOM_EXPANDED|LAYOUT_ENGAGEMENT_PANEL_EXPAND))
let tab_change = BF_LayoutCh_Panel;
let isChatOrTabExpandTriggering = !!((new_layoutStatus) & BF_LayoutCh_Panel);
let isChatOrTabCollaspeTriggering = !!((~new_layoutStatus) & BF_LayoutCh_Panel);
let moreThanOneShown = (new_isTabExpanded + new_isExpandedChat + new_isExpandEPanel) > 1
let requestVideoResize = false;
// two column; not theater; tab collapse; chat expand; ep expand
const IF_01a = LAYOUT_TWO_COLUMNS|LAYOUT_THEATER|LAYOUT_TAB_EXPANDED|LAYOUT_CHATROOM|LAYOUT_CHATROOM_COLLASPED|LAYOUT_ENGAGEMENT_PANEL_EXPAND;
const IF_01b = LAYOUT_TWO_COLUMNS|0|0|LAYOUT_CHATROOM|0|LAYOUT_ENGAGEMENT_PANEL_EXPAND;
// two column; not theater;
const IF_02a = BF_TWOCOL_N_THEATER;
const IF_02b = LAYOUT_TWO_COLUMNS;
// two column; not theater; tab expand; chat expand;
const IF_03a = LAYOUT_TWO_COLUMNS|LAYOUT_THEATER|LAYOUT_TAB_EXPANDED|LAYOUT_CHATROOM|LAYOUT_CHATROOM_COLLASPED;
const IF_03b = LAYOUT_TWO_COLUMNS|0|LAYOUT_TAB_EXPANDED|LAYOUT_CHATROOM|0;
// two column; tab expand; chat expand;
const IF_06a = LAYOUT_TWO_COLUMNS|LAYOUT_TAB_EXPANDED|LAYOUT_CHATROOM|LAYOUT_CHATROOM_COLLASPED;
const IF_06b = LAYOUT_TWO_COLUMNS|LAYOUT_TAB_EXPANDED|LAYOUT_CHATROOM|0;
// two column; theater;
const IF_04a = BF_TWOCOL_N_THEATER;
const IF_04b = BF_TWOCOL_N_THEATER;
// not fullscreen; two column; not theater; not tab expand; not EP expand; not expand chat
const IF_05a = LAYOUT_FULLSCREEN|LAYOUT_TWO_COLUMNS|LAYOUT_THEATER|LAYOUT_TAB_EXPANDED|LAYOUT_ENGAGEMENT_PANEL_EXPAND|LAYOUT_CHATROOM_EXPANDED;
const IF_05b = 0|LAYOUT_TWO_COLUMNS|0|0|0|0;
if(new_isFullScreen){
if (tab_change == LAYOUT_CHATROOM_EXPANDED && (new_layoutStatus & IF_06a) === IF_06b && statusCollaspedFalse && !column_mode_changed) {
// two column; tab expand; chat expand;
switchTabActivity(null);
}
if( !!(tab_change & LAYOUT_CHATROOM_EXPANDED) && new_isExpandedChat ){
//tab_change = LAYOUT_CHATROOM_EXPANDED
//tab_change = LAYOUT_CHATROOM_EXPANDED|LAYOUT_TAB_EXPANDED
timeline.setTimeout(() => {
let scrollElement = document.querySelector('ytd-app[scrolling]')
if(!scrollElement) return;
// single column view; click button; scroll to tab content area 100%
let chatFrame = document.querySelector('ytd-live-chat-frame#chat');
if (chatFrame && isChatExpand()) {
_console.log(7290,1)
chatFrame.scrollIntoView(true);
}
}, 60)
}
if( !!(tab_change & LAYOUT_ENGAGEMENT_PANEL_EXPAND) && new_isExpandEPanel ){
timeline.setTimeout(() => {
let scrollElement = document.querySelector('ytd-app[scrolling]')
if(!scrollElement) return;
// single column view; click button; scroll to tab content area 100%
let epPanel = document.querySelector('ytd-engagement-panel-section-list-renderer[visibility="ENGAGEMENT_PANEL_VISIBILITY_EXPANDED"]');
if (epPanel ) {
_console.log(7290,2)
let pi=50;
let cid = setInterval(()=>{
if(--pi) epPanel.scrollIntoView(true); else clearInterval(cid)
},17)
//
}
}, 60)
}
}else if (fullscreen_mode_changed) {
if( !new_isFullScreen && statusCollaspedTrue && isWideScreenWithTwoColumns() && !isTheater()){
showTabOrChat();
requestVideoResize = true;
} else if( !new_isFullScreen && statusCollaspedFalse && isWideScreenWithTwoColumns() && isTheater()){
ytBtnCancelTheater();
requestVideoResize = true;
}
} else if((new_layoutStatus & IF_01a) === IF_01b && !column_mode_changed && (tab_change==LAYOUT_CHATROOM_EXPANDED || tab_change==LAYOUT_ENGAGEMENT_PANEL_EXPAND) ){
// two column; not theater; tab collapse; chat expand; ep expand
if(epanel_expanded_changed){
layoutStatusMutex.lockWith(unlock => {
ytBtnCollapseChat();
setTimeout(unlock,13)
})
}else if(chat_collasped_changed){
layoutStatusMutex.lockWith(unlock => {
ytBtnCloseEngagementPanels();
setTimeout(unlock,13)
})
}
} else if (!tab_change && column_mode_changed && (new_layoutStatus & IF_02a) === IF_02b && moreThanOneShown) {
// two column; not theater;
// moreThanOneShown
showTabOrChat();
requestVideoResize = true;
} else if (tab_change == LAYOUT_CHATROOM_EXPANDED && (new_layoutStatus & IF_03a) === IF_03b && statusCollaspedFalse && !column_mode_changed) {
// two column; not theater; tab expand; chat expand;
switchTabActivity(null);
requestVideoResize = true;
} else if (isChatOrTabExpandTriggering && (new_layoutStatus & IF_04a) === IF_04b && statusCollaspedFalse && (changes & BF_TWOCOL_N_THEATER) === 0 ) {
ytBtnCancelTheater();
requestVideoResize = true;
} else if ((new_layoutStatus & IF_04a) === IF_04b && statusCollaspedFalse) {
hideTabAndChat();
requestVideoResize = true;
} else if (isChatOrTabCollaspeTriggering && (new_layoutStatus & IF_02a) === IF_02b && statusCollaspedTrue && !column_mode_changed) {
if(tab_change==LAYOUT_ENGAGEMENT_PANEL_EXPAND){
lstTab.lastPanel = null;
if(new_isFullScreen){
}else{
showTabOrChat();
}
}else if(tab_change==LAYOUT_CHATROOM_EXPANDED){
lstTab.lastPanel = null;
if(new_isFullScreen){
}else{
showTabOrChat();
}
}else{
if(new_isFullScreen){
}else{
ytBtnSetTheater();
}
}
requestVideoResize = true;
} else if (!tab_change && !!(changes & BF_TWOCOL_N_THEATER) && (new_layoutStatus & IF_02a) === IF_02b && statusCollaspedTrue) {
showTabOrChat();
requestVideoResize = true;
} else if ( (new_layoutStatus & IF_05a) === IF_05b ) {
// bug fix for restoring from mini player
layoutStatusMutex.lockWith(unlock => {
setToActiveTab();
timeline.setTimeout(unlock, 40);
})
requestVideoResize = true;
} else if (tab_expanded_changed) {
requestVideoResize = true;
}
if (requestVideoResize) {
timeout_resize_for_layout_change.clear();
timeout_resize_for_layout_change.set(() => {
dispatchWindowResize();
}, 92)
}
}
function fixLayoutStatus(x){
const new_isExpandedChat = !(x & LAYOUT_CHATROOM_COLLASPED) && (x & LAYOUT_CHATROOM)
return new_isExpandedChat ? (x | LAYOUT_CHATROOM_EXPANDED) : (x & ~LAYOUT_CHATROOM_EXPANDED);
}
const wls = new Proxy({
/** @type {number | null} */
layoutStatus:undefined
}, {
get: function(target, prop) {
return target[prop];
},
set: function(target, prop, value) {
if(prop=='layoutStatus'){
if (value === 0) {
target[prop] = value;
return;
}else if(target[prop]===value){
return;
}else{
if (!target.layoutStatus_pending) {
target.layoutStatus_pending = true;
const old_layoutStatus = target[prop];
target[prop] = value;
layoutStatusMutex.lockWith(unlock => {
target.layoutStatus_pending = false;
layoutStatusChanged((old_layoutStatus), (target[prop]));
timeline.setTimeout(unlock, 40)
})
return;
}
}
}
target[prop] = value;
},
has: function(target, prop) {
return (prop in target);
}
});
const svgElm = (w, h, vw, vh, p) => ``
let settings = {
defaultTab: "#tab-videos"
};
function isVideoPlaying(video) {
return video.currentTime > 0 && !video.paused && !video.ended && video.readyState > video.HAVE_CURRENT_DATA;
}
function wAttr(elm, attr, kv) {
if (!elm || kv === null) {} else if (kv === true) { elm.setAttribute(attr, '') } else if (kv === false) { elm.removeAttribute(attr) } else if (typeof kv == 'string') { elm.setAttribute(attr, kv) }
}
function hideTabBtn(tabBtn) {
//console.log('hideTabBtn', tabBtn)
let isActiveBefore = tabBtn.classList.contains('active');
tabBtn.classList.add("tab-btn-hidden");
if (isActiveBefore) {
setToActiveTab();
}
}
function hasAttribute(obj, key) {
return obj && obj.hasAttribute(key);
}
function isTheater() {
const cssElm = kRef(ytdFlexy);
return (cssElm && cssElm.hasAttribute('theater'))
}
function isFullScreen() {
const cssElm = kRef(ytdFlexy);
return (cssElm && cssElm.hasAttribute('fullscreen'))
}
function isChatExpand() {
const cssElm = kRef(ytdFlexy);
return cssElm && cssElm.hasAttribute('userscript-chatblock') && !cssElm.hasAttribute('userscript-chat-collapsed')
}
function isWideScreenWithTwoColumns() {
const cssElm = kRef(ytdFlexy);
return (cssElm && cssElm.hasAttribute('is-two-columns_'))
}
function isAnyActiveTab() {
return $('#right-tabs .tab-btn.active').length > 0
}
function isEngagementPanelExpanded() { //note: not checking the visual elements
const cssElm = kRef(ytdFlexy);
return (cssElm && +cssElm.getAttribute('userscript-engagement-panel') > 0)
}
function engagement_panels_() {
let res = [];
let shownRes = [];
let v = 0,
k = 1,
count = 0;
for (const ePanel of document.querySelectorAll(
`ytd-watch-flexy ytd-engagement-panel-section-list-renderer[o3r-${sa_epanel}]`
)) {
let visibility = ePanel.getAttribute('visibility') //ENGAGEMENT_PANEL_VISIBILITY_EXPANDED //ENGAGEMENT_PANEL_VISIBILITY_HIDDEN
switch (visibility) {
case 'ENGAGEMENT_PANEL_VISIBILITY_EXPANDED':
v |= k;
count++;
shownRes.push(ePanel)
res.push({ ePanel, k, visible: true });
break;
case 'ENGAGEMENT_PANEL_VISIBILITY_HIDDEN':
res.push({ ePanel, k, visible: false });
break;
default:
res.push({ ePanel, k, visible: false });
}
k = k << 1;
}
return { list: res, value: v, count: count, shownRes };
}
function ytBtnOpenEngagementPanel(/** @type {number | string} */ panel_id) {
if (typeof panel_id == 'string') {
panel_id = panel_id.replace('#engagement-panel-', '');
panel_id = parseInt(panel_id);
}
if (panel_id >= 0) {} else return false;
let panels = engagement_panels_();
for (const { ePanel, k, visible } of panels.list) {
if ((panel_id & k) === k) {
if (!visible) ePanel.setAttribute('visibility', "ENGAGEMENT_PANEL_VISIBILITY_EXPANDED");
} else {
if (visible) ytBtnCloseEngagementPanel(ePanel);
}
}
}
function ytBtnCloseEngagementPanel(/** @type {HTMLElement} */ s) {
//ePanel.setAttribute('visibility',"ENGAGEMENT_PANEL_VISIBILITY_HIDDEN");
let button = querySelectorFromAnchor.call(s,'ytd-watch-flexy ytd-engagement-panel-title-header-renderer #header > #visibility-button');
if (button){
button =
querySelectorFromAnchor.call(button, 'div.yt-spec-touch-feedback-shape') ||
querySelectorFromAnchor.call(button, 'ytd-button-renderer');
if(button) button.click();
}
}
function ytBtnCloseEngagementPanels() {
if (isEngagementPanelExpanded()) {
for (const s of document.querySelectorAll(
`ytd-watch-flexy ytd-engagement-panel-section-list-renderer[o3r-${sa_epanel}]`
)) {
if (s.getAttribute('visibility') == "ENGAGEMENT_PANEL_VISIBILITY_EXPANDED") ytBtnCloseEngagementPanel(s);
}
}
}
function ytBtnSetTheater() {
if (!isTheater()) {
const sizeBtn = document.querySelector('ytd-watch-flexy #ytd-player button.ytp-size-button')
if (sizeBtn) sizeBtn.click();
}
}
function ytBtnCancelTheater() {
if (isTheater()) {
const sizeBtn = document.querySelector('ytd-watch-flexy #ytd-player button.ytp-size-button')
if (sizeBtn) sizeBtn.click();
}
}
function ytBtnExpandChat() {
let button = document.querySelector('ytd-live-chat-frame#chat[collapsed] > .ytd-live-chat-frame#show-hide-button')
if (button){
button =
querySelectorFromAnchor.call(button, 'div.yt-spec-touch-feedback-shape') ||
querySelectorFromAnchor.call(button, 'ytd-toggle-button-renderer');
if(button) button.click();
}
}
function ytBtnCollapseChat() {
let button = document.querySelector('ytd-live-chat-frame#chat:not([collapsed]) > .ytd-live-chat-frame#show-hide-button')
if (button){
button =
querySelectorFromAnchor.call(button, 'div.yt-spec-touch-feedback-shape') ||
querySelectorFromAnchor.call(button, 'ytd-toggle-button-renderer');
if(button) button.click();
}
}
const Q = {}
function chatFrameContentDocument() {
// non-null if iframe exist && contentDocument && readyState = complete
/** @type {HTMLIFrameElement | null} */
let iframe = document.querySelector('ytd-live-chat-frame iframe#chatframe');
if (!iframe) return null; //iframe must be there
/** @type {Document | null} */
let cDoc = null;
try {
cDoc = iframe.contentDocument;
} catch (e) {}
if (!cDoc) return null;
if (cDoc.readyState != 'complete') return null; //we must wait for its completion
return cDoc;
}
function chatFrameElement(/** @type {string} */ cssSelector) {
let cDoc = chatFrameContentDocument();
if (!cDoc) return null;
/** @type {HTMLElement | null} */
let elm = null;
try {
elm = cDoc.querySelector(cssSelector)
} catch (e) {
console.log('iframe error', e)
}
return elm;
}
function fixTabs() {
if (!scriptEnable) return;
let queryElement = document.querySelector('*:not(#tab-videos) > #related:not([non-placeholder-videos]) > ytd-watch-next-secondary-results-renderer')
let isRelocated = !!queryElement;
if (isRelocated) {
let relocatedRelated = closestDOM.call(queryElement, '#related'); // NOT NULL
let right_tabs = document.querySelector('#right-tabs');
let tab_videos = querySelectorFromAnchor.call(right_tabs,"#tab-videos");
if (!right_tabs || !tab_videos) return;
for (const s of querySelectorAllFromAnchor.call(relocatedRelated,'#related')) {
s.setAttribute('non-placeholder-videos', '')
}
let target_container = document.querySelector('ytd-watch-flexy:not([is-two-columns_]) #primary-inner, ytd-watch-flexy[is-two-columns_] #secondary-inner')
if (target_container) target_container.append(right_tabs) // last-child
let videos_related = relocatedRelated; // NOT NULL
$('[placeholder-videos]').removeAttr('placeholder-videos');
$('[placeholder-for-youtube-play-next-queue]').removeAttr('placeholder-for-youtube-play-next-queue');
tab_videos.appendChild(videos_related);
let videos_results_renderer = querySelectorFromAnchor.call(relocatedRelated,"ytd-watch-next-secondary-results-renderer");
if (videos_results_renderer) videos_results_renderer.setAttribute('data-dom-changed-by-tabview-youtube', scriptVersionForExternal);
videos_related.setAttribute('placeholder-for-youtube-play-next-queue', '')
videos_related.setAttribute('placeholder-videos', '')
$('[placeholder-videos]').on("scroll", windowScroll);
}
/** @type {HTMLElement | null} */
let chatroom = null;
if (chatroom = document.querySelector('*:not([data-positioner="before|#chat"]) + ytd-live-chat-frame#chat, ytd-live-chat-frame#chat:first-child')) {
let positioner = document.querySelector('tabview-youtube-positioner[data-positioner="before|#chat"]');
if (positioner) positioner.remove();
if (document.querySelector('.YouTubeLiveFilledUpView')) {
// no relocation
} else {
$(chatroom).insertBefore('#right-tabs')
}
$(positioner ? positioner : document.createElement('tabview-youtube-positioner')).attr('data-positioner', 'before|#chat').insertBefore(chatroom)
}
}
function handlerAutoCompleteExist() {
/** @type {HTMLElement} */
let autoComplete = this;
autoComplete.removeEventListener('autocomplete-sc-exist', handlerAutoCompleteExist, false)
let domId = autoComplete.getAttribute('data-autocomplete-input-id')
let searchBox = autoComplete.ownerDocument.querySelector(`[data-autocomplete-results-id="${domId}"]`)
if (!domId || !searchBox) return;
let positioner = searchBox.nextSibling;
if (positioner && positioner.nodeName.toLowerCase() == "autocomplete-positioner") {} else if (positioner && positioner.nodeName.toLowerCase() != "autocomplete-positioner") {
$(positioner = document.createElement("autocomplete-positioner")).insertAfter(searchBox);
} else {
$(positioner = document.createElement("autocomplete-positioner")).prependTo(searchBox.parentNode);
}
$(autoComplete).prependTo(positioner);
positioner.style.setProperty('--sb-margin-bottom', getComputedStyle(searchBox).marginBottom)
positioner.style.setProperty('--height', searchBox.offsetHeight + 'px')
}
function mtf_fixAutoCompletePosition(/** @type {HTMLElement} */ elmAutoComplete) {
elmAutoComplete.setAttribute('autocomplete-disable-updatesc', '')
elmAutoComplete.addEventListener('autocomplete-sc-exist', handlerAutoCompleteExist, false)
let tf = ()=>{
if(!document.documentElement.hasAttribute('tabview-injection-js-1-ready')) return setTimeout(tf, 300);
document.dispatchEvent(new CustomEvent('tabview-fix-autocomplete'))
}
tf();
//script_inject_facp.inject();
}
function mtf_AfterFixTabs() {
/** @type {HTMLElement | null} */
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm) return;
/** @type {HTMLElement | null} */
const rootElement = ytdFlexyElm;
const autocomplete = querySelectorFromAnchor.call(rootElement,'[placeholder-for-youtube-play-next-queue] input#suggestions-search + autocomplete-positioner > .autocomplete-suggestions[data-autocomplete-input-id]:not([position-fixed-by-tabview-youtube])')
if (autocomplete) {
const searchBox = document.querySelector('[placeholder-for-youtube-play-next-queue] input#suggestions-search')
if (searchBox) {
autocomplete.parentNode.setAttribute('position-fixed-by-tabview-youtube', '');
autocomplete.setAttribute('position-fixed-by-tabview-youtube', '');
autocomplete.setAttribute('userscript-scrollbar-render', '')
if (!searchBox.hasAttribute('is-set-click-to-toggle')) {
searchBox.setAttribute('is-set-click-to-toggle', '')
searchBox.addEventListener('click', function() {
setTimeout(() => {
const autocomplete = document.querySelector(`.autocomplete-suggestions[data-autocomplete-input-id="${ this.getAttribute('data-autocomplete-results-id') }"]`)
if (!autocomplete) return;
const isNotEmpty = (autocomplete.textContent || '').length > 0 && (this.value || '').length > 0;
if (isNotEmpty) {
let elmVisible = isDOMVisible(autocomplete)
if (elmVisible) $(autocomplete).hide();
else $(autocomplete).show();
}
}, 20);
})
let timeoutOnce_searchbox_keyup = new Timeout();
searchBox.addEventListener('keyup', function() {
timeoutOnce_searchbox_keyup.set(() => {
const autocomplete = document.querySelector(`.autocomplete-suggestions[data-autocomplete-input-id="${ this.getAttribute('data-autocomplete-results-id') }"]`)
if (!autocomplete) return;
const isNotEmpty = (autocomplete.textContent || '').length > 0 && (this.value || '').length > 0
if (isNotEmpty) {
let elmVisible = isDOMVisible(autocomplete)
if (!elmVisible) $(autocomplete).show();
}
}, 20);
})
}
}
}
let currentLastVideo = querySelectorFromAnchor.call(rootElement,'[placeholder-videos] #items ytd-compact-video-renderer:last-of-type')
let prevLastVideo = kRef(_cachedLastVideo);
if (prevLastVideo !== currentLastVideo && currentLastVideo) {
_cachedLastVideo = mWeakRef(currentLastVideo);
}
if (prevLastVideo !== currentLastVideo && currentLastVideo && prevLastVideo) {
let isPrevRemoved = !prevLastVideo.parentNode
function getVideoListHash() {
let res = [...document.querySelectorAll('[placeholder-videos] #items ytd-compact-video-renderer')].map(renderer => {
return querySelectorFromAnchor.call(renderer,'a[href*="watch"][href*="v="]').getAttribute('href')
}).join('|')
// /watch?v=XXXXX|/watch?v=XXXXXX|/watch?v=XXXXXX
// alternative - DOM.data.videoId
// let elms = document.querySelectorAll('[placeholder-videos] #items ytd-compact-video-renderer')
// let res = [...elms].map(elm=>elm.data.videoId||'').join('|') ;
if (res.indexOf('||') >= 0) {
res = '';
}
return res ? res : null;
}
if (isPrevRemoved) {
// this is the replacement of videos instead of addition
const searchBox = document.querySelector('[placeholder-for-youtube-play-next-queue] input#suggestions-search')
let currentPlayListHash = getVideoListHash() || null;
if (!currentPlayListHash) {
} else if (!videoListBeforeSearch && searchBox) {
videoListBeforeSearch = currentPlayListHash;
if (videoListBeforeSearch) {
//console.log('fromSearch', videoListBeforeSearch)
requestAnimationFrame(function() {
let renderer = document.querySelector('[placeholder-videos] ytd-watch-next-secondary-results-renderer');
if (searchBox && searchBox.parentNode) searchBox.blur();
if (renderer) {
let scrollParent = renderer.parentNode;
if (scrollParent.scrollHeight > scrollParent.offsetHeight) {
let targetTop = renderer.offsetTop;
if (searchBox && searchBox.parentNode == scrollParent) targetTop -= searchBox.offsetHeight
scrollParent.scrollTop = targetTop - scrollParent.firstChild.offsetTop;
}
}
});
}
} else if (videoListBeforeSearch) {
if (currentPlayListHash != videoListBeforeSearch) {
videoListBeforeSearch = null;
//console.log('fromSearch', videoListBeforeSearch)
}
}
}
}
}
function base_ChatExist() {
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm) return null;
// no mutation triggering if the changes are inside the iframe
// 1) Detection of #continuations inside iframe
// iframe ownerDocument is accessible due to same origin
// if the chatroom is collasped, no determination of live chat or replay (as no #continuations and somehow a blank iframe doc)
// 2) Detection of meta tag
// This is fastest but not reliable. It is somehow a bug that the navigation might not update the meta tag content
// 3) Detection of HTMLElement inside video player for live video
// (1)+(3) = solution
let attr_chatblock = null
let attr_chatcollapsed = null;
const elmChat = document.querySelector('ytd-live-chat-frame#chat')
let elmCont = null;
if (elmChat) {
elmCont = chatFrameElement('yt-live-chat-renderer #continuations')
let s = 0;
if (elmCont) {
//not found if it is collasped.
s |= querySelectorFromAnchor.call(elmCont,'yt-timed-continuation') ? 1 : 0;
s |= querySelectorFromAnchor.call(elmCont,'yt-live-chat-replay-continuation, yt-player-seek-continuation') ? 2 : 0;
//s |= elmCont.querySelector('yt-live-chat-restricted-participation-renderer')?4:0;
if (s == 1) {
attr_chatblock = 'chat-live';
}else if (s == 2) attr_chatblock = 'chat-playback';
if (s == 1) $("span#tab3-txt-loader").text('');
} else if (!ytdFlexyElm.hasAttribute('userscript-chatblock')) {
// live chat frame but type not known
attr_chatblock = '';
}
//keep unknown as original
let isCollapsed = !!elmChat.hasAttribute('collapsed');
attr_chatcollapsed = isCollapsed;
} else {
attr_chatblock = false;
attr_chatcollapsed = false;
}
return { attr_chatblock, attr_chatcollapsed }
}
let t_heated_BodyScroll = 0;
function windowScroll() {
let ct = Date.now();
if (ct - t_heated_BodyScroll < 6) return; // avoid duplicate calling
t_heated_BodyScroll = ct;
window.dispatchEvent(new Event("scroll")); // dispatch Scroll Event to Window for content display
}
/* items - > special case (2022/11/09) */
// continuous check for element relocation
function mtf_append_comments() {
/** @type {HTMLElement | null} */
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm) return;
/** @type {HTMLElement | null} */
const rootElement = ytdFlexyElm;
let comments = querySelectorFromAnchorFizzy(rootElement,'comments', '#primary ytd-watch-metadata ~ ytd-comments#comments');
if (comments) {
_console.log(3202)
$(comments).appendTo('#tab-comments').attr('data-dom-changed-by-tabview-youtube', scriptVersionForExternal)
}
}
// continuous check for element relocation
function mtf_liveChatBtnF() {
/** @type {HTMLElement | null} */
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm) return;
/** @type {HTMLElement | null} */
const rootElement = ytdFlexyElm;
let button = querySelectorFromAnchor.call(rootElement,'ytd-live-chat-frame#chat > .ytd-live-chat-frame#show-hide-button:nth-child(n+2)');
if (button){
let parentNode = closestDOM.call(button, 'ytd-live-chat-frame#chat');
if (!parentNode){
console.log('parentNode failed')
} else if( HTMLElement.prototype.prepend ){
// using prepend
HTMLElement.prototype.prepend.call(parentNode, button)
} else{
// using insertBefore
try{
elementInsertBefore.call(parentNode, button, parentNode.firstChild);
}catch(e){
console.log('element insert failed in old browser CE')
}
}
}
//if (button) button.parentNode.insertBefore(button, button.parentNode.firstChild)
}
// continuous check for element relocation
// fired at begining & window resize, etc
function mtf_append_playlist(playlist) {
/** @type {HTMLElement | null} */
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm || !playlist) return;
let ple1 = querySelectorFromAnchor.call(playlist, "*:not(#ytd-userscript-playlist) > ytd-playlist-panel-renderer#playlist:not(.ytd-miniplayer) #items.ytd-playlist-panel-renderer:not(:empty)");
if (ple1) {
let ct = Date.now();
let truePlaylist = closestDOM.call(ple1, 'ytd-playlist-panel-renderer#playlist');
if(!truePlaylist || truePlaylist.nodeType!==1) truePlaylist = null;
else {
let tab_list = document.querySelector("#tab-list");
if(!tab_list) return;
truePlaylist.setAttribute('tabview-true-playlist', ct)
for (const s of document.querySelectorAll(`ytd-playlist-panel-renderer#playlist:not([tabview-true-playlist="${ct}"])`)){
s.removeAttribute('tabview-true-playlist')
}
let $wrapper = getWrapper('ytd-userscript-playlist')
$wrapper.append(truePlaylist).appendTo(tab_list);
truePlaylist.setAttribute('data-dom-changed-by-tabview-youtube', scriptVersionForExternal)
setDisplayedPlaylist(); // relocation after re-layout
requestAnimationFrame(() => {
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm) return;
if (!switchTabActivity_lastTab && (ytdFlexyElm.getAttribute('tabview-selection') + '').indexOf('#tab-') === 0 && /https\:\/\/www\.youtube\.com\/watch.*[\?\&]list=[\w\-\_]+/.test(location.href)) {
if (setToActiveTab('#tab-list')) switchTabActivity_lastTab = '#tab-list';
}
})
}
}
}
// content fix - info & playlist
// fired at begining, and keep for in case any change
function mtf_fix_details() {
if (!scriptEnable) return;
let contentToggleBtn = document.querySelector('ytd-watch-flexy #tab-info ytd-expander tp-yt-paper-button#less.ytd-expander:not([hidden]), #tab-info ytd-expander tp-yt-paper-button#more.ytd-expander:not([hidden])')
if(contentToggleBtn){
(()=> {
const domElement = contentToggleBtn;
contentToggleBtn = null;
// if(!domElement.parentElement) return; // not working in pseudo custom element - parentNode = documentFragment
const expander = closestDOM.call(domElement, 'ytd-watch-flexy #tab-info ytd-expander')
if(!expander || expander.nodeType!==1) return; // checking whether it is still on the page
if(expander.style.getPropertyValue('--ytd-expander-collapsed-height')){
expander.style.setProperty('--ytd-expander-collapsed-height','')
}
nativeCall(expander, [
{'property':'canToggleJobId','value':1}, // false disable calculateCanCollapse in childrenChanged
{'property':'alwaysToggleable','value':false}, // this is checked in childrenChanged
{'property':'recomputeOnResize','value':false}, // no need to check toggleable
{'property':'isToggled','value':true}, // show full content
{'property':'canToggle','value':false}, // hide show more or less btn
{'property':'collapsedHeight','value':999999} // disable collapsed height check
])
})();
}
// just in case the playlist is collapsed
let playlist = document.querySelector('#tab-list ytd-playlist-panel-renderer#playlist')
if(playlist && playlist.matches('[collapsed], [collapsible]')) {
(()=> {
const domElement = playlist;
playlist = null;
// if(!domElement.parentElement || domElement.nodeType!==1) return; // not working in pseudo custom element - parentNode = documentFragment
const tablist = closestDOM.call(domElement, 'ytd-watch-flexy #tab-list')
if(!tablist || tablist.nodeType!==1) return; // checking whether it is still on the page
if (domElement.hasAttribute('collapsed')) wAttr(domElement, 'collapsed', false);
if (domElement.hasAttribute('collapsible')) wAttr(domElement, 'collapsible', false);
})();
}
}
let mTime = Date.now()-152000000;
//let loadedComments = [];
const innerCommentsFuncs=[
// comments
function(){
_console.log(2907,1)
let span = document.querySelector("span#tab3-txt-loader")
let r = '0';
let txt = this.elm.textContent
if (typeof txt == 'string') {
let m = txt.match(/[\d\,\s]+/)
if (m) {
r = m[0].trim()
}
}
if (span){
let tab_btn = closestDOM.call(span,'.tab-btn[userscript-tab-content="#tab-comments"]')
if(tab_btn)tab_btn.setAttribute('loaded-comment','normal')
span.textContent = r;
}
setCommentSection(1);
},
// message
function(){
_console.log(2907,2)
let span = document.querySelector("span#tab3-txt-loader")
if (span){
let tab_btn = closestDOM.call(span,'.tab-btn[userscript-tab-content="#tab-comments"]')
if(tab_btn)tab_btn.setAttribute('loaded-comment','message')
span.textContent ='\u200B';
}
setCommentSection(1);
}
]
/** @type {WeakMap} */
let loadedCommentsDT = new WeakMap();
let cmTime = 0;
function _innerCommentsLoader() {
/** @type {HTMLElement | null} */
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm) return;
_console.log(3434, pageType)
if (pageType!=='watch') return;
//console.log(823100,rootElement)
/** @type {Array} */
let qmElms = [...document.querySelectorAll('ytd-comments#comments #count.ytd-comments-header-renderer, ytd-comments#comments ytd-item-section-renderer.ytd-comments#sections #header ~ #contents > ytd-message-renderer.ytd-item-section-renderer')]
let eTime = +`${Date.now()-mTime}00`;
let res = new Array(qmElms.length);
res.newFound = false;
let ci = 0;
let latest = -1;
let retrival = cmTime;
cmTime = eTime;
for(const qmElm of qmElms){
let mgz = 0
if (qmElm.id === 'count') {
//#count.ytd-comments-header-renderer
mgz = 1;
} else if ((qmElm.textContent || '').trim()) {
//ytd-message-renderer.ytd-item-section-renderer
mgz = 2;
// it is possible to get the message before the header generation.
// sample link - https://www.youtube.com/watch?v=PVUZ8Nvr1ic
// sample link - https://www.youtube.com/watch?v=yz8AiQc1Bk8
}
if(mgz>0){
let lastUpdate = loadedCommentsDT.get(qmElm)||0;
let diff = retrival-lastUpdate
_console.log(2907, diff)
let isNew = (diff>4 || diff<-4);
if(!isNew){
loadedCommentsDT.set(qmElm,eTime);
}else{
loadedCommentsDT.set(qmElm,eTime+1);
res.newFound = true;
latest = ci;
}
res[ci]={
status: mgz,
text: qmElm.textContent,
elm: qmElm,
isNew: isNew,
isLatest: false, //set afterwards
f: innerCommentsFuncs[mgz-1]
}
ci++;
}
}
if(res.length>ci) res.length=ci;
if(latest>=0){
res[latest].isLatest = true;
}
_console.log(2908, res)
return res;
}
// function _innerCommentsLoader() {
// /** @type {HTMLElement | null} */
// let ytdFlexyElm = kRef(ytdFlexy);
// if (!scriptEnable || !ytdFlexyElm) return;
// if (deferredVarYTDHidden) return;
// //console.log(823100,rootElement)
// /** @type {Array} */
// let qmElms = [...document.querySelectorAll('ytd-comments#comments #count.ytd-comments-header-renderer, ytd-comments#comments ytd-item-section-renderer.ytd-comments#sections #header ~ #contents > ytd-message-renderer.ytd-item-section-renderer')]
// let di = 0;
// let dz = false;
// /** @type {WeakSet} */
// let dS = new WeakSet()
// for (const loadedComment of loadedComments) {
// let loadedCommentElm = loadedComment ? kRef(loadedComment) : null;
// if (!loadedCommentElm || !qmElms.includes(loadedCommentElm)) {
// loadedComments[di] = null;
// dz = true;
// } else {
// dS.add(loadedCommentElm)
// }
// di++;
// }
// if (dz) {
// loadedComments = loadedComments.filter(e => !!e)
// }
// let res = new Array(qmElms.length);
// res.newFound = false;
// let ci = 0;
// for (const qmElm of qmElms) {
// let mgz = 0
// if (qmElm.id === 'count') {
// //#count.ytd-comments-header-renderer
// mgz = 1;
// } else if ((qmElm.textContent || '').trim()) {
// //ytd-message-renderer.ytd-item-section-renderer
// mgz = 2;
// // it is possible to get the message before the header generation.
// // sample link - https://www.youtube.com/watch?v=PVUZ8Nvr1ic
// // sample link - https://www.youtube.com/watch?v=yz8AiQc1Bk8
// }
// if (mgz > 0) {
// let isNew = !dS.has(qmElm)
// if (isNew) {
// loadedComments.push(mWeakRef(qmElm))
// res.newFound = true;
// }
// res[ci] = {
// status: mgz,
// text: qmElm.textContent,
// elm: qmElm,
// isNew: isNew,
// f: innerCommentsFuncs[mgz-1]
// }
// }
// ci++;
// }
// let lastElm = kRef(loadedComments[loadedComments.length - 1])
// for (const entry of res) {
// entry.isLatest = entry.elm === lastElm;
// }
// dS = null
// _console.log(2908, res)
// return res;
// }
function restoreFetching(){
if(mtf_forceCheckLiveVideo_disable===2) return;
let ytdFlexyElm = kRef(ytdFlexy);
if(!ytdFlexyElm) return;
_console.log(2901)
if(isCommentsK(ytdFlexyElm))return;
_console.log(2902)
let visibleComments = querySelectorFromAnchor.call(ytdFlexyElm, 'ytd-comments#comments:not([hidden])')
if(!visibleComments) return;
_console.log(2903)
fetchPendings.length= 0 ;
fetched=false;
akAttr(ytdFlexyElm, 'tabview-youtube-comments', false, 'K');
const tabBtn = document.querySelector('[userscript-tab-content="#tab-comments"]');
let span = querySelectorFromAnchor.call(tabBtn,'span#tab3-txt-loader');
tabBtn.removeAttribute('loaded-comment')
span.innerHTML='';
if(tabBtn){
tabBtn.classList.remove("tab-btn-hidden")
}
_console.log(2905)
}
let fetched = false;
let pendingFetch = null;
const comments_ulfx = (innerCommentsLoaderRet)=> {
if (!scriptEnable || !ytdFlexy || !kRef(ytdFlexy)) return;
if (pageType!=='watch') return;
_console.log(980, 1)
let newFound = innerCommentsLoaderRet.newFound;
b=innerCommentsLoaderRet
_console.log(980, 2, !newFound)
if (!newFound) {
if (b.length === 1) {
_console.log(980, 3, 1)
let entry = b[0]
pendingFetch = entry;
}
return;
}
pendingFetch=null;
if (b.length === 1) {
_console.log(980, 3, 3)
let entry = b[0]
entry.f();
fetched = true;
} else if (b.length > 1) {
_console.log(980, 3, 5)
let yy = null;
for (const entry of b) {
if (entry.isLatest === true && entry.isNew) {
yy = entry;
break;
}
}
_console.log(980, 3, 6, yy)
if (yy) {
yy.f();
fetched = true;
}
}
_console.log(2907, innerCommentsLoaderRet)
};
let mtc_nr_comments=0;
const cssOnceFunc_comments = (comments)=>{
// once per {ytd-comments#comments} detection
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm) return;
if (!comments) return;
_console.log(3901)
if(mtoVisibility_Comments.bindElement(comments)){
mtoVisibility_Comments.observer.check(9);
}
};
const cssOnceFunc_teaserInfo = ()=>{
//obsolete?
// for Teaser UI
// once per {#description-and-actions.style-scope.ytd-watch-metadata > #description > ytd-text-inline-expander} detection
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm) return ;
let addedInfo = document.querySelector('#tab-info ytd-expander[tabview-info-expander]');
if(!addedInfo) return;
if(!document.documentElement.hasAttribute('tabview-injection-js-1-ready')) return;
_console.log(3903)
teaserInfo.setAttribute('tabview-removed-duplicate','')
teaserInfo.dispatchEvent(new CustomEvent('tabview-no-duplicate-info'))
//console.log(56)
}
function setOnlyOneEPanel(ePanel){
layoutStatusMutex.lockWith(unlock => {
let cPanels = engagement_panels_();
for (const entry of cPanels.list) {
if (entry.ePanel != ePanel && entry.visible) ytBtnCloseEngagementPanel(entry.ePanel);
}
setTimeout(unlock, 30)
})
}
function immHidden(){
//if(t_preheat_ImmHidden>Date.now()) return setTimeout(immHidden,400); // avoid timeline reset due to change of video
let comments = document.querySelector('ytd-comments#comments')
if(!comments.hasAttribute('hidden')) return; // visible comments content
if(pageType==='watch' && Q.comments_section_loaded===1){
emptyCommentSection();
}
}
const FP = {
mtoBodyF: ( /** @type {MutationRecord[]} */ mutations, /** @type {MutationObserver} */ observer) => {
//subtree DOM mutation checker - {body} \single \subtree
if (!scriptEnable) return;
if (pageType!=='watch') return;
for (const mutation of mutations) {
for (const addedNode of mutation.addedNodes)
if (addedNode.nodeType === 1) {
if (addedNode.nodeName == "DIV" && addedNode.matches('.autocomplete-suggestion:not([autocomplete-disable-updatesc])') ) {
mtf_fixAutoCompletePosition(addedNode)
}else if(addedNode.nodeName == "DIV" && (addedNode.id==='lyricscontainer' || addedNode.id==='showlyricsbutton')){
goYoutubeGeniusLyrics();
}
}
}
},
mtf_attrPlaylist: (attrName, newValue) => {
//attr mutation checker - {ytd-playlist-panel-renderer#playlist} \single
//::attr ~ hidden
//console.log(1210)
_console.log(21311)
if (!scriptEnable) return;
if (pageType!=='watch') return;
let cssElm = kRef(ytdFlexy);
if (!cssElm) return;
_console.log(21312)
let playlist = document.querySelector('ytd-playlist-panel-renderer#playlist[tabview-true-playlist]')
let isAnyPlaylistExist = playlist && !playlist.hasAttribute('hidden');
const tabBtn = document.querySelector('[userscript-tab-content="#tab-list"]');
//console.log(1212.2, isPlaylistHidden, playlist.getAttribute('hidden'))
if (tabBtn) {
//console.log('attr playlist changed')
let isPlaylistTabHidden = tabBtn.classList.contains('tab-btn-hidden')
if (isPlaylistTabHidden && isAnyPlaylistExist) {
//console.log('attr playlist changed - no hide')
tabBtn.classList.remove("tab-btn-hidden");
} else if (!isPlaylistTabHidden && !isAnyPlaylistExist) {
//console.log('attr playlist changed - add hide')
hideTabBtn(tabBtn);
}
}
/* visible layout for triggering hidden removal */
akAttr(cssElm, 'tabview-youtube-playlist', !isAnyPlaylistExist);
},
mtf_attrComments: (attrName, newValue) => {
//attr mutation checker - {ytd-comments#comments} \single
//::attr ~ hidden
// *** consider this can happen immediately after pop state. timeout / interval might clear out.
if (pageType!=='watch') return;
let comments = document.querySelector('ytd-comments#comments')
const tabBtn = document.querySelector('[userscript-tab-content="#tab-comments"]');
if (!comments || !tabBtn) return;
let isCommentHidden = comments.hasAttribute('hidden')
//console.log('attr comments changed')
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm) return;
if( mtf_forceCheckLiveVideo_disable === 2 ){
}else if (!isCommentHidden) {
akAttr(ytdFlexyElm, 'tabview-youtube-comments', false, 'K');
emptyCommentSection();
tabBtn.classList.remove("tab-btn-hidden") //if contains
} else if (isCommentHidden) {
akAttr(ytdFlexyElm, 'tabview-youtube-comments', true, 'K');
immHidden();
}
},
mtf_attrChatroom: (attrName, newValue) => {
//attr mutation checker - {ytd-live-chat-frame#chat} \single
//::attr ~ collapsed
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm) return;
if (pageType!=='watch') return;
layoutStatusMutex.lockWith(unlock => {
const chatBlock = document.querySelector('ytd-live-chat-frame#chat')
const cssElm = kRef(ytdFlexy)
if (!chatBlock || !cssElm) {
unlock();
return;
}
if (pageType!=='watch') {
unlock();
return;
}
if (!cssElm.hasAttribute('userscript-chatblock')) wAttr(cssElm, 'userscript-chatblock', true);
_console.log(332,ytdFlexyElm.hasAttribute('userscript-chatblock'))
let isCollapsed = !!chatBlock.hasAttribute('collapsed');
wAttr(cssElm, 'userscript-chat-collapsed', isCollapsed);
if (cssElm.hasAttribute('userscript-chatblock') && !isCollapsed) lstTab.lastPanel = '#chatroom';
if (!isCollapsed && document.querySelector('#right-tabs .tab-btn.active') && isWideScreenWithTwoColumns() && !isTheater()) {
switchTabActivity(null);
timeline.setTimeout(unlock, 40);
} else {
unlock();
}
if (isCollapsed) {
chatBlock.removeAttribute('yt-userscript-iframe-loaded');
}
})
},
mtf_attrEngagementPanel: ( /** @type {MutationRecord[]} */ mutations, /** @type {MutationObserver} */ observer) => {
//attr mutation checker - {ytd-engagement-panel-section-list-renderer} \mutiple
//::attr ~ visibility
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm) return;
//multiple instance
if (mutations) {
for (const mutation of mutations) {
let ePanel = mutation.target;
if (ePanel.getAttribute('visibility') == 'ENGAGEMENT_PANEL_VISIBILITY_EXPANDED') {
setOnlyOneEPanel(ePanel);
break;
}
}
}
if (pageType!=='watch') return;
layoutStatusMutex.lockWith(unlock => {
const ePanel = document.querySelector('ytd-watch-flexy ytd-engagement-panel-section-list-renderer')
const cssElm = kRef(ytdFlexy)
if (!ePanel || !cssElm) {
unlock();
return;
}
let previousValue = +cssElm.getAttribute('userscript-engagement-panel') || 0;
let { shownRes, value, count } = engagement_panels_();
let nextValue = value;
let nextCount = count;
if (nextCount == 0 && cssElm.hasAttribute('userscript-engagement-panel')) {
storeLastPanel=null;
wAttr(cssElm, 'userscript-engagement-panel', false);
unlock();
} else {
if ((nextCount > 1) || (cssElm.hasAttribute('userscript-engagement-panel') && previousValue === nextValue)) {
unlock();
return;
}
cssElm.setAttribute('userscript-engagement-panel', nextValue);
let b = false;
if (previousValue != nextValue && nextValue > 0) {
lstTab.lastPanel = `#engagement-panel-${nextValue}`;
b = true;
storeLastPanel = mWeakRef( shownRes[0])
//console.log(9999, shownRes[0])
}
if (b && document.querySelector('#right-tabs .tab-btn.active') && isWideScreenWithTwoColumns() && !isTheater()) {
switchTabActivity(null);
timeline.setTimeout(unlock, 40);
} else {
unlock();
}
}
})
},
mtf_initalAttr_chatroom: () => {
// every per [new] {ytd-live-chat-frame#chat} detection - reset after mini-playview
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm) return true;
const rootElement = ytdFlexyElm;
if (!mgChatFrame.inPage()) {
mtoVisibility_Chatroom.clear(true);
let chatroom = querySelectorFromAnchor.call(rootElement,`ytd-live-chat-frame#chat:not([${sa_chatroom}]`)
if (chatroom) {
mgChatFrame.setVar(chatroom);
//console.log(124,chatroom)
if(mtoVisibility_Chatroom.bindElement(chatroom)){
mtoVisibility_Chatroom.observer.check(9)
}
chatroom = null
}
}
return true;
},
}
/** @type {WeakRef | null} */
let displayedPlaylist = null;
/** @type {WeakRef | null} */
let scrollingVideosList = null;
let scriptEnable = false;
let scriptEC = 0;
let lstTab = null;
function lstTabInit(){
lstTab =
{
lastTab: null,
lastPanel: null,
last: null
};
}
let _cachedLastVideo = null;
let videoListBeforeSearch = null;
/** @type {WeakRef | null} */
let ytdFlexy = null;
function pluginUnhook() {
_pluginUnhook();
emptyCommentSection();
}
function _pluginUnhook() {
//console.log(8001)
videoListBeforeSearch = null;
_cachedLastVideo = null;
lstTabInit()
displayedPlaylist = null;
scrollingVideosList = null;
scriptEnable = false;
scriptEC++;
if (scriptEC > 788888888) scriptEC = 188888888;
ytdFlexy = null;
wls.layoutStatus = 0;
//console.log('unc01')
mtoVisibility_EngagementPanel.clear(true)
mtoVisibility_Playlist.clear(true)
mtoVisibility_Comments.clear(true)
mgChatFrame.kVar = null;
mtoVisibility_Chatroom.clear(true)
mtoFlexyAttr.clear(true)
for (const elem of document.querySelectorAll(
['ytd-expander[tabview-info-expander]'].join(', ')
)) {
elem.removeAttribute('tabview-info-expander');
}
mtoMutation_body.clear(true)
Q.mtf_chatBlockQ = null;
}
let pageLang = 'en';
const langWords ={
'en':{
'share':'Share',
'info':'Info',
'videos':'Videos',
'playlist':'Playlist'
},
'jp':{
'share':'共有',
'info':'情報',
'videos':'動画',
'playlist':'再生リスト'
},
'tw':{
'share':'分享',
'info':'資訊',
'videos':'影片',
'playlist':'播放清單'
},
'cn':{
'share':'分享',
'info':'资讯',
'videos':'视频',
'playlist':'播放列表'
},
'du':{
'share':'Teilen',
'info':'Info',
'videos':'Videos',
'playlist':'Playlist'
},
'fr':{
'share':'Partager',
'info':'Info',
'videos':'Vidéos',
'playlist':'Playlist'
},
'kr':{
'share':'공유',
'info':'정보',
'videos':'동영상',
'playlist':'재생목록'
}
};
function getWord(tag){
return langWords[pageLang][tag]||langWords['en'][tag]||'';
}
function getTabsHTML() {
const sTabBtnVideos = `${svgElm(16,16,298,298,svgVideos)}${getWord('videos')}`;
const sTabBtnInfo = `${svgElm(16,16,23.625,23.625,svgInfo)}${getWord('info')}`;
const sTabBtnPlayList = `${svgElm(16,16,426.667,426.667,svgPlayList)}${getWord('playlist')}`;
let str1 = `
`;
let str_fbtns = `
`.replace(/[\r\n]+/g,'');
const str_tabs = [
`${sTabBtnInfo}${str1}${str_fbtns}`,
`${svgElm(16,16,60,60,svgComments)}${str1}${str_fbtns}`,
`${sTabBtnVideos}${str1}${str_fbtns}`,
`${sTabBtnPlayList}${str1}${str_fbtns}`
].join('');
let addHTML = `
`;
return addHTML;
}
function getLang(){
let lang = 'en';
let htmlLang = ((document||0).documentElement||0).lang||'';
switch (htmlLang) {
case 'en':
case 'en-GB':
lang = 'en';
break;
case 'de':
case 'de-DE':
lang = 'du';
break;
case 'fr':
case 'fr-CA':
case 'fr-FR':
lang = 'fr';
break;
case 'zh-Hant':
case 'zh-Hant-HK':
case 'zh-Hant-TW':
lang = 'tw';
break;
case 'zh-Hans':
case 'zh-Hans-CN':
lang = 'cn';
break;
case 'ja':
case 'ja-JP':
lang = 'jp';
break;
case 'ko':
case 'ko-KR':
lang = 'kr';
break;
default:
lang = 'en';
}
if(langWords[lang]) pageLang = lang; else pageLang = 'en';
}
function checkEvtTarget(evt, nodeNames){
return nodeNames.includes((((evt||0).target||0).nodeName||0));
}
let cid_nav_end = 0;
let rc_nav_end = 0;
const symbol_noRepeat = Symbol();
function globalHook(eventType, func){
document.addEventListener(eventType,function(evt){
if(evt[symbol_noRepeat])return;
evt[symbol_noRepeat]=true;
new Promise(()=>{
func(evt);
})
},true)
}
let initializerResolve = null;
let initializerLock = new Promise(resolve=>{initializerResolve=resolve});
const S_GENERAL_RENDERERS = ['YTD-TOGGLE-BUTTON-RENDERER','YTD-MENU-RENDERER']
function pageCheck(){
let m_teaser_info = document.querySelector('#description-and-actions.style-scope.ytd-watch-metadata > #description ytd-text-inline-expander:not([tabview-removed-duplicate])')
if(m_teaser_info) cssOnceFunc_teaserInfo(m_teaser_info)
FP.mtf_initalAttr_chatroom();
mtf_append_comments();
mtf_liveChatBtnF();
fixTabs();
mtf_AfterFixTabs();
}
globalHook('yt-rendererstamper-finished',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName,904, evt.type, evt.detail);
(async ()=>{
await initializerLock;
const nodeName = evt.target.nodeName.toUpperCase();
const node = evt.target
_console.log(evt.target.nodeName,905, evt.type)
if(!S_GENERAL_RENDERERS.includes(nodeName)){
mtc_nr_comments = Date.now();
switch(nodeName){
case 'YTD-ENGAGEMENT-PANEL-SECTION-LIST-RENDERER':
// ignore; trigger by page-updated
break;
default:
_console.log(782,1, nodeName)
pageCheck();
}
}
_console.log(2774, evt.type, !!document.querySelector('ytd-live-chat-frame#chat'))
//console.log(2344, document.querySelector('ytd-live-chat-frame#chat')?5:2)
//console.log(555, document.querySelectorAll('#meta-contents ytd-expander').length)
if(nodeName === 'YTD-MENU-RENDERER'){
//console.log('YTD-MENU-RENDERER !! ', evt.target)
}
if(nodeName === 'YTD-PLAYLIST-PANEL-RENDERER'){
//("yt-playlist-data-updated");
//yt-playlist-reloading
mtf_append_playlist(node);
let m_playlist = document.querySelector(`ytd-playlist-panel-renderer#playlist[tabview-true-playlist]:not([o3r-${sa_playlist}])`)
// once per {ytd-playlist-panel-renderer#playlist} detection
_console.log(3902, !!m_playlist)
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm){}
else if (m_playlist){
if(mtoVisibility_Playlist.bindElement(m_playlist)){
mtoVisibility_Playlist.observer.check(9) //delay check required for browser bug - hidden changed not triggered
}
m_playlist = null;
}
FP.mtf_attrPlaylist();
}else if (nodeName ==='YTD-WATCH-METADATA'){
}else if (nodeName ==='YTD-COMMENTS-HEADER-RENDERER'){
_console.log(3205,1)
let innerCommentsLoaderRet = _innerCommentsLoader();
comments_ulfx(innerCommentsLoaderRet);
_console.log(3205,3)
if(!fetched && pendingFetch){
requestAnimationFrame(()=>{
pendingFetch.f();
fetched= true;
fetchCommentsFinished();
fetched=false;pendingFetch=null;
})
}
if(fetched){
fetchCommentsFinished();
fetched=false;pendingFetch=null;
}
}else if(nodeName==='YTD-ENGAGEMENT-PANEL-SECTION-LIST-RENDERER'){
let ytdFlexyElm = kRef(ytdFlexy);
if (scriptEnable && ytdFlexyElm) {
let match = node.matches(`ytd-watch-flexy ytd-engagement-panel-section-list-renderer:not([o3r-${sa_epanel}])`);
if(match){
mtoVisibility_EngagementPanel.bindElement(node, {
attributes: true,
attributeFilter: ['visibility'],
attributeOldValue: true
})
}
}
}
})()
})
globalHook('yt-player-updated',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
mtc_nr_comments = Date.now();
_console.log(evt.target.nodeName,904, evt.type);
_innerCommentsLoader();
//restoreFetching(); //yt-player-updated might happen after comments ready
if(!fetched){
window.dispatchEvent(new Event("scroll"));
}
pageCheck();
(async ()=>{
await initializerLock;
let nodeName = evt.target.nodeName.toUpperCase()
_console.log(nodeName,905, evt.type)
_console.log(2344,evt.type, document.querySelector('ytd-live-chat-frame#chat')?5:2)
if(nodeName === 'YTD-PLAYER'){
_console.log(2554, nodeName, evt.type, evt.detail )
let m_comments = document.querySelector(`ytd-comments#comments:not([o3r-${sa_comments}])`);
if(m_comments) cssOnceFunc_comments(m_comments);
FP.mtf_initalAttr_chatroom();
}
})();
})
globalHook('yt-page-data-updated',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName,904, evt.type);
mtc_nr_comments = Date.now();
FP.mtf_attrEngagementPanel();
_innerCommentsLoader();
if(!fetched){
window.dispatchEvent(new Event("scroll"));
}
pageCheck();
(async ()=>{
await initializerLock;
_console.log(evt.target.nodeName,905, evt.type)
_console.log(2774, !!document.querySelector('ytd-live-chat-frame#chat'))
_console.log(2344,evt.type, document.querySelector('ytd-live-chat-frame#chat')?5:2)
_console.log(544, document.querySelectorAll('#meta-contents'))
_console.log(546, document.querySelectorAll('#meta-contents ytd-expander'))
let expander = document.querySelector('#meta-contents ytd-expander:not([tabview-info-expander])')
if(expander){
// once per $$native-info-description$$ {#meta-contents ytd-expander} detection
// append the detailed meta contents to the tab-info
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm) return ;
if (!expander) return ;
expander.setAttribute('tabview-info-expander','')
$(expander).appendTo("#tab-info").attr('data-dom-changed-by-tabview-youtube', scriptVersionForExternal)
}
mtf_fix_details();
})();
})
globalHook('yt-page-data-fetched',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
mtc_nr_comments = Date.now();
let nodeName = evt.target.nodeName.toUpperCase()
_console.log(nodeName,904, evt.type);
let d_page = ((evt.detail||0).pageData||0).page;
pageType = d_page;
if(d_page=='watch'){
document.documentElement.classList.toggle('tabview-normal-player', true)
}else{
document.documentElement.classList.toggle('tabview-normal-player', false)
}
dispatchWindowResize(); // player control positioning
(async ()=>{
await initializerLock;
_console.log(nodeName,905, evt.type);
_console.log(2344,evt.type, document.querySelector('ytd-live-chat-frame#chat')?5:2)
if(nodeName==='YTD-APP'){
try{
global_page_url = evt.detail.pageData.url;
}catch(e){
global_page_url =null;
}
_console.log(2554, nodeName, evt.type, evt.detail )
newVideoPage();
}
})();
})
globalHook('yt-watch-comments-ready',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
mtc_nr_comments = Date.now();
let nodeName = evt.target.nodeName.toUpperCase()
_console.log(nodeName,904, evt.type, evt.detail);
//fetchCommentsFinished();
window.dispatchEvent(new Event("scroll"));
pageCheck();
(async ()=>{
await initializerLock;
_console.log(nodeName,905, evt.type);
if(nodeName==='YTD-WATCH-FLEXY'){
let m_comments = document.querySelector(`ytd-comments#comments:not([o3r-${sa_comments}])`);
if(m_comments) cssOnceFunc_comments(m_comments);
if(mtf_forceCheckLiveVideo_disable===2){
}else{
_console.log(3713, fetched)
if(document.querySelector(`ytd-comments#comments`).hasAttribute('hidden')){
//unavailable apart from live chat
_disableComments();
_console.log(3713, 3)
}else{
let innerCommentsLoaderRet = _innerCommentsLoader();
comments_ulfx(innerCommentsLoaderRet);
_console.log(3713, 5)
if(fetched|| pendingFetch ){
_console.log(3713, 6)
// if there is only message, no rendering event occur after yt-watch-comments-ready
timeline.setTimeout(()=>{
if(!fetched && pendingFetch){
pendingFetch.f();
fetched= true;
}
if(fetched){
fetchCommentsFinished();
fetched=false;pendingFetch=null;
}
},800)
}
}
_console.log(3714, fetched, !!pendingFetch)
}
}
})();
})
function onNavigationEnd(evt) {
console.log('yt-navigate-finish')
globalHook('yt-navigate',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
})
globalHook('ytd-playlist-lockup-now-playing-active',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
})
globalHook('yt-service-request-completed',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
})
globalHook('yt-commerce-action-done',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
})
globalHook('yt-navigate',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
})
globalHook('yt-execute-service-endpoint',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
_console.log(2774, evt.type, !!document.querySelector('ytd-live-chat-frame#chat'))
})
globalHook('yt-request-panel-mode-change',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
})
window.addEventListener("message", (evt) => {
if(evt.origin === location.origin && evt.data.tabview){
let data = evt.data.tabview;
if(data.eventType=== "yt-page-type-changed"){
let detail = data.eventDetail
let {newPageType, oldPageType} = detail;
if(newPageType && oldPageType ){
let bool = false;
if(newPageType == 'ytd-watch-flexy'){
bool = true;
pageType = 'watch';
}else if(newPageType=='ytd-browse'){
pageType = 'browse';
}
//if(newPageType=='ytd-browse'){
//}
document.documentElement.classList.toggle('tabview-normal-player', bool)
dispatchWindowResize(); // player control positioning
}
}
console.log(37192,evt)
}
}, false);
/*
document.addEventListener('yt-page-type-changed',(evt)=>{
console.log(3553,evt,evt.detail,evt.target.data)
},false);
*/
globalHook('yt-visibility-refresh',(evt)=>{
if(!evt || !evt.target /*|| evt.target.nodeType !== 1*/) return;
_console.log(evt.target.nodeName||'', evt.type)
_console.log(2784, evt.type, kRef(ytdFlexy).hasAttribute('hidden'),evt.detail)
_console.log(evt.detail)
})
//document.addEventListener('')
globalHook('yt-request-panel-mode-change',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
_console.log(2774, evt.type, !!document.querySelector('ytd-live-chat-frame#chat'))
})
globalHook('yt-navigate-finish',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
_console.log(2774, evt.type, !!document.querySelector('ytd-live-chat-frame#chat'))
let m_comments = document.querySelector(`ytd-comments#comments:not([o3r-${sa_comments}])`);
if(m_comments) cssOnceFunc_comments(m_comments);
})
globalHook('app-reset-layout',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
_console.log(2774, evt.type, !!document.querySelector('ytd-live-chat-frame#chat'))
})
globalHook('yt-guide-close',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
_console.log(2774, evt.type, !!document.querySelector('ytd-live-chat-frame#chat'))
})
globalHook('yt-page-data-will-change',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
})
globalHook('yt-retrieve-location',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
})
globalHook('yt-refit',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
_console.log(2774, evt.type, !!document.querySelector('ytd-live-chat-frame#chat'))
})
globalHook('addon-attached',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
_console.log(2774, evt.type, !!document.querySelector('ytd-live-chat-frame#chat'))
})
globalHook('yt-live-chat-context-menu-opened',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
_console.log(2774, evt.type, !!document.querySelector('ytd-live-chat-frame#chat'))
})
globalHook('yt-live-chat-context-menu-closed',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
_console.log(2774, evt.type, !!document.querySelector('ytd-live-chat-frame#chat'))
})
globalHook('yt-commentbox-resize',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
})
globalHook('yt-rich-grid-layout-refreshed',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(2327, evt.target.nodeName, evt.type)
})
/*
window.addEventListener('resize',function(evt){
if(!evt || !evt.target ) return;
_console.log(7413, 1, evt.target.nodeName||null, evt.type, evt.detail)
let lastTime = 0;
layoutStatusMutex.lockWith(unlock=>{
let thisTime = Date.now();
if(thisTime-lastTime>16){
_console.log(782,2)
pageCheck();
}
requestAnimationFrame(()=>{
let thisTime = Date.now();
if(thisTime-lastTime<16){
if(unlock){
unlock();
unlock=null;
}
}else{
if(unlock){
setTimeout(unlock,17)
unlock=null;
}
}
lastTime=thisTime;
})
if(unlock){
setTimeout(unlock,300)
unlock=null;
}
})
},true)
*/
globalHook('data-changed',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
let nodeName = evt.target.nodeName.toUpperCase()
_console.log(nodeName, evt.type)
if(nodeName==='YTD-ITEM-SECTION-RENDERER' || nodeName==='YTD-COMMENTS'){
_console.log(344)
}
})
globalHook('animationend',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
})
globalHook('yt-dismissible-item-dismissed',(evt)=>{
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
})
document.addEventListener('yt-dismissible-item-undismissed',function(evt){
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
},true)
document.addEventListener('yt-load-next-continuation',function(evt){
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
},true)
document.addEventListener('yt-load-reload-continuation',function(evt){
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
},true)
document.addEventListener('yt-toggle-button',function(evt){
if(!evt || !evt.target || evt.target.nodeType !== 1) return;
_console.log(evt.target.nodeName, evt.type)
},true)
document.dispatchEvent(new CustomEvent('tabview-v-change')) //possible duplicated
document.documentElement.setAttribute('youtube-ready','')
script_inject_js1.inject();
document.documentElement.dispatchEvent(new CustomEvent('tabview-ce-hack'))
//document.dispatchEvent(new CustomEvent('tabview-youtube-comments-check'))
forceConfig();
/*
console.log(window.ytcfg)
try{
window.ytcfg.set({
"EXPERIMENT_FLAGS": {"kevlar_watch_metadata_refresh":false}})
}catch(e){}
// ytcfg.set({EXPERIMENT_FLAGS:{"kevlar_watch_metadata_refresh":true}})
*/
let mRet =
(/^https?\:\/\/(\w+\.)*youtube\.com\/watch\?(\w+\=[^\/\?\&]+\&|\w+\=?\&)*v=[\w\-\_]+/.test(window.location.href) ? 1 : 0) +
(document.querySelector('ytd-watch-flexy[tabview-selection]') ? 2 : 0);
if (mRet === 1) {
document.addEventListener('load',function(evt){
if( checkEvtTarget(evt, ['IFRAME']) && evt.target.matches('body iframe.style-scope.ytd-live-chat-frame#chatframe')){
let iframe = evt.target;
new Promise(resolve=>{
let k = 270
let cid = setInterval(()=>{
if(k--<1) {
clearInterval(cid);
resolve(false);
}
let cDoc = iframe.contentDocument;
if (!cDoc) return null;
if (cDoc.readyState != 'complete') return;
if (!cDoc.querySelector('body')) {
clearInterval(cid);
resolve(false);
}
if(!cDoc.querySelector('yt-live-chat-app')) return;
clearInterval(cid);
if(!document.contains(iframe)) return resolve(false);
resolve(cDoc);
},17)
}).then((res)=>{
if(res){
callFind(res)
}
})
}
}, true)
pluginUnhook(); // in case not triggered by popstate - say mini playing
let timeout = 4; // max. 4 animation frames
let tf = () => {
if (--timeout > 0 && !document.querySelector('#player video')) return requestAnimationFrame(tf);
let ytdFlexyElm = document.querySelector('ytd-watch-flexy')
if(!ytdFlexyElm) return;
scriptEnable = true;
scriptEC++;
ytdFlexy = mWeakRef(ytdFlexyElm)
let timeoutR_findRelated = new Timeout();
timeoutR_findRelated.set(function() {
let ytdFlexyElm = kRef(ytdFlexy);
if(!ytdFlexyElm) return true;
let related = querySelectorFromAnchor.call(ytdFlexyElm,"#related");
if (!related) return true;
foundRelated(related);
}, 100, 10)
function foundRelated(related) {
let promise = Promise.resolve();
if (!document.querySelector("#right-tabs")) {
promise = promise.then(() => {
getLang();
$(getTabsHTML()).insertBefore(related).attr('data-dom-created-by-tabview-youtube', scriptVersionForExternal);
})
}
promise.then(runAfterTabAppended)
}
}
tf();
} else if (mRet === 3) {
if(!scriptEnable) return;
let elmComments = document.querySelector('ytd-comments#comments')
if (elmComments && querySelectorFromAnchor.call(elmComments,'ytd-item-section-renderer#sections.style-scope.ytd-comments')){
nativeFunc(elmComments, "loadComments")
}
} else if (mRet === 0) {
pluginUnhook(); // in case not triggered by popstate - say mini playing
}
if(scriptEnable) {
let m_comments = document.querySelector(`ytd-comments#comments:not([o3r-${sa_comments}])`);
if(m_comments) cssOnceFunc_comments(m_comments);
if(cid_nav_end>0) {
clearInterval(cid_nav_end)
cid_nav_end = 0;
}
rc_nav_end = 0;
cid_nav_end=setInterval(()=>{
if(rc_nav_end<1000) rc_nav_end++;
// regular check for different issues with UI display status
// until the initialization of the page finished + 4700ms
if(rc_nav_end<20){ // <=15s
let playlist = document.querySelector('ytd-playlist-panel-renderer#playlist[tabview-true-playlist]')
if(playlist){
playlist.dispatchEvent(new CustomEvent("userscript-fix-playlist-display", { detail: {} }))
}
let chatroomBtn = document.querySelector('ytd-live-chat-frame#chat #show-hide-button.ytd-live-chat-frame')
if(chatroomBtn){
// in case text is different from the visual state
chatroomBtn.dispatchEvent(new CustomEvent("userscript-fix-chatroombtn-text", { detail: {} }))
}
}
let clearTimer = false;
if(rc_nav_end<80){ // <=63s
let cTime = Date.now();
if(cTime - mtc_nr_comments<1270) return;
if(!mtc_nr_comments || cTime - mtc_nr_comments>2870){
clearTimer = true; // last check
}
}else{
clearTimer = true;
}
if(clearTimer && cid_nav_end>0) {
clearInterval(cid_nav_end)
cid_nav_end = 0;
}
},800)
initializerResolve();
}
}
function setToActiveTab(defaultTab) {
if (isTheater() && isWideScreenWithTwoColumns()) return;
const jElm = document.querySelector(`a[userscript-tab-content="${switchTabActivity_lastTab}"]:not(.tab-btn-hidden)`) ||
document.querySelector(`a[userscript-tab-content="${(defaultTab||settings.defaultTab)}"]:not(.tab-btn-hidden)`) ||
document.querySelector("a[userscript-tab-content]:not(.tab-btn-hidden)") ||
null;
switchTabActivity(jElm);
return !!jElm;
}
function getWrapper(wrapperId) {
let $wrapper = $(`#${wrapperId}`);
if (!$wrapper[0]) $wrapper = $(``)
return $wrapper.first();
}
function runAfterTabAppended() {
document.documentElement.setAttribute('plugin-tabview-youtube', '')
const ytdFlexyElm = kRef(ytdFlexy)
if(!ytdFlexyElm) return;
if (!ytdFlexyElm.hasAttribute('tabview-selection')) ytdFlexyElm.setAttribute('tabview-selection', '')
// append the next videos
// it exists as "related" is already here
fixTabs();
setToActiveTab(); // just switch to the default tab
prepareTabBtn();
mtoFlexyAttr.clear(true)
mtf_checkFlexy()
document.querySelector("#right-tabs .tab-content").addEventListener('scroll', windowScroll, true);
// for automcomplete plugin or other userscript plugins
// document.body for Firefox >= 60
mtoMutation_body.bindElement(document.querySelector('body'), {
childList: true,
subtree: false,
attributes: false
})
}
function fetchCommentsFinished() {
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm) return;
if(mtf_forceCheckLiveVideo_disable===2) return;
akAttr(ytdFlexyElm, 'tabview-youtube-comments', false, 'LS')
_console.log(2909,1)
}
function setCommentSection( /** @type {number} */ value) {
Q.comments_section_loaded = value;
}
function emptyCommentSection(){
let tab_btn = document.querySelector('.tab-btn[userscript-tab-content="#tab-comments"]')
if(tab_btn){
let span = querySelectorFromAnchor.call(tab_btn,'span#tab3-txt-loader');
tab_btn.removeAttribute('loaded-comment')
if(span){
span.textContent='';
}
}
setCommentSection(0);
}
function _disableComments() {
_console.log(2909,1)
if (!scriptEnable) return;
let cssElm = kRef(ytdFlexy);
if (!cssElm) return;
_console.log(2909,2)
let comments = document.querySelector('ytd-comments#comments')
if(mtf_forceCheckLiveVideo_disable===2){
// earlier than DOM change
}else{
if(comments && !comments.hasAttribute('hidden')) return; // visible comments content)
}
if(Q.comments_section_loaded===2) return; //already disabled
_console.log(2909,3)
_console.log(2909,4)
setCommentSection(2);
_console.log(2909,5)
let tabBtn = document.querySelector('.tab-btn[userscript-tab-content="#tab-comments"]');
if(tabBtn) {
let span = querySelectorFromAnchor.call(tabBtn,'span#tab3-txt-loader');
tabBtn.removeAttribute('loaded-comment')
if (!tabBtn.classList.contains('tab-btn-hidden')) {
//console.log('hide', comments, comments && comments.hasAttribute('hidden'))
hideTabBtn(tabBtn)
}
if(span){
span.textContent='';
}
}
akAttr(cssElm, 'tabview-youtube-comments', true, 'D');
_console.log(2909,10)
}
let layoutStatusMutex = new Mutex();
function forceDisplayChatReplay() {
let items = chatFrameElement('yt-live-chat-item-list-renderer #items');
if (items && items.childElementCount !== 0) return;
let ytd_player = document.querySelector('ytd-player#ytd-player');
if (!ytd_player) return;
let videoElm = querySelectorFromAnchor.call(ytd_player,'video');
if (!videoElm) return;
let video = videoElm;
if (videoElm && video.currentTime > 0 && !video.ended && video.readyState > video.HAVE_CURRENT_DATA) {
let chat = document.querySelector('ytd-live-chat-frame#chat');
if (chat) {
nativeFunc(chat, "postToContentWindow", [{ "yt-player-video-progress": videoElm.currentTime }])
}
}
}
function addIframeStyle(cDoc){
if(cDoc.querySelector('#userscript-tabview-chatroom-css')) return false;
addStyle(`
body #input-panel.yt-live-chat-renderer::after {
background: transparent;
}
.style-scope.yt-live-chat-item-list-renderer{
box-sizing: border-box;
}
yt-live-chat-text-message-renderer:nth-last-child(-n+30):hover #menu.yt-live-chat-text-message-renderer{
transition-delay: 87ms;
}
yt-live-chat-text-message-renderer #menu.yt-live-chat-text-message-renderer{
transition-delay: 1ms;
}
#item.style-scope.yt-live-chat-item-list-renderer, #item-scroller.style-scope.yt-live-chat-item-list-renderer{
transition-delay: 42ms;
}
yt-live-chat-item-list-renderer img[alt] {
pointer-events: auto;
}
body yt-live-chat-item-list-renderer img[alt] ~ tp-yt-paper-tooltip, body yt-live-chat-item-list-renderer #image ~ tp-yt-paper-tooltip {
--paper-tooltip-delay-in: 120ms !important;
white-space: nowrap;
}
#items.style-scope.yt-live-chat-item-list-renderer > yt-live-chat-text-message-renderer.yt-live-chat-item-list-renderer{
--tabview-chat-message-display: block;
--tabview-chat-message-mt: 2px;
--tabview-chat-message-mb: 4px;
}
#message.yt-live-chat-text-message-renderer {
display: var(--tabview-chat-message-display);
margin-top: var(--tabview-chat-message-mt);
margin-bottom: var(--tabview-chat-message-mb);
}
[collapsed] #message.yt-live-chat-text-message-renderer{
--tabview-chat-message-display: 'VOID';
--tabview-chat-message-mt: 'VOID';
--tabview-chat-message-mb: 'VOID';
}
@supports (contain: layout paint style){
body yt-live-chat-app{
contain: size layout paint style;
content-visibility: auto;
transform: translate3d(0,0,0);
overflow: hidden;
}
#items.style-scope.yt-live-chat-item-list-renderer,
#item-offset.style-scope.yt-live-chat-item-list-renderer {
contain: layout paint style;
}
#item-scroller.style-scope.yt-live-chat-item-list-renderer{
contain: size style;
}
#contents.style-scope.yt-live-chat-item-list-renderer,
#chat.style-scope.yt-live-chat-renderer,
img.style-scope.yt-img-shadow[width][height]{
contain: size layout paint style;
}
.style-scope.yt-live-chat-ticker-renderer[role="button"][aria-label],
.style-scope.yt-live-chat-ticker-renderer[role="button"][aria-label] > #container {
contain: layout paint style;
}
yt-img-shadow#author-photo.style-scope{
contain: layout paint style;
content-visibility: auto;
contain-intrinsic-size: 24px 24px;
}
yt-live-chat-text-message-renderer:not([author-is-owner]) #author-photo.style-scope.yt-live-chat-text-message-renderer, yt-live-chat-text-message-renderer:not([author-is-owner]) yt-live-chat-author-chip.style-scope.yt-live-chat-text-message-renderer{
pointer-events: none;
}
yt-live-chat-text-message-renderer:not([author-is-owner]) span#message.style-scope.yt-live-chat-text-message-renderer > img.emoji.yt-formatted-string.style-scope.yt-live-chat-text-message-renderer {
cursor: default;
}
yt-live-chat-text-message-renderer:not([author-is-owner]) span#message.style-scope.yt-live-chat-text-message-renderer,
yt-live-chat-paid-message-renderer #message.yt-live-chat-paid-message-renderer,
yt-live-chat-text-message-renderer:not([author-is-owner]) #timestamp.style-scope.yt-live-chat-text-message-renderer,
yt-live-chat-membership-item-renderer #header-content.style-scope.yt-live-chat-membership-item-renderer,
yt-live-chat-membership-item-renderer #timestamp.style-scope.yt-live-chat-membership-item-renderer,
yt-live-chat-paid-message-renderer #header-content.yt-live-chat-paid-message-renderer,
yt-live-chat-paid-message-renderer #timestamp.style-scope.yt-live-chat-paid-message-renderer,
yt-live-chat-paid-sticker-renderer #content.style-scope.yt-live-chat-paid-sticker-renderer,
yt-live-chat-paid-sticker-renderer #timestamp.style-scope.yt-live-chat-paid-sticker-renderer {
cursor: default;
pointer-events: none;
}
yt-live-chat-text-message-renderer.style-scope.yt-live-chat-item-list-renderer,
yt-live-chat-membership-item-renderer.style-scope.yt-live-chat-item-list-renderer,
yt-live-chat-paid-message-renderer.style-scope.yt-live-chat-item-list-renderer,
yt-live-chat-banner-manager.style-scope.yt-live-chat-item-list-renderer {
contain: layout style;
}
tp-yt-paper-tooltip[style*="inset"][role="tooltip"] {
contain: layout paint style;
}
}
#chat-messages tp-yt-iron-dropdown#dropdown.style-scope.tp-yt-paper-menu-button{
margin-right: var(--ytd-margin-12x);
}
`, cDoc.documentElement).id='userscript-tabview-chatroom-css'
return true;
}
let _last_iframe = null;
function callFind(cDoc){
_last_iframe = cDoc;
if(!cDoc)return;
if(addIframeStyle(cDoc)===false)return;
let frc= 0;
let cid = 0;
let fullReady = ()=>{
if(!cDoc.documentElement.hasAttribute('style') && ++frc<900) return;
clearInterval(cid);
if (!scriptEnable || !isChatExpand()) return;
let iframe = document.querySelector('ytd-live-chat-frame iframe#chatframe');
if(!document.contains(iframe)) return;
if (cDoc.querySelector('yt-live-chat-renderer #continuations')) {
$(document.querySelector('ytd-live-chat-frame#chat')).attr('yt-userscript-iframe-loaded', '')
}
forceDisplayChatReplay();
iframe.dispatchEvent(new CustomEvent("tabview-chatroom-ready"))
}
cid = setInterval(fullReady,10)
fullReady();
}
function flexyAttr_toggleFlag(mFlag, b, flag) {
return b ? (mFlag | flag) : (mFlag & ~flag) ;
}
function flexAttr_toLayoutStatus(nls, attributeName) {
let attrElm, b, v;
switch (attributeName) {
case 'theater':
b = isTheater();
nls = flexyAttr_toggleFlag(nls, b, LAYOUT_THEATER);
break;
case 'userscript-chat-collapsed':
case 'userscript-chatblock':
attrElm = kRef(ytdFlexy);
if (hasAttribute(attrElm, 'userscript-chat-collapsed')) {
nls = flexyAttr_toggleFlag(nls, true, LAYOUT_CHATROOM | LAYOUT_CHATROOM_COLLASPED);
} else {
nls = flexyAttr_toggleFlag(nls, hasAttribute(attrElm, 'userscript-chatblock'), LAYOUT_CHATROOM);
nls = flexyAttr_toggleFlag(nls, false, LAYOUT_CHATROOM_COLLASPED);
}
break;
case 'is-two-columns_':
b = isWideScreenWithTwoColumns();
nls = flexyAttr_toggleFlag(nls, b, LAYOUT_TWO_COLUMNS);
break;
case 'tabview-selection':
attrElm = kRef(ytdFlexy);
b = isNonEmptyString(attrElm.getAttribute('tabview-selection'));
nls = flexyAttr_toggleFlag(nls, b, LAYOUT_TAB_EXPANDED);
break;
case 'fullscreen':
attrElm = kRef(ytdFlexy);
b = attrElm.hasAttribute('fullscreen');
nls = flexyAttr_toggleFlag(nls, b, LAYOUT_FULLSCREEN);
break;
case 'userscript-engagement-panel':
attrElm = kRef(ytdFlexy);
v = attrElm.getAttribute('userscript-engagement-panel');
b = (+v > 0)
nls = flexyAttr_toggleFlag(nls, b, LAYOUT_ENGAGEMENT_PANEL_EXPAND);
break;
}
return nls;
}
let mtf_attrFlexy = (mutations, observer) => {
//attr mutation checker - $$ytdFlexyElm$$ {ytd-watch-flexy} \single
//::attr
// ~ 'userscript-chat-collapsed', 'userscript-chatblock', 'theater', 'is-two-columns_',
// ~ 'tabview-selection', 'fullscreen', 'userscript-engagement-panel',
// ~ 'hidden'
//console.log(15330, scriptEnable, kRef(ytdFlexy), mutations)
if (!scriptEnable) return;
const cssElm = kRef(ytdFlexy)
if (!cssElm) return;
if (!mutations) return;
const old_layoutStatus = wls.layoutStatus
if (old_layoutStatus === 0) return;
let new_layoutStatus = old_layoutStatus;
let checkedChat = false;
for (const mutation of mutations) {
new_layoutStatus = flexAttr_toLayoutStatus(new_layoutStatus, mutation.attributeName);
if (mutation.attributeName == 'userscript-chat-collapsed' || mutation.attributeName == 'userscript-chatblock') {
if(!checkedChat){
checkedChat = true; // avoid double call
if (cssElm.getAttribute('userscript-chatblock') === 'chat-live') {
// assigned new attribute - "chat-live" => disable comments section
_disableComments();
}
if (!cssElm.hasAttribute('userscript-chatblock')) {
// might or might not collapsed before
timeline.setTimeout(() => {
if (!scriptEnable) return;
//delayed call => check with the "no active focus" condition with chatroom status
if (!isAnyActiveTab() && !isChatExpand() && !isTheater() && isWideScreenWithTwoColumns() && !isFullScreen()) {
setToActiveTab();
}
}, 240);
}
}
}else if(mutation.attributeName == 'userscript-engagement-panel'){
// assume any other active component such as tab content and chatroom
if (+(cssElm.getAttribute('userscript-engagement-panel')||0)===0 && +mutation.oldValue>0) {
timeline.setTimeout(() => {
if (!scriptEnable) return;
//delayed call => check with the "no active focus" condition with engagement panel status
if (!isAnyActiveTab() && !isEngagementPanelExpanded() && !isTheater() && isWideScreenWithTwoColumns() && !isFullScreen() && !isChatExpand()) {
setToActiveTab();
}
}, 240);
}
}
}
new_layoutStatus = fixLayoutStatus(new_layoutStatus);
if (new_layoutStatus !== old_layoutStatus) {
wls.layoutStatus = new_layoutStatus
// resize => is-two-columns_
if (((new_layoutStatus ^ old_layoutStatus) & LAYOUT_TWO_COLUMNS) === LAYOUT_TWO_COLUMNS) {
requestAnimationFrame(() => {
pageCheck();
singleColumnScrolling(true); //initalize sticky
})
}
}
}
const mtf_checkFlexy = () => {
// once per $$native-ytd-watch-flexy$$ {ytd-watch-flexy} detection
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm) return true;
wls.layoutStatus = 0;
let isFlexyHidden = (ytdFlexyElm.hasAttribute('hidden'));
if (!isFlexyHidden) {
let rChatExist = base_ChatExist();
if (rChatExist) {
let { attr_chatblock, attr_chatcollapsed } = rChatExist;
if (attr_chatblock === null) {
//remove attribute if it is unknown
attr_chatblock = false;
attr_chatcollapsed = false;
}
wAttr(ytdFlexyElm, 'userscript-chatblock', attr_chatblock)
_console.log(322,ytdFlexyElm.hasAttribute('userscript-chatblock'))
wAttr(ytdFlexyElm, 'userscript-chat-collapsed', attr_chatcollapsed)
}
}
let rTabSelection = [...querySelectorAllFromAnchor.call(ytdFlexyElm,'.tab-btn[userscript-tab-content]')]
.map(elm => ({ elm, hidden: elm.classList.contains('tab-btn-hidden') }));
if(rTabSelection.length === 0){
wAttr(ytdFlexyElm, 'tabview-selection', false);
}else{
rTabSelection=rTabSelection.filter(entry => entry.hidden !== true); // all available tabs
if (rTabSelection.length === 0) wAttr(ytdFlexyElm, 'tabview-selection', '');
}
rTabSelection = null;
let rEP = engagement_panels_();
if (rEP && rEP.count > 0) wAttr(ytdFlexyElm, 'userscript-engagement-panel', false);
//else wAttr(ytdFlexyElm, 'userscript-engagement-panel', rEP.value + ""); // can be 0
else if(rEP.value>0) wAttr(ytdFlexyElm, 'userscript-engagement-panel', rEP.value + ""); // can be 0
let ls = LAYOUT_VAILD;
ls = flexAttr_toLayoutStatus(ls, 'theater')
ls = flexAttr_toLayoutStatus(ls, 'userscript-chat-collapsed')
ls = flexAttr_toLayoutStatus(ls, 'userscript-chatblock')
ls = flexAttr_toLayoutStatus(ls, 'is-two-columns_')
ls = flexAttr_toLayoutStatus(ls, 'tabview-selection')
ls = flexAttr_toLayoutStatus(ls, 'fullscreen')
ls = flexAttr_toLayoutStatus(ls, 'userscript-engagement-panel')
fixLayoutStatus(ls)
wls.layoutStatus = ls
mtoFlexyAttr.bindElement(ytdFlexyElm,{
attributes: true,
attributeFilter: ['userscript-chat-collapsed', 'userscript-chatblock', 'theater', 'is-two-columns_', 'tabview-selection', 'fullscreen', 'userscript-engagement-panel', 'hidden'],
attributeOldValue: true
})
let columns = document.querySelector('ytd-page-manager#page-manager #columns')
if (columns) {
wAttr(columns, 'userscript-scrollbar-render', true);
}
return false;
}
function checkVisibleEngagementPanel(){
if(storeLastPanel){
let elm_storeLastPanel = kRef(storeLastPanel);
if(elm_storeLastPanel && !isDOMVisible(elm_storeLastPanel) ){
storeLastPanel=null;
ytBtnCloseEngagementPanels();
}
}
}
let switchTabActivity_lastTab = null
function setDisplayedPlaylist() {
//override the default youtube coding event prevention
let cssElm = kRef(ytdFlexy);
if (!scriptEnable || !cssElm) return;
displayedPlaylist = mWeakRef(document.querySelector('ytd-watch-flexy #tab-list:not(.tab-content-hidden) ytd-playlist-panel-renderer[tabview-true-playlist]') || null);
}
function switchTabActivity(activeLink) {
if (!scriptEnable) return;
const ytdFlexyElm = kRef(ytdFlexy);
if (!ytdFlexyElm) return;
if (activeLink && activeLink.classList.contains('tab-btn-hidden')) return; // not allow to switch to hide tab
//if (isTheater() && isWideScreenWithTwoColumns()) activeLink = null;
function runAtEnd() {
//console.log(12312)
if (activeLink) {
lstTab.lastTab = activeLink.getAttribute('userscript-tab-content')
lstTab.lastPanel = null;
}
displayedPlaylist = null;
scrollingVideosList = null;
if (activeLink && lstTab.lastTab == '#tab-list') {
setDisplayedPlaylist();
} else if (activeLink && lstTab.lastTab == '#tab-videos') {
scrollingVideosList = mWeakRef(document.querySelector('ytd-watch-flexy #tab-videos:not(.tab-content-hidden) [placeholder-videos]'));
}
ytdFlexyElm.setAttribute('tabview-selection', activeLink ? lstTab.lastTab : '')
if (activeLink && lstTab.lastTab == '#tab-comments' && (ytdFlexyElm.getAttribute('tabview-youtube-comments') || '').lastIndexOf('S') >= 0) {
if(mtf_forceCheckLiveVideo_disable===2) {}
else{
akAttr(ytdFlexyElm, 'tabview-youtube-comments', false, 'L');
_console.log(2909,3)
}
}
}
const links = document.querySelectorAll('#material-tabs a[userscript-tab-content]');
//console.log(701, activeLink)
for (const link of links) {
let content = document.querySelector(link.getAttribute('userscript-tab-content'));
if (link && content) {
if (link !== activeLink) {
link.classList.remove("active");
content.classList.add("tab-content-hidden");
} else {
//console.log(3343)
link.classList.add("active");
content.classList.remove("tab-content-hidden");
//timeline.setTimeout(()=>content.focus(),400);
}
}
}
runAtEnd();
}
const STORE_VERSION = 1;
const STORE_key = 'userscript-tabview-settings';
function getStore(){
let s = localStorage[STORE_key];
function resetStore(){
let ret = {
version: 1,
};
localStorage[STORE_key]=JSON.stringify(ret);
return ret;
}
if(!s) return resetStore();
let obj = null;
try{
obj = JSON.parse(s);
}catch(e){}
return obj && obj.version === STORE_VERSION ? obj : resetStore();
}
function setStore(obj){
if(!obj || typeof obj !=='object') return false;
if(obj.version !== STORE_VERSION) return false;
localStorage[STORE_key]=JSON.stringify(obj);
return true;
}
let tabsUiScript_setclick = false;
function prepareTabBtn() {
const materialTab = document.querySelector("#material-tabs")
if (!materialTab) return;
let noActiveTab = !!document.querySelector('ytd-watch-flexy[userscript-chatblock]:not([userscript-chat-collapsed])')
const activeLink = querySelectorFromAnchor.call(materialTab,'a[userscript-tab-content].active:not(.tab-btn-hidden)')
if (activeLink) switchTabActivity(noActiveTab ? null : activeLink)
if (!tabsUiScript_setclick) {
tabsUiScript_setclick = true;
$(materialTab).on("click", "a", function(evt) {
//console.log(8510)
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm) return null;
if (!this.hasAttribute('userscript-tab-content')) return;
if (evt.target.matches('.font-size-btn')) return;
evt.preventDefault();
//console.log(8511)
//console.log(8513)
new Promise(requestAnimationFrame).then(() => {
//console.log(8514)
layoutStatusMutex.lockWith(unlock => {
//console.log(8515)
switchTabActivity_lastTab = this.getAttribute('userscript-tab-content');
let isActiveAndVisible = this.classList.contains('tab-btn') && this.classList.contains('active') && !this.classList.contains('tab-btn-hidden')
if( isFullScreen() ){
console.log(isActiveAndVisible, this)
if(isActiveAndVisible){
timeline.setTimeout(unlock, 80);
switchTabActivity(null);
}else{
if (isChatExpand() && isWideScreenWithTwoColumns()) {
ytBtnCollapseChat();
} else if (isEngagementPanelExpanded() && isWideScreenWithTwoColumns()) {
ytBtnCloseEngagementPanels();
}
timeline.setTimeout(() => {
let scrollElement = document.querySelector('ytd-app[scrolling]')
if(!scrollElement) return;
// single column view; click button; scroll to tab content area 100%
let rightTabs = document.querySelector('#right-tabs');
let pTop = rightTabs.getBoundingClientRect().top - scrollElement.getBoundingClientRect().top
if (rightTabs && pTop > 0 && this.classList.contains('active')) {
rightTabs.scrollIntoView(true);
}
}, 60)
timeline.setTimeout(unlock, 80);
switchTabActivity(this)
}
} else if (isWideScreenWithTwoColumns() && !isTheater() && isActiveAndVisible) {
//optional
timeline.setTimeout(unlock, 80);
switchTabActivity(null);
ytBtnSetTheater();
} else if (isActiveAndVisible) {
timeline.setTimeout(unlock, 80);
switchTabActivity(null);
} else {
if (isChatExpand() && isWideScreenWithTwoColumns()) {
ytBtnCollapseChat();
} else if (isEngagementPanelExpanded() && isWideScreenWithTwoColumns()) {
ytBtnCloseEngagementPanels();
} else if (isWideScreenWithTwoColumns() && isTheater() && !isFullScreen()) {
ytBtnCancelTheater();
}
timeline.setTimeout(() => {
// single column view; click button; scroll to tab content area 100%
let rightTabs = document.querySelector('#right-tabs');
if (!isWideScreenWithTwoColumns() && rightTabs && rightTabs.offsetTop > 0 && this.classList.contains('active')) {
let tabButtonBar = document.querySelector('#material-tabs');
let tabButtonBarHeight = tabButtonBar ? tabButtonBar.offsetHeight : 0;
window.scrollTo(0, rightTabs.offsetTop - tabButtonBarHeight);
}
}, 60)
// _console.log(8519)
timeline.setTimeout(unlock, 80)
switchTabActivity(this)
}
})
})
});
function updateCSS_fontsize(){
let store = getStore();
let ytdFlexyElm = kRef(ytdFlexy);
if(ytdFlexyElm){
if(store['font-size-#tab-info'])
ytdFlexyElm.style.setProperty('--ut2257-info', store['font-size-#tab-info'])
if(store['font-size-#tab-comments'])
ytdFlexyElm.style.setProperty('--ut2257-comments', store['font-size-#tab-comments'])
if(store['font-size-#tab-videos'])
ytdFlexyElm.style.setProperty('--ut2257-videos', store['font-size-#tab-videos'])
if(store['font-size-#tab-list'])
ytdFlexyElm.style.setProperty('--ut2257-list', store['font-size-#tab-list'])
}
}
$(materialTab).on("click", ".font-size-btn", function(evt){
evt.preventDefault();
evt.stopPropagation();
evt.stopImmediatePropagation();
let value = evt.target.matches('.font-size-plus')?1: evt.target.matches('.font-size-minus')?-1 :0;
let active_tab_content = closestDOM.call(evt.target,'[userscript-tab-content]').getAttribute('userscript-tab-content');
let store = getStore();
let settingKey = `font-size-${active_tab_content}`
if(!store[settingKey]) store[settingKey] = 1.0;
if(value<0) store[settingKey] -= 0.05;
else if(value>0) store[settingKey] += 0.05;
if(store[settingKey]<0.1) store[settingKey] = 0.1;
else if(store[settingKey]>10) store[settingKey] = 10.0;
setStore(store);
updateCSS_fontsize();
//console.log(this.textContent)
});
updateCSS_fontsize();
}
}
// ---------------------------------------------------------------------------------------------
document.addEventListener("yt-navigate-finish", onNavigationEnd)
//yt-navigate-redirect
//"yt-page-data-fetched"
//yt-navigate-error
//yt-navigate-start
//yt-page-manager-navigate-start
//"yt-navigate"
//"yt-navigate-cache
document.addEventListener("yt-navigate-cache",()=>{
console.log('yt-navigate-cache')
})
document.addEventListener("yt-navigate-redirect",()=>{
console.log('yt-navigate-redirect')
script_inject_js1.inject();
})
function onReady(){
//might be earlier than yt-navigation-finish
console.log('html-onReady')
if(location.pathname=='/watch') script_inject_js1.inject();
}
if(document.readyState!=='loading'){
onReady();
}else{
document.addEventListener('DOMContentLoaded', onReady)
}
function forceConfig(){
}
document.addEventListener("yt-navigate-start",()=>{
console.log('yt-navigate-start') // not always trigger before navigate-end
script_inject_js1.inject();
forceConfig();
})
document.addEventListener("yt-page-manager-navigate-start",()=>{
console.log('yt-page-manager-navigate-start')
forceConfig();
})
// ---------------------------------------------------------------------------------------------
let scrolling_lastD = 0;
const singleColumnScrolling = function(/** @type {boolean} */ scrolling_lastF) {
if (!scriptEnable || pageType!=='watch') return;
let pageY = 0;
if(scrolling_lastD === 0){
if( (wls.layoutStatus & LAYOUT_TWO_COLUMNS) === LAYOUT_TWO_COLUMNS){
return;
}
pageY = scrollY;
if (pageY < 10 && !scrolling_lastF) return;
}else{
pageY = scrollY;
}
let targetElm, header, navElm;
_console.log(7891,'scrolling')
Promise.resolve().then(() => {
targetElm = document.querySelector("#right-tabs");
if (!targetElm) return;
header = querySelectorFromAnchor.call(targetElm,"header");
if (!header) return;
navElm = document.querySelector('#masthead-container, #masthead')
if (!navElm) return;
let navHeight = navElm ? navElm.offsetHeight : 0
let elmY = targetElm.offsetTop
let xyz = [elmY + navHeight, pageY, elmY - navHeight]
let xyStatus = 0
if (xyz[1] < xyz[2] && xyz[2] < xyz[0]) {
// 1
xyStatus = 1
}
if (xyz[0] > xyz[1] && xyz[1] > xyz[2]) {
//2
xyStatus = 2
}
if (xyz[2] < xyz[0] && xyz[0] < xyz[1]) {
// 3
xyStatus = 3
}
return xyStatus;
}).then((xyStatus) => {
if ((xyStatus == 2 || xyStatus == 3) && (scrolling_lastD === 0 || scrolling_lastF)) {
scrolling_lastD = 1;
let {
offsetHeight
} = header
let {
offsetWidth
} = targetElm
targetElm.style.setProperty("--userscript-sticky-width", offsetWidth + 'px')
targetElm.style.setProperty("--userscript-sticky", offsetHeight + 'px')
wAttr(targetElm, 'userscript-sticky', true);
} else if ((xyStatus == 1) && (scrolling_lastD === 1 || scrolling_lastF)) {
scrolling_lastD = 0;
wAttr(targetElm, 'userscript-sticky', false);
}
targetElm = null;
header = null;
navElm = null;
});
};
window.addEventListener("scroll", function() {
singleColumnScrolling(false)
}, {
capture: false,
passive: true
})
//let lastResizeAt = 0;
window.addEventListener('resize', function() {
if (!scriptEnable) return;
if (pageType!=='watch') return;
//lastResizeAt = Date.now();
if((wls.layoutStatus & LAYOUT_TWO_COLUMNS) !== LAYOUT_TWO_COLUMNS){
requestAnimationFrame(() => {
singleColumnScrolling(true)
})
}
}, {
capture: false,
passive: true
})
function resetBuggyLayoutForNewVideoPage() {
let ytdFlexyElm = kRef(ytdFlexy);
if (!ytdFlexyElm) return;
//(flexy is visible and watch video page)
scriptEnable = true;
//mtf_forceCheckLiveVideo_disable = 0;
_console.log(27056)
let new_layoutStatus = wls.layoutStatus
new_layoutStatus & (LAYOUT_CHATROOM_COLLASPED | LAYOUT_CHATROOM)
const new_isExpandedChat = !(new_layoutStatus & LAYOUT_CHATROOM_COLLASPED) && (new_layoutStatus & LAYOUT_CHATROOM)
const new_isCollaspedChat = (new_layoutStatus & LAYOUT_CHATROOM_COLLASPED) && (new_layoutStatus & LAYOUT_CHATROOM)
const new_isTwoColumns = new_layoutStatus & LAYOUT_TWO_COLUMNS;
const new_isTheater = new_layoutStatus & LAYOUT_THEATER;
const new_isTabExpanded = new_layoutStatus & LAYOUT_TAB_EXPANDED;
const new_isFullScreen = new_layoutStatus & LAYOUT_FULLSCREEN;
const new_isExpandEPanel = new_layoutStatus & LAYOUT_ENGAGEMENT_PANEL_EXPAND;
if (ytdFlexyElm.getAttribute('tabview-selection') === '' && new_isTwoColumns && !new_isTheater && !new_isTabExpanded && !new_isFullScreen && !new_isExpandEPanel && !new_isExpandedChat) {
// e.g. engage panel removed after miniview and change video
setToActiveTab();
} else if (new_isExpandEPanel && querySelectorAllFromAnchor.call(ytdFlexyElm, 'ytd-engagement-panel-section-list-renderer[visibility="ENGAGEMENT_PANEL_VISIBILITY_EXPANDED"]').length === 0) {
wls.layoutStatus = new_layoutStatus & (~LAYOUT_ENGAGEMENT_PANEL_EXPAND)
}
}
function newVideoPage() {
console.log('newVideoPage')
pendingFetch = null
fetched = false;
document.dispatchEvent(new CustomEvent('tabview-v-change')) //possible duplicated
//console.log('newVideoPage-', 150, location.href)
let ytdFlexyElm = kRef(ytdFlexy);
if (!ytdFlexyElm) return;
timeline.reset();
layoutStatusMutex = new Mutex();
//console.log('newVideoPage-', 350, location.href)
if (pageType === 'watch') {
resetBuggyLayoutForNewVideoPage();
}
pendingFetch = null;
_innerCommentsLoader();
if (!fetched) {
window.dispatchEvent(new Event("scroll"));
}
let liveChatRenderer = null;
let isReplay = null;
try {
liveChatRenderer = evt.detail.pageData.response.contents.twoColumnWatchNextResults.conversationBar.liveChatRenderer
} catch (e) { }
if (liveChatRenderer) {
if (liveChatRenderer.isReplay === true) isReplay = true;
}
const chatBlockR = liveChatRenderer ? (isReplay ? 3 : 1) : 0
const initialDisplayState = liveChatRenderer ? liveChatRenderer.initialDisplayState : null;
let f = () => {
_console.log(932, 1, 1)
let ytdFlexyElm = kRef(ytdFlexy);
if (!scriptEnable || !ytdFlexyElm) return;
_console.log(932, 1, 2)
if (pageType !== 'watch') return;
_console.log(932, 1, 3)
if (Q.mtf_chatBlockQ !== chatBlockR) {
Q.mtf_chatBlockQ = chatBlockR
let [attr_chatblock, attr_chatcollapsed] = (chatBlockR & 1) ? [
chatBlockR === 1 ? 'chat-live' : chatBlockR === 3 ? 'chat-playback' : false,
initialDisplayState === 'LIVE_CHAT_DISPLAY_STATE_COLLAPSED' ? true : false
] : [false, false];
_console.log(932, 2, attr_chatblock, attr_chatcollapsed)
//LIVE_CHAT_DISPLAY_STATE_COLLAPSED
//LIVE_CHAT_DISPLAY_STATE_EXPANDED
wAttr(ytdFlexyElm, 'userscript-chatblock', attr_chatblock)
wAttr(ytdFlexyElm, 'userscript-chat-collapsed', attr_chatcollapsed)
_console.log(932, 3, ytdFlexyElm.hasAttribute('userscript-chatblock'))
if (pageType === 'watch') { // reset info when hidden
checkVisibleEngagementPanel();
}
if (attr_chatblock == 'chat-live') {
_console.log(932, 4)
mtf_forceCheckLiveVideo_disable = 2;
_disableComments();
} else {
_console.log(932, 5)
mtf_forceCheckLiveVideo_disable = 0;
setCommentSection(0);
restoreFetching();
_console.log(932, 6, mtf_forceCheckLiveVideo_disable)
if (mtf_forceCheckLiveVideo_disable !== 2) {
const tabBtn = document.querySelector('[userscript-tab-content="#tab-comments"].tab-btn-hidden')
if (tabBtn) {
emptyCommentSection();
tabBtn.classList.remove("tab-btn-hidden")
}
}
}
} else {
// restore Fetching only
let [attr_chatblock, attr_chatcollapsed] = (chatBlockR & 1) ? [
chatBlockR === 1 ? 'chat-live' : chatBlockR === 3 ? 'chat-playback' : false,
initialDisplayState === 'LIVE_CHAT_DISPLAY_STATE_COLLAPSED' ? true : false
] : [false, false];
if (mtf_forceCheckLiveVideo_disable !== 2 && (attr_chatblock === false || attr_chatblock === 'chat-playback')) {
setCommentSection(0);
restoreFetching();
}
}
}
f();
}
document.addEventListener('wheel', function(evt) {
if (!scriptEnable) return;
const displayedPlaylist_element = kRef(displayedPlaylist);
if (displayedPlaylist_element && elementContains.call(displayedPlaylist_element, evt.target)) {
evt.stopPropagation();
evt.stopImmediatePropagation();
}
}, { capture: true, passive: true });
function setVideosTwoColumns(/** @type {number} */ flag, /** @type {boolean} */ bool) {
//two columns to one column
/*
[placeholder-videos] ytd-watch-next-secondary-results-renderer.style-scope.ytd-watch-flexy
is-two-columns ="" => no is-two-columns
[placeholder-videos] tp-yt-paper-spinner#spinner.style-scope.ytd-continuation-item-renderer
no hidden => hidden =""
[placeholder-videos] div#button.style-scope.ytd-continuation-item-renderer
hidden ="" => no hidden
*/
let cssSelector1 = '[placeholder-videos] ytd-watch-next-secondary-results-renderer.style-scope.ytd-watch-flexy'
let cssSelector2 = '[placeholder-videos] tp-yt-paper-spinner#spinner.style-scope.ytd-continuation-item-renderer'
let cssSelector3 = '[placeholder-videos] div#button.style-scope.ytd-continuation-item-renderer'
let res = {}
if (flag & 1) {
res.m1 = document.querySelector(cssSelector1)
if (res.m1) wAttr(res.m1, 'is-two-columns', bool ? '' : false);
}
if (flag & 2) {
res.m2 = document.querySelector(cssSelector2)
if (res.m2) wAttr(res.m2, 'hidden', bool ? false : '');
}
if (flag & 4) {
res.m3 = document.querySelector(cssSelector3)
if (res.m3) wAttr(res.m3, 'hidden', bool ? '' : false);
}
return res
}
let lastScrollFetch = 0;
// function isScrolledToEnd(){
// return (window.innerHeight + window.pageYOffset) >= document.scrollingElement.scrollHeight - 2;
// }
let lastOffsetTop = 0;
window.addEventListener('scroll', function(evt) {
//console.log(evt.target)
if (!scriptEnable) return;
let isTwoCol = (wls.layoutStatus & LAYOUT_TWO_COLUMNS) === LAYOUT_TWO_COLUMNS
if(isTwoCol) return;
if (!kRef(scrollingVideosList)) return;
if (videoListBeforeSearch) return;
let visibleHeight = document.scrollingElement.clientHeight;
let totalHeight = document.scrollingElement.scrollHeight;
if (totalHeight < visibleHeight * 1.5) return; // filter out two column view;
let z = window.pageYOffset + visibleHeight;
let h_advanced = totalHeight - (visibleHeight > 5 * 40 ? visibleHeight * 0.5 : 40);
if (z > h_advanced) {
let ct = Date.now();
if (ct - lastScrollFetch < 500) return; //prevent continuous calling
lastScrollFetch = ct;
let res = setVideosTwoColumns(2 | 4, true)
if (res.m3 && res.m2) {
//wait for DOM change, just in case
requestAnimationFrame(() => {
let { offsetTop } = res.m2 // as visibility of m2 & m3 switched.
if (offsetTop - lastOffsetTop < 40) return; // in case bug, or repeating calling. // the next button shall below the this button
lastOffsetTop = offsetTop
res.m2.parentNode.dispatchEvent(new Event('yt-service-request-sent-button-renderer'))
res = null
})
} else {
res = null
}
}
}, { passive: true })
let fetchPendings = [];
let mtf_forceCheckLiveVideo_disable = 0;
let storeLastPanel = null;
let mgChatFrame = {
setVar(elm) {
mgChatFrame.kVar = mWeakRef(elm)
},
getVar() {
return kRef(mgChatFrame.kVar)
},
inPage() {
let elm = mgChatFrame.getVar();
if (!elm) return false;
let ytdFlexyElm = kRef(ytdFlexy);
if(!ytdFlexyElm) return false;
return elementContains.call(ytdFlexyElm, elm)
}
};
const timeline = {
// after initialized (initObserver)
cn1:{},
cn2:{},
setTimeout( /** @type {TimerHandler} */ f,/** @type {number} */ d){
let cid = setTimeout(f,d)
timeline.cn1[cid]=true
return cid;
},
clearTimeout(/** @type {number} */ cid){
timeline.cn1[cid]=false; return clearTimeout(cid)
},
setInterval(/** @type {TimerHandler} */ f,/** @type {number} */ d){
let cid = setInterval(f,d);
timeline.cn2[cid]=true
return cid;
},
clearInterval(/** @type {number} */ cid){
timeline.cn2[cid]=false; return clearInterval(cid)
},
reset(){
for(let cid in timeline.cn1) timeline.cn1[cid] && clearTimeout(cid)
for(let cid in timeline.cn2) timeline.cn2[cid] && clearInterval(cid)
timeline.cn1={}
timeline.cn2={}
}
}
class AttributeMutationObserver extends MutationObserver {
constructor(flist){
super((mutations, observer)=>{
for(const mutation of mutations){
if (mutation.type === 'attributes') {
this.checker(mutation.target, mutation.attributeName)
}
}
})
this.flist=flist;
this.res={}
}
takeRecords(){
super.takeRecords();
}
disconnect(){
this._target = null;
super.disconnect();
}
observe(/** @type {Node} */ target){
if(this._target) return;
//console.log(123124, target)
this._target = mWeakRef(target);
//console.log(123125, kRef(this._target))
const options = {
attributes: true,
attributeFilter: Object.keys(this.flist),
//attributeFilter: [ "status", "username" ],
attributeOldValue: true
}
super.observe(target, options)
}
checker(/** @type {Node} */ target,/** @type {string} */ attributeName){
let nv = target.getAttribute(attributeName);
if(this.res[attributeName]!==nv){
this.res[attributeName] = nv
let f = this.flist[attributeName];
if(f) f(attributeName, nv);
}
}
check(delay = 0){
setTimeout(()=>{
let target = kRef(this._target)
if(target!==null){
for(const key of Object.keys(this.flist)){
this.checker(target,key)
}
}else{
console.log('target is null') //disconnected??
}
target = null;
},delay)
}
}
function goYoutubeGeniusLyrics() {
setTimeout(function $f() {
if (!document.documentElement.hasAttribute('w-engagement-panel-genius-lyrics')) return setTimeout($f, 100)
document.documentElement.dispatchEvent(new CustomEvent('engagement-panel-genius-lyrics'))
}, 100)
}
//Object.keys($0).filter(key=>!(key in $0.constructor.prototype))
//Object.getOwnPropertyNames(window).filter(k=>k.startsWith('HTML'))
//Object.getOwnPropertyNames(window).filter(k=>k.startsWith('HTML')).filter(k=>$0 instanceof window[k])
/* --------------------------- browser's bug in -webkit-box ----------------------------------------- */
/*
fix bug for comment section - version 1.8.7
This issue is the bug in browser's rendering
I guess, this is due to the lines clamp with display:-webkit-box
use stupid coding to let it re-render when its content become visible
/*
ytd-expander[should-use-number-of-lines][collapsed] > #content.ytd-expander {
color: var(--yt-spec-text-primary);
display: -webkit-box;
overflow: hidden;
max-height: none;
-webkit-box-orient: vertical;
-webkit-line-clamp: var(--ytd-expander-max-lines, 4);
}
// v1.8.36 imposed a effective solution for fixing this bug
*/
/* --------------------------- browser's bug in -webkit-box ----------------------------------------- */
/**
*
f.initChildrenObserver=function(){var a=this;this.observer=new MutationObserver(function(){return a.childrenChanged()});
this.observer.observe(this.$.content,{subtree:!0,childList:!0,attributes:!0});this.childrenChanged()};
f.childrenChanged=function(){var a=this;this.alwaysToggleable?this.canToggle=this.alwaysToggleable:this.canToggleJobId||(this.canToggleJobId=window.requestAnimationFrame(function(){$h(function(){a.canToggleJobId=0;a.calculateCanCollapse()})}))};
f.onIronResize=function(){this.recomputeOnResize&&this.childrenChanged()};
onButtonClick_:function(){this.fire("yt-close-upsell-dialog")},
computeHasHeader_:function(a){return!!a.headerBackgroundImage}});var geb;var heb;var ieb;var jeb;var xI=function(){var a=L.apply(this,arguments)||this;a.alignAuto=!1;a.collapsed=!0;a.isToggled=!1;a.alwaysCollapsed=!1;a.canToggle=!0;a.collapsedHeight=80;a.disableToggle=!1;a.alwaysToggleable=!1;a.reversed=!1;a.shouldUseNumberOfLines=!1;a.recomputeOnResize=!1;a.canToggleJobId=0;return a};
n(xI,L);f=xI.prototype;f.alwaysToggleableChanged=function(){this.alwaysToggleable&&(this.canToggle=!0)};
f.calculateCanCollapse=function(){this.canToggle=this.shouldUseNumberOfLines?this.alwaysToggleable||this.$.content.offsetHeightthis.collapsedHeight};
f.detachObserver=function(){this.observer&&this.observer.disconnect()};
*
*
*
*/
})();
// https://github.com/cyfung1031/Tabview-Youtube/raw/main/js/content.js
}
;!(function $$() {
'use strict';
if(document.documentElement==null) return window.requestAnimationFrame($$)
const cssTxt = GM_getResourceText("contentCSS");
function addStyle (styleText) {
const styleNode = document.createElement('style');
styleNode.type = 'text/css';
styleNode.textContent = styleText;
document.documentElement.appendChild(styleNode);
return styleNode;
}
addStyle (cssTxt);
main(window.$);
// Your code here...
})();