您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Make comments and lists into tabs
当前为
- // ==UserScript==
- // @name Tabview Youtube
- // @namespace http://tampermonkey.net/
- // @version 0.6
- // @description Make comments and lists into tabs
- // @author CY Fung
- // @match https://www.youtube.com/watch?v=*
- // @resource contentCSS https://raw.githubusercontent.com/cyfung1031/Tabview-Youtube/b57d5c149caf4b78df3eeb0b9a791af8347d97cb/css/style_content.css
- // @icon https://github.com/cyfung1031/Tabview-Youtube/raw/main/images/icon128p.png
- // @require https://code.jquery.com/jquery-3.6.0.slim.min.js
- // @grant GM_getResourceText
- // @run-at document-start
- // @license MIT https://github.com/cyfung1031/Tabview-Youtube/blob/main/LICENSE
- // ==/UserScript==
- function main($){
- // MIT License
- // https://github.com/cyfung1031/Tabview-Youtube/raw/main/js/content.js
- /**
- * SVG resources:
- * <div>Icons made by <a href="https://www.flaticon.com/authors/smashicons" title="Smashicons">Smashicons</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a></div>
- */
- const scriptVersionForExternal = '2021/07/03';
- const svgComments = `
- <path d="M40.068,13.465L5.93,13.535c-3.27,0-5.93,2.66-5.93,5.93v21.141c0,3.27,2.66,5.929,5.93,5.929H12v10
- c0,0.413,0.254,0.784,0.64,0.933c0.117,0.045,0.239,0.067,0.36,0.067c0.276,0,0.547-0.115,0.74-0.327l9.704-10.675l16.626-0.068
- c3.27,0,5.93-2.66,5.93-5.929V19.395C46,16.125,43.34,13.465,40.068,13.465z M10,23.465h13c0.553,0,1,0.448,1,1s-0.447,1-1,1H10
- c-0.553,0-1-0.448-1-1S9.447,23.465,10,23.465z M36,37.465H10c-0.553,0-1-0.448-1-1s0.447-1,1-1h26c0.553,0,1,0.448,1,1
- S36.553,37.465,36,37.465z M36,31.465H10c-0.553,0-1-0.448-1-1s0.447-1,1-1h26c0.553,0,1,0.448,1,1S36.553,31.465,36,31.465z"/>
- <path d="M54.072,2.535L19.93,2.465c-3.27,0-5.93,2.66-5.93,5.93v3.124l26.064-0.054c4.377,0,7.936,3.557,7.936,7.93v21.07v0.071
- v2.087l3.26,3.586c0.193,0.212,0.464,0.327,0.74,0.327c0.121,0,0.243-0.022,0.36-0.067c0.386-0.149,0.64-0.52,0.64-0.933v-10h1.07
- c3.27,0,5.93-2.66,5.93-5.929V8.465C60,5.195,57.34,2.535,54.072,2.535z"/>
- `
- const svgVideos = `<path d="M298,33c0-13.255-10.745-24-24-24H24C10.745,9,0,19.745,0,33v232c0,13.255,10.745,24,24,24h250c13.255,0,24-10.745,24-24V33
- z M91,39h43v34H91V39z M61,259H30v-34h31V259z M61,73H30V39h31V73z M134,259H91v-34h43V259z M123,176.708v-55.417
- c0-8.25,5.868-11.302,12.77-6.783l40.237,26.272c6.902,4.519,6.958,11.914,0.056,16.434l-40.321,26.277
- C128.84,188.011,123,184.958,123,176.708z M207,259h-43v-34h43V259z M207,73h-43V39h43V73z M268,259h-31v-34h31V259z M268,73h-31V39
- h31V73z"/>`
- const svgInfo = `<path d="M11.812,0C5.289,0,0,5.289,0,11.812s5.289,11.813,11.812,11.813s11.813-5.29,11.813-11.813
- S18.335,0,11.812,0z M14.271,18.307c-0.608,0.24-1.092,0.422-1.455,0.548c-0.362,0.126-0.783,0.189-1.262,0.189
- c-0.736,0-1.309-0.18-1.717-0.539s-0.611-0.814-0.611-1.367c0-0.215,0.015-0.435,0.045-0.659c0.031-0.224,0.08-0.476,0.147-0.759
- l0.761-2.688c0.067-0.258,0.125-0.503,0.171-0.731c0.046-0.23,0.068-0.441,0.068-0.633c0-0.342-0.071-0.582-0.212-0.717
- c-0.143-0.135-0.412-0.201-0.813-0.201c-0.196,0-0.398,0.029-0.605,0.09c-0.205,0.063-0.383,0.12-0.529,0.176l0.201-0.828
- c0.498-0.203,0.975-0.377,1.43-0.521c0.455-0.146,0.885-0.218,1.29-0.218c0.731,0,1.295,0.178,1.692,0.53
- c0.395,0.353,0.594,0.812,0.594,1.376c0,0.117-0.014,0.323-0.041,0.617c-0.027,0.295-0.078,0.564-0.152,0.811l-0.757,2.68
- c-0.062,0.215-0.117,0.461-0.167,0.736c-0.049,0.275-0.073,0.485-0.073,0.626c0,0.356,0.079,0.599,0.239,0.728
- c0.158,0.129,0.435,0.194,0.827,0.194c0.185,0,0.392-0.033,0.626-0.097c0.232-0.064,0.4-0.121,0.506-0.17L14.271,18.307z
- M14.137,7.429c-0.353,0.328-0.778,0.492-1.275,0.492c-0.496,0-0.924-0.164-1.28-0.492c-0.354-0.328-0.533-0.727-0.533-1.193
- c0-0.465,0.18-0.865,0.533-1.196c0.356-0.332,0.784-0.497,1.28-0.497c0.497,0,0.923,0.165,1.275,0.497
- c0.353,0.331,0.53,0.731,0.53,1.196C14.667,6.703,14.49,7.101,14.137,7.429z"/>`
- const svgPlayList = `
- <rect x="0" y="64" width="256" height="42.667"/>
- <rect x="0" y="149.333" width="256" height="42.667"/>
- <rect x="0" y="234.667" width="170.667" height="42.667"/>
- <polygon points="341.333,234.667 341.333,149.333 298.667,149.333 298.667,234.667 213.333,234.667 213.333,277.333
- 298.667,277.333 298.667,362.667 341.333,362.667 341.333,277.333 426.667,277.333 426.667,234.667"/>
- `
- const svgElm = (w, h, vw, vh, p) => `<svg width="${w}" height="${h}" viewBox="0 0 ${vw} ${vh}" preserveAspectRatio="xMidYMid meet">${p}</svg>`
- let settings = {
- toggleSettings: {
- tabs: 1,
- tInfo: 1,
- tComments: 1,
- tVideos: 1,
- },
- defaultTab: "videos"
- };
- const mtoInterval1=40;
- const mtoInterval2=150;
- const clickInterval1=100;
- const clickInterval2=30;
- let mtoInterval = mtoInterval1;
- let clickInterval=clickInterval1;
- function isVideoPlaying(video) {
- return video.currentTime > 0 && !video.paused && !video.ended && video.readyState > video.HAVE_CURRENT_DATA;
- }
- function setAttr(elm, attrName, b){
- if(!elm)return;
- if(b) elm.setAttribute(attrName,''); else elm.removeAttribute(attrName);
- }
- function hideTabBtn($tabBtn){
- var isActiveBefore = $tabBtn.is('.active')
- $tabBtn.addClass("tab-btn-hidden");
- if (isActiveBefore) {
- setToActiveTab();
- }
- }
- function isTheater(){
- const cssElm=document.querySelector('ytd-watch-flexy');
- return (cssElm && cssElm.hasAttribute('theater'))
- }
- function isChatExpand(){
- const cssElm=document.querySelector('ytd-watch-flexy');
- return cssElm && cssElm.hasAttribute('userscript-chatblock') && !cssElm.hasAttribute('userscript-chat-collapsed')
- }
- function isWideScreenWithTwoColumns(){
- const cssElm=document.querySelector('ytd-watch-flexy');
- return (cssElm && cssElm.hasAttribute('is-two-columns_'))
- }
- function isAnyActiveTab(){
- return $('#right-tabs .tab-btn.active').length>0
- }
- 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.querySelector('ytd-toggle-button-renderer').click();
- }
- function ytBtnCollapseChat(){
- let button = document.querySelector('ytd-live-chat-frame#chat:not([collapsed])>.ytd-live-chat-frame#show-hide-button')
- if (button) button.querySelector('ytd-toggle-button-renderer').click();
- }
- function fixDisplayForTheaterModeChanged(){
- const cssElm = document.querySelector('ytd-watch-flexy')
- if(!cssElm) return;
- if(isTheater() && isWideScreenWithTwoColumns()){
- if( isAnyActiveTab()) switchTabActivity(null)
- if( isChatExpand() ) ytBtnCollapseChat()
- }else if( !isTheater() && !isChatExpand() && !isAnyActiveTab()){
- console.log('a112', lastShowTab)
- if(lastShowTab=='#chatroom') ytBtnExpandChat(); else setToActiveTab();
- }else if( !isWideScreenWithTwoColumns() && !isChatExpand() && !isAnyActiveTab() ){
- setToActiveTab();
- }
- }
- function hackImgShadow(imgShadow){
- // add to #columns and add back after loaded
- let img = imgShadow.querySelector('img')
- if(!img)return;
- let p=imgShadow.parentNode
- let z=$(imgShadow).clone()[0]; //to occupy the space
- p.replaceChild(z, imgShadow)
- $(imgShadow).prependTo('#columns'); // refer to css hack
- function onload(evt){
- if(evt) this.removeEventListener('load',onload,false)
- p.replaceChild(imgShadow, z)
- p=null;
- z=null;
- imgShadow=null;
- }
- if (img.complete) onload();
- else img.addEventListener('load',onload,false)
- }
- const Q={}
- Q.$callOnceAsync=async function(key){
- if (Q[key] && Q[key]() === false) Q[key] = null
- }
- function chatFrameElement(cssSelector){
- let iframe = document.querySelector('iframe#chatframe');
- if(!iframe) return null;
- let cDoc = iframe.contentDocument;
- if(!cDoc) return null;
- if(cDoc.readyState != 'complete') return null; //we must wait for its completion
- let elm = null;
- try{
- elm = cDoc.querySelector(cssSelector)
- }catch(e){
- console.log('iframe error', e)
- }
- return elm;
- }
- function fixRelated(){
- if(!document.querySelector("#tab-videos>[placeholder-videos]>ytd-watch-next-secondary-results-renderer[data-dom-changed-by-tabview-youtube]")){
- let relatedVideos = document.querySelector("#related>ytd-watch-next-secondary-results-renderer");
- if(relatedVideos){
- $('[placeholder-videos]').removeAttr('placeholder-videos')
- $('[placeholder-for-youtube-play-next-queue]').removeAttr('placeholder-for-youtube-play-next-queue')
- let $parentNode= $(relatedVideos.parentNode).appendTo(document.querySelector("#tab-videos"))
- $(relatedVideos).attr('data-dom-changed-by-tabview-youtube',scriptVersionForExternal)
- $parentNode.attr('placeholder-for-youtube-play-next-queue','').attr('placeholder-videos','')
- $('[placeholder-videos]').scroll(makeBodyScrollByEvt);
- }
- }
- }
- function extractTextContent(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 mtf_fixTabsAtTheEnd(){
- // if window resize, youtube coding will relocate the element
- // for example, chatroom move before #right-tabs
- // causing difference apperance after resize of window
- fixRelated();
- let nonLastRightTabs = document.querySelector('#secondary #right-tabs:not(:last-child)')
- if(nonLastRightTabs){
- nonLastRightTabs.parentNode.appendChild(nonLastRightTabs)
- }
- let chatroom = document.querySelector('#primary ytd-live-chat-frame#chat');
- if(chatroom){
- let right_tabs = document.querySelector('#secondary #right-tabs:last-child')
- if(right_tabs){
- right_tabs.parentNode.insertBefore(chatroom, right_tabs)
- }
- }
- const autocomplete=document.querySelector('body>.autocomplete-suggestions:not([position-fixed-by-tabview-youtube]):not(:empty)')
- if(autocomplete){
- const searchBox = document.querySelector('[placeholder-for-youtube-play-next-queue] input#suggestions-search')
- if(searchBox){
- autocomplete.setAttribute('position-fixed-by-tabview-youtube','');
- if(!searchBox.hasAttribute('is-set-click-to-toggle')){
- searchBox.setAttribute('is-set-click-to-toggle','')
- searchBox.addEventListener('click',function(){
- setTimeout(function(){
- let elm=document.querySelector('.autocomplete-suggestions[position-fixed-by-tabview-youtube]:not(:empty)')
- $(elm).toggle()
- //if(elm.style.display=='none') elm.style.display=''; else elm.style.display='none';
- },100);
- })
- }
- let aaa=searchBox.nextSibling;
- if(aaa && aaa.nodeName=="BFJQ"){
- }else if(aaa && aaa.nodeName!="BFJQ"){
- $(aaa=document.createElement("BFJQ")).insertAfter(searchBox);
- }else{
- $(aaa=document.createElement("BFJQ")).prependTo(searchBox.parentNode);
- }
- $(autocomplete).prependTo(aaa);
- aaa.style.setProperty('--sb-margin-bottom',getComputedStyle(searchBox).marginBottom)
- aaa.style.setProperty('--height',searchBox.offsetHeight + 'px')
- /*
- setInterval(function(){
- autocomplete.style.setProperty('--ac-left',autocomplete.getBoundingClientRect().left + 'px')
- autocomplete.style.setProperty('--ac-top',autocomplete.getBoundingClientRect().top + 'px')
- autocomplete.style.setProperty('--sb-left',aaa.getBoundingClientRect().left + 'px')
- autocomplete.style.setProperty('--sb-top',aaa.getBoundingClientRect().top + 'px')
- },270)*/
- }
- }
- let zCache=document.querySelector('[placeholder-for-youtube-play-next-queue] #items ytd-compact-video-renderer:last-of-type')
- if(cachedLastVideo && zCache && cachedLastVideo!==zCache){
- // let cachedLastVideoStr = cachedLastVideo.__userscript_prev_textcontent__;
- // cachedLastVideo=zCache
- // cachedLastVideo.
- /* $0.textContent.replace(/\s+/g,'').replace(/[^\da-zA-Z\u4E00-\u9FFF]/g,'')*/
- const searchBox = document.querySelector('[placeholder-for-youtube-play-next-queue] input#suggestions-search')
- if(!cachedLastVideo.parentNode/* || cachedLastVideoStr !== cachedLastVideo.*/){
- //removed
- fromSearch=true;
- requestAnimationFrame(function(){
- $('[placeholder-for-youtube-play-next-queue]')[0].scrollTop=0;
- const searchBox=document.querySelector('[placeholder-for-youtube-play-next-queue] input#suggestions-search')
- if(searchBox) searchBox.blur();
- });
- }else if(searchBox && !zCache.__clone_last_results__){
- let p=zCache.parentNode;
- //update from youtube loading
- setTimeout(function(){
- if(p&& p.parentNode) zCache.__clone_last_results__=$(p).clone();
- //$()
- //$('ytd-watch-next-secondary-results-renderer')
- },800)
- }
- cachedLastVideo=zCache
- setTimeout(function(){
- const searchBox = document.querySelector('[placeholder-for-youtube-play-next-queue] input#suggestions-search')
- let zCache=document.querySelector('[placeholder-for-youtube-play-next-queue] ytd-watch-next-secondary-results-renderer #items>ytd-compact-video-renderer:last-of-type')
- let items = document.querySelector('[placeholder-for-youtube-play-next-queue] ytd-watch-next-secondary-results-renderer #items');
- if(searchBox && $(searchBox).is(":visible") && (searchBox.value||"").length===0 && items.__clone_last_results__ && fromSearch ){
- if(items.__clone_last_results__){
- for(const s of items.querySelectorAll('ytd-compact-video-renderer')){
- try{
- items.removeChild(s);
- }catch(e){}
- }
- /*
- let texts=[];
- for(const s of items.querySelectorAll('[placeholder-for-youtube-play-next-queue] ytd-item-section-renderer ytd-compact-video-renderer')){
- texts.push(extractTextContent(s))
- }*/
- for(const s of items.__clone_last_results__.querySelectorAll('ytd-compact-video-renderer')){
- /* const text = extractTextContent(s)
- if(texts.indexOf(text)>0)continue;
- */ $(items).append(s)
- }
- fromSearch=false;
- }
- }
- },300)
- }else if(!cachedLastVideo && zCache && cachedLastVideo!==zCache){
- cachedLastVideo=zCache
- }
- }
- function mtf_ChatExist(){
- // 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
- const elmChat = document.querySelector('ytd-live-chat-frame#chat')
- let elmCont = null;
- if(elmChat){
- elmCont=chatFrameElement('yt-live-chat-renderer #continuations')
- }
- const chatBlockR = (elmChat?1:0)+(elmCont?2:0)
- if(Q.mtf_chatBlockQ!==chatBlockR){
- //console.log(897, Q.mtf_chatBlockQ, chatBlockR)
- Q.mtf_chatBlockQ=chatBlockR
- const cssElm = document.querySelector('ytd-watch-flexy')
- if(elmChat){
- let s=0;
- if(elmCont){
- //not found if it is collasped.
- s |= elmCont.querySelector('yt-timed-continuation')?1:0;
- s |= elmCont.querySelector('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) {
- cssElm.setAttribute('userscript-chatblock', 'chat-live')
- requestingComments=null;
- }
- if(s==2) cssElm.setAttribute('userscript-chatblock', 'chat-playback')
- //if(s==5) cssElm.setAttribute('userscript-chatblock', 'chat-live-paid')
- if(s==1) $("span#tab3-txt-loader").text('');
- }
- //keep unknown as original
- if( !cssElm.hasAttribute) cssElm.setAttribute('userscript-chatblock', '')
- }else{
- cssElm.removeAttribute('userscript-chatblock')
- cssElm.removeAttribute('userscript-chat-collapsed')
- }
- }
- }
- let lastScrollAt = 0;
- function makeBodyScrollByEvt(){
- // inside marco task (event)
- Promise.resolve().then(()=>window.dispatchEvent(new Event("scroll")))
- }
- function makeBodyScroll() {
- // avoid over triggering scroll event
- if (+new Date - lastScrollAt < 30) return;
- lastScrollAt = +new Date;
- //required for youtube content display
- requestAnimationFrame(()=>{
- window.dispatchEvent(new Event("scroll"));
- })
- }
- let requestingComments = null
- function scrollForComments_TF(){
- let comments = requestingComments;
- if ( comments && comments.hasAttribute('hidden')) makeBodyScroll();
- }
- function scrollForComments() {
- setTimeout(scrollForComments_TF, 80);
- setTimeout(scrollForComments_TF, 240);
- setTimeout(scrollForComments_TF, 870);
- }
- let mtoNav = null;
- const mtoVs={}
- function initObserver(){
- // continuous check for element relocation
- function mtf_append_comments() {
- let comments = document.querySelector('#primary ytd-watch-metadata ~ #info ~ ytd-comments#comments');
- if (comments) $(comments).appendTo('#tab-comments').attr('data-dom-changed-by-tabview-youtube',scriptVersionForExternal)
- }
- // continuous check for element relocation
- function mtf_liveChatBtnF() {
- let button = document.querySelector('ytd-live-chat-frame#chat>.ytd-live-chat-frame#show-hide-button:nth-child(n+2)');
- if (button) button.parentNode.insertBefore(button, button.parentNode.firstChild)
- }
- // continuous check for element relocation
- // fired at begining & window resize, etc
- function mtf_append_playlist(){
- let ple1 = document.querySelector("*:not(#ytd-userscript-playlist)>ytd-playlist-panel-renderer#playlist");
- if(ple1){
- appendWithWrapper(
- ple1,
- 'ytd-userscript-playlist',
- document.querySelector("#tab-list")
- );
- $(ple1).attr('data-dom-changed-by-tabview-youtube',scriptVersionForExternal)
- }
- }
- // content fix - info & playlist
- // fired at begining, and keep for in case any change
- function mtf_fix_details() {
- const content = document.querySelector('#meta-contents ytd-expander>#content, #tab-info ytd-expander>#content')
- if (content) {
- const expander = content.parentNode;
- if (expander.hasAttribute('collapsed')) setAttr(expander,'collapsed',false);
- let btn1 = expander.querySelector('tp-yt-paper-button#less:not([hidden])');
- let btn2 = expander.querySelector('tp-yt-paper-button#more:not([hidden])');
- if (btn1) setAttr(btn1,'hidden',false);
- if (btn2) setAttr(btn2,'hidden',false);
- }
- // just in case the playlist is collapsed
- const playlist = document.querySelector('#tab-list ytd-playlist-panel-renderer#playlist')
- if(playlist){
- if(playlist.hasAttribute('collapsed')) setAttr(playlist,'collapsed',false);
- if(playlist.hasAttribute('collapsible')) setAttr(playlist,'collapsible',false);
- }
- }
- let mtoNav_requestNo=0;
- let mtoNav_delayedF = () => {
- let {addP, removeP} = Q;
- Q.addP = 0;
- Q.removeP = 0;
- let promisesForAddition=!scriptEnable?[]:addP > 0?[
- Q.$callOnceAsync('mtf_advancedComments'),
- Q.$callOnceAsync('mtf_infoSectionHeight'),
- Q.$callOnceAsync('mtf_checkDescriptionLoaded'),
- Q.$callOnceAsync('mtf_checkPlayList'),
- Q.$callOnceAsync('mtf_fetchCommentsAvailable'),
- Q.$callOnceAsync('mtf_initalAttr_comments'),
- Q.$callOnceAsync('mtf_initalAttr_playlist'),
- Q.$callOnceAsync('mtf_checkStatus_chatroom'),
- Q.$callOnceAsync('mtf_checkFlexy'),
- Q.$callOnceAsync('mtf_forceCheckLiveVideo'),
- (async () => {
- mtf_append_comments();
- })(),
- (async () => {
- mtf_liveChatBtnF();
- })(),
- (async ()=>{
- mtf_fixTabsAtTheEnd();
- })(),
- (async () => {
- mtf_append_playlist();
- })()
- ]:[];
- let promisesForEveryMutation=!scriptEnable?[]:[
- (async () => {
- mtf_fix_details();
- })(),
- (async () => {
- mtf_ChatExist();
- })()
- ];
- Promise.all([...promisesForAddition,...promisesForEveryMutation]).then(()=>{
- mtoNav_requestNo--;
- //console.log('motnav reduced to', mtoNav_requestNo)
- if(mtoNav_requestNo>0){
- mtoNav_requestNo=1;
- setTimeout(mtoNav_delayedF,mtoInterval);
- }
- })
- }
- Q.addP=0;
- Q.removeP=0;
- let hReqNo=0;
- const mtoNavF=(mutations, observer) => {
- let ch = false;
- for (const mutation of mutations) {
- for (const addedNode of mutation.addedNodes)
- if (addedNode.nodeType === 1) {
- Q.addP++
- ch = true;
- }
- for (const removedNode of mutation.removedNodes)
- if (removedNode.nodeType === 1) {
- Q.removeP++;
- ch = true;
- }
- }
- if (!ch) return;
- mtoNav_requestNo++;
- hReqNo++;
- if(hReqNo==36) {
- mtoInterval=mtoInterval2;
- clickInterval=clickInterval2;
- }
- //console.log('motnav added to', mtoNav_requestNo)
- if(mtoNav_requestNo==1) setTimeout(mtoNav_delayedF,mtoInterval);
- }
- mtoNav = new MutationObserver(mtoNavF);
- mtoNav.observe(document.querySelector('ytd-watch-flexy'), {
- subtree: true,
- childList: true
- })
- 1;1&&(async()=>{
- Q.addP=1; //fake the function
- mtoNav_requestNo++;
- if(mtoNav_requestNo==1) mtoNav_delayedF();
- })();
- }
- let displayedPlaylist=null
- let scrollingVideosList=null
- let scriptEnable =false;
- let lastShowTab = null;
- let cachedLastVideo=null;
- let fromSearch=false;
- function resetBeforeNav() {
- fromSearch=true;
- cachedLastVideo=null;
- lastShowTab=null;
- displayedPlaylist=null
- scrollingVideosList=null
- scriptEnable =false;
- clearMutationObserver(mtoVs,'mtoVisibility_Playlist')
- clearMutationObserver(mtoVs,'mtoVisibility_Comments')
- clearMutationObserver(mtoVs,'mtoVisibility_Chatroom')
- clearMutationObserver(mtoVs,'mtoFlexyAttr')
- if (mtoNav) {
- mtoNav.takeRecords();
- mtoNav.disconnect();
- mtoNav = null;
- Q.mtf_advancedComments=null;
- Q.mtf_checkDescriptionLoaded = null;
- Q.mtf_checkPlayList = null;
- Q.mtf_fetchCommentsAvailable = null;
- Q.mtf_initalAttr_comments = null;
- Q.mtf_initalAttr_playlist = null;
- Q.mtf_checkStatus_chatroom = null;
- Q.mtf_forceCheckLiveVideo=null;
- Q.mtf_chatBlockQ = null;
- }
- mtoInterval = mtoInterval1;
- clickInterval = clickInterval1;
- }
- function resetAtNav() {
- scriptEnable =true;
- $("ytd-watch-flexy").removeAttr("userscript-chatblock").removeAttr("userscript-chat-collapsed");
- $('#tab-comments').attr('lazy-loading', '');
- $('span#tab3-txt-loader').text('');
- //removed any cache of #comments header (i.e. count message)
- var prevCommentsHeader = document.querySelector('ytd-comments#comments ytd-comments-header-renderer');
- if (prevCommentsHeader) prevCommentsHeader.parentNode.removeChild(prevCommentsHeader);
- var prevCommentsMsg= document.querySelector('ytd-item-section-renderer#sections #header ~ #contents>ytd-message-renderer:only-child');
- if (prevCommentsMsg) prevCommentsMsg.parentNode.removeChild(prevCommentsMsg);
- //force to [hidden]
- var prevComemnts = document.querySelector('ytd-comments#comments');
- if (prevComemnts) {
- setAttr(prevComemnts, 'hidden', true);
- requestingComments = prevComemnts;
- //scrollForComments();
- }
- //playlist bug
- /*
- var prevPlaylist = document.querySelector('ytd-watch-flexy #columns ytd-playlist-panel-renderer#playlist')
- var secondInner = document.querySelector('ytd-watch-flexy #secondary>#secondary-inner');
- if (prevPlaylist && secondInner){
- prevPlaylist.removeAttribute('hidden')
- secondInner.appendChild(prevPlaylist)
- }
- var prevRelated = document.querySelector('ytd-watch-flexy #columns #related')
- if (prevRelated && secondInner){
- secondInner.appendChild(prevRelated)
- }*/
- }
- function getTabsHTML(){
- let ts = settings.toggleSettings;
- if (!ts.tabs) return;
- const sTabBtnVideos = `${svgElm(16,16,298,298,svgVideos)}<span>Videos</span>`
- const sTabBtnInfo = `${svgElm(16,16,23.625,23.625,svgInfo)}<span>Info</span>`
- const sTabBtnPlayList = `${svgElm(16,16,426.667,426.667,svgPlayList)}<span>Playlist</span>`
- const str1 = `
- <paper-ripple class="style-scope yt-icon-button">
- <div id="background" class="style-scope paper-ripple" style="opacity:0;"></div>
- <div id="waves" class="style-scope paper-ripple"></div>
- </paper-ripple>
- `;
- const str_tabs = [
- ts.tInfo ? `<a id="tab-btn1" data-name="info" userscript-tab-content="#tab-info" class="tab-btn">${sTabBtnInfo}${str1}</a>` : '',
- `<a id="tab-btn2" userscript-tab-content="#tab-live" class="tab-btn tab-btn-hidden">Chat${str1}</a>`,
- ts.tComments ? `<a id="tab-btn3" userscript-tab-content="#tab-comments" data-name="comments" class="tab-btn">${svgElm(16,16,60,60,svgComments)}<span id="tab3-txt-loader"></span>${str1}</a>` : '',
- ts.tVideos ? `<a id="tab-btn4" userscript-tab-content="#tab-videos" data-name="videos" class="active tab-btn">${sTabBtnVideos}${str1}</a>` : '',
- `<a id="tab-btn5" userscript-tab-content="#tab-list" class="tab-btn">${sTabBtnPlayList}${str1}</a>`
- ].join('')
- var addHTML = `
- <div id="right-tabs">
- <header>
- <div id="material-tabs">
- ${str_tabs}
- </div>
- </header>
- <div class="tab-content">
- <div id="tab-info" class="tab-content-cld" userscript-scrollbar-render></div>
- <div id="tab-live" class="tab-content-cld tab-content-hidden" userscript-scrollbar-render></div>
- <div id="tab-comments" class="tab-content-cld" userscript-scrollbar-render></div>
- <div id="tab-videos" class="tab-content-cld" userscript-scrollbar-render></div>
- <div id="tab-list" class="tab-content-cld" userscript-scrollbar-render></div>
- </div>
- </div>
- `;
- return addHTML
- }
- function onNavigationEnd() {
- resetBeforeNav();
- if(!/https?\:\/\/(\w+\.)*youtube\.com\/watch\?(\w+\=[^\/\?\&]+\&)*v=[\w\-\_]+/.test(window.location.href))return;
- resetAtNav();
- let promise = Promise.resolve();
- if (!document.querySelector("#right-tabs")) {
- let targetElm = document.querySelector("ytd-watch-flexy #secondary>#secondary-inner")||document.querySelector("ytd-watch-flexy #secondary")||document.querySelector("ytd-watch-flexy #columns");
- if(!targetElm) throw 'Userscript: Two Column flexy layout not found'; // not flexy layout
- promise=promise.then(()=>{
- $(getTabsHTML()).appendTo(targetElm).attr('data-dom-created-by-tabview-youtube',scriptVersionForExternal);
- targetElm=null;
- })
- }
- promise.then(runAfterTabAppended).then(initObserver)
- }
- function setToActiveTab() {
- if(isTheater() && isWideScreenWithTwoColumns())return;
- const jElm = document.querySelector(`a[userscript-tab-content="${switchTabActivity_lastTab}"]:not(.tab-btn-hidden)`) ||
- document.querySelector(`a[userscript-tab-content="#tab-${settings.defaultTab}"]:not(.tab-btn-hidden)`) ||
- document.querySelector("a[userscript-tab-content]:not(.tab-btn-hidden)") ||
- null;
- switchTabActivity(jElm);
- }
- function insertBefore(elm, p) {
- if (elm && p && p.parentNode)
- p.parentNode.insertBefore(elm, p);
- }
- function appendWithWrapper(elm, wrapperId, toParent){
- if(!toParent||!elm)return;
- let $wrapper = $(`#${wrapperId}`);
- if(!$wrapper[0]) $wrapper=$(`<div id="${wrapperId}"></div>`)
- $wrapper.append(elm).appendTo(toParent);
- }
- function runAfterTabAppended() {
- // just switch to the default tab
- setToActiveTab();
- // append the next videos
- // it exists as "related" is already here
- fixRelated();
- prepareTabBtn();
- // append the detailed meta contents to the tab-info
- Q.mtf_checkDescriptionLoaded = () => {
- const expander = document.querySelector("#meta-contents ytd-expander");
- if (!expander) return true;
- $(expander).appendTo("#tab-info").attr('data-dom-changed-by-tabview-youtube',scriptVersionForExternal)
- const avatar = document.querySelector('ytd-watch-flexy #meta-contents yt-img-shadow#avatar');
- if(avatar) hackImgShadow(avatar)
- return false;
- }
- Q.$callOnceAsync('mtf_checkDescriptionLoaded')
- // force window scroll when #continuations is first detected and #comments still [hidden]
- Q.mtf_advancedComments = () => {
- const continuations = document.querySelector("ytd-comments#comments #continuations");
- if (!continuations) return true;
- requestingComments = document.querySelector('ytd-comments#comments');
- scrollForComments();
- return false;
- }
- Q.$callOnceAsync('mtf_advancedComments')
- /*
- Q.mtf_infoSectionHeight=()=>{
- const infoSection = document.querySelector("#primary #player ~ #info>#info-contents");
- if (!infoSection) return true;
- if(mtoVs.rsoInfoSection) {
- mtoVs.rsoInfoSection.disconnect();
- mtoVs.rsoInfoSection=null;
- }
- mtoVs.rsoInfoSection=new ResizeObserver(()=>{
- const cssElm = document.querySelector('ytd-watch-flexy')
- if(!cssElm)return;
- cssElm.style.setProperty('--userscript-info-section-height', infoSection.offsetHeight);
- });
- return false;
- }
- Q.$callOnceAsync('mtf_infoSectionHeight')*/
- // make window scroll event from playlist scrolling
- // i guess it shall be not neccessary, just in case
- /*Q.mtf_checkPlayList = () => {
- const items= document.querySelector('ytd-playlist-panel-renderer>#container>#items');
- if(!items) return true;
- $(items).scroll(makeBodyScrollByEvt);
- return false;
- }
- Q.$callOnceAsync('mtf_checkPlayList')*/
- // use video player's element to detect the live-chat situation (no commenting section)
- // this would be very useful if the live chat is collapsed, i.e. iframe has no indication on the where it is live or replay
- Q.mtf_forceCheckLiveVideo_tf =()=>{
- const cssElm = document.querySelector('ytd-watch-flexy')
- if(!cssElm) return;
- if($('#ytd-player .ytp-time-display').is('.ytp-live')) {
- cssElm.setAttribute('userscript-chatblock', 'chat-live')
- requestingComments=null;
- }
- }
- Q.mtf_forceCheckLiveVideo = () => {
- const playerLabel = document.querySelector('#ytd-player .ytp-time-display') && document.querySelector('ytd-live-chat-frame#chat')
- if (!playerLabel) return true;
- setTimeout(Q.mtf_forceCheckLiveVideo_tf,170)
- return false;
- }
- Q.$callOnceAsync('mtf_forceCheckLiveVideo')
- createAttributeObservants();
- checkChatStatus();
- $("#right-tabs [userscript-scrollbar-render]").scroll(makeBodyScrollByEvt);
- }
- async function asyncFetchCommentsAvailable() {
- let span = document.querySelector("span#tab3-txt-loader")
- if (!span) return;
- makeBodyScroll();
- let fetchedOnce = false
- Q.mtf_fetchCommentsAvailable = () => {
- if(!scriptEnable)return;
- let messageElm, messageStr;
- const commentRenderer = document.querySelector("ytd-comments#comments #count.ytd-comments-header-renderer");
- if (commentRenderer) {
- fetchedOnce=true;
- let r = '0';
- let txt = commentRenderer.textContent
- if (typeof txt == 'string') {
- let m = txt.match(/[\d\,\s]+/)
- if (m) r = m[0].trim()
- }
- span.textContent = r;
- $('#tab-comments[lazy-loading]').removeAttr('lazy-loading')
- mtoInterval=mtoInterval2;
- clickInterval=clickInterval2;
- }else if((messageElm = document.querySelector('ytd-item-section-renderer#sections #header ~ #contents>ytd-message-renderer:only-child'))&&(messageStr=(messageElm.textContent||'').trim())){ //ytd-message-renderer
- // it is possible to get the message before the header generation.
- setTimeout(function(){
- if(fetchedOnce)return;
- const mainMsg= messageElm.querySelector('#message, #submessage')
- if(mainMsg && mainMsg.textContent){
- for(const msg of mainMsg.querySelectorAll('*:not(:empty)')){
- if(msg.childElementCount===0 && msg.textContent) {
- messageStr=msg.textContent.trim()
- break
- }
- }
- }
- span.textContent = messageStr;
- $('#tab-comments[lazy-loading]').removeAttr('lazy-loading')
- },240);
- }
- return true;
- }
- Q.$callOnceAsync('mtf_fetchCommentsAvailable')
- }
- function createAttributeObservants() {
- // Attr Mutation Observer - #playlist - hidden
- clearMutationObserver(mtoVs,'mtoVisibility_Playlist')
- // Attr Mutation Observer callback - #playlist - hidden
- let mtf_attrPlaylist=(mutations, observer)=>{
- var playlist=document.querySelector('ytd-playlist-panel-renderer#playlist')
- const $tabBtn = $('[userscript-tab-content="#tab-list"]');
- //console.log(3712,$tabBtn)
- //console.log('attr playlist changed')
- if( $tabBtn.is('.tab-btn-hidden') && !playlist.hasAttribute('hidden') ){
- //console.log(3713)
- //console.log('attr playlist changed - no hide')
- $tabBtn.removeClass("tab-btn-hidden");
- }else if( !$tabBtn.is('.tab-btn-hidden') && playlist.hasAttribute('hidden') ){
- //console.log(3714)
- //console.log('attr playlist changed - add hide')
- hideTabBtn($tabBtn);
- }
- }
- // pending for #playlist and set Attribute Observer
- Q.mtf_initalAttr_playlist=()=>{
- var playlist=document.querySelector('ytd-playlist-panel-renderer#playlist')
- if(!playlist) return true;
- initMutationObserver(mtoVs,'mtoVisibility_Playlist', mtf_attrPlaylist)
- mtoVs.mtoVisibility_Playlist.observe(playlist, {
- attributes: true,
- attributeFilter: ['hidden'],
- attributeOldValue: true
- })
- //console.log(3711)
- mtf_attrPlaylist()
- return false;
- }
- //console.log(3710)
- Q.$callOnceAsync('mtf_initalAttr_playlist')
- // Attr Mutation Observer - ytd-comments#comments - hidden
- clearMutationObserver(mtoVs,'mtoVisibility_Comments')
- // Attr Mutation Observer callback - ytd-comments#comments - hidden
- let mtf_attrComments=(mutations, observer)=>{
- var comments=document.querySelector('ytd-comments#comments')
- const $tabBtn = $('[userscript-tab-content="#tab-comments"]');
- if(!comments || !$tabBtn[0])return;
- //console.log('attr comments changed')
- if( $tabBtn.is('.tab-btn-hidden') && !comments.hasAttribute('hidden') ){
- //console.log('attr comments changed - no hide')
- $tabBtn.removeClass("tab-btn-hidden");
- asyncFetchCommentsAvailable();
- }else if( !$tabBtn.is('.tab-btn-hidden') && comments.hasAttribute('hidden') ){
- //console.log('attr comments changed - add hide')
- if(!document.querySelector('[userscript-chatblock="chat-live"]')){
- requestingComments=comments
- $('#tab-comments').attr('lazy-loading','')
- }
- $('span#tab3-txt-loader').text('');
- hideTabBtn($tabBtn);
- }
- }
- // pending for #comments and set Attribute Observer
- Q.mtf_initalAttr_comments=()=>{
- var comments=document.querySelector('ytd-comments#comments')
- if(!comments) return true;
- initMutationObserver(mtoVs,'mtoVisibility_Comments',mtf_attrComments)
- mtoVs.mtoVisibility_Comments.observe(comments, {
- attributes: true,
- attributeFilter: ['hidden'],
- attributeOldValue: true
- })
- mtf_attrComments()
- requestingComments = document.querySelector('ytd-comments#comments');
- scrollForComments()
- return false;
- }
- Q.$callOnceAsync('mtf_initalAttr_comments')
- }
- function isEmptyBody(){
- //only deal with loaded document without body
- //other situation, case by case
- let iframe = document.querySelector('ytd-live-chat-frame iframe#chatframe');
- if(!iframe) return false; //iframe must be there
- if(iframe.readyState != 'complete') return false; //we must wait for its completion
- let doc = null;
- try{
- doc=iframe.contentDocument
- }catch(e){}
- if(!doc) return false; //might be not loaded yet
- if(doc.body && doc.body.childElementCount===0){
- //empty body
- return true;
- }
- }
- function checkChatStatus(){
- clearMutationObserver(mtoVs,'mtoVisibility_Chatroom')
- let cid_chatFrameCheck=0;
- let mtf_attrChatroom=(mutations, observer)=>{
- const chatBlock = document.querySelector('ytd-live-chat-frame#chat')
- const cssElm = document.querySelector('ytd-watch-flexy')
- if(!cssElm.hasAttribute('userscript-chatblock')) setAttr(cssElm, 'userscript-chatblock', true);
- setAttr(cssElm,'userscript-chat-collapsed',!!chatBlock.hasAttribute('collapsed'));
- if(cssElm.hasAttribute('userscript-chatblock')&&!chatBlock.hasAttribute('collapsed')) lastShowTab='#chatroom'
- if( chatBlock && cssElm && cssElm.hasAttribute('userscript-chatblock') && !chatBlock.hasAttribute('collapsed') && !cid_chatFrameCheck){
- let dd=+new Date;
- cid_chatFrameCheck=setInterval(()=>{
- // mutation on iframe window would not trigger the observer
- // just check the first few seconds for this purpose.
- let chatFrameChecking, iframe;
- if(+new Date - dd>6750){
- //
- }else if( isEmptyBody() ){
- // bug. youtube iframe loaded with nothing
- let button = document.querySelector('ytd-live-chat-frame#chat>.ytd-live-chat-frame#show-hide-button ytd-toggle-button-renderer')
- if (button) {
- setTimeout(function(){
- if(button && button.parentNode && isChatExpand()){
- button.click();
- setTimeout(function(){
- if(button && button.parentNode && !isChatExpand()) button.click();
- button=null;
- },80)
- }else{
- button=null;
- }
- },20)
- }
- }else if(chatFrameChecking=!!chatFrameElement('yt-live-chat-renderer #continuations')){
- mtf_ChatExist();
- $(document.querySelector('ytd-live-chat-frame#chat')).attr('yt-userscript-iframe-loaded','')
- }else{
- return;
- }
- return (cid_chatFrameCheck=clearInterval(cid_chatFrameCheck));
- },270)
- }else if(chatBlock){
- chatBlock.removeAttribute('yt-userscript-iframe-loaded')
- }
- }
- Q.mtf_checkStatus_chatroom=()=>{
- var chatroom=document.querySelector('ytd-live-chat-frame#chat')
- if(!chatroom) return true;
- initMutationObserver(mtoVs,'mtoVisibility_Chatroom',mtf_attrChatroom)
- mtoVs.mtoVisibility_Chatroom.observe(chatroom, {
- attributes: true,
- attributeFilter: ['collapsed'],
- attributeOldValue: true
- })
- mtf_attrChatroom()
- return false;
- }
- Q.$callOnceAsync('mtf_checkStatus_chatroom')
- clearMutationObserver(mtoVs,'mtoFlexyAttr')
- let mtf_attrFlexy=(mutations, observer)=>{
- const cssElm=document.querySelector('ytd-watch-flexy');
- if(!cssElm)return;
- let chatBlockStatusChanged = false
- let theaterStatusChanged = false;
- let twoColStatusChanged = false;
- let initalTriggering = !mutations
- if(!initalTriggering){
- for(const mutation of mutations) {
- if (mutation.attributeName == 'theater') {
- theaterStatusChanged=true;
- }
- if (mutation.attributeName == 'userscript-chat-collapsed' || 'userscript-chatblock'){
- chatBlockStatusChanged=true
- }
- if (mutation.attributeName == 'is-two-columns_'){
- twoColStatusChanged=true;
- }
- }
- }
- if(theaterStatusChanged || chatBlockStatusChanged || twoColStatusChanged || initalTriggering ){
- if(twoColStatusChanged || initalTriggering){
- fixDisplayForTheaterModeChanged();
- }else if(theaterStatusChanged){
- isChatExpandBeforeTheaterChange = isChatExpand()
- requestAnimationFrame(fixDisplayForTheaterModeChanged)
- }else if(chatBlockStatusChanged){
- //chatroom is shown or hidden
- let isOpenChatFrame = !cssElm.hasAttribute('userscript-chat-collapsed') && cssElm.hasAttribute('userscript-chatblock')
- new Promise(requestAnimationFrame).then(() => {
- if (isOpenChatFrame && !isTheater() && isWideScreenWithTwoColumns()) {
- switchTabActivity(null)
- } else if(!isOpenChatFrame && !isTheater() && isWideScreenWithTwoColumns()){
- setToActiveTab();
- } else if(isTheater() && isWideScreenWithTwoColumns() && isOpenChatFrame && isWideScreenWithTwoColumns()){
- ytBtnCancelTheater();
- }
- })
- }
- }
- }
- Q.mtf_checkFlexy=()=>{
- var flexy=document.querySelector('ytd-watch-flexy')
- if(!flexy) return true;
- initMutationObserver(mtoVs,'mtoFlexyAttr',mtf_attrFlexy)
- mtoVs.mtoFlexyAttr.observe(flexy, {
- attributes: true,
- attributeFilter: ['userscript-chat-collapsed','userscript-chatblock','theater','is-two-columns_'],
- attributeOldValue: true
- })
- mtf_attrFlexy()
- let columns = document.querySelector('ytd-page-manager#page-manager #columns')
- if(columns){
- setAttr(columns, 'userscript-scrollbar-render', true);
- }
- return false;
- }
- Q.$callOnceAsync('mtf_checkFlexy')
- }
- let switchTabActivity_lastTab = null
- function switchTabActivity(activeLink) {
- if (activeLink && $(activeLink).is('.tab-btn-hidden')) return; // not allow to switch to hide tab
- if(isTheater() && isWideScreenWithTwoColumns()) activeLink=null;
- const links = document.querySelectorAll('#material-tabs a[userscript-tab-content]');
- function runAtEnd(){
- if(activeLink) lastShowTab=activeLink.getAttribute('userscript-tab-content')
- //override the default youtube coding event prevention
- //let elm=$('ytd-watch-flexy:not([is-two-columns_]) #tab-list:not(.tab-content-hidden) ytd-playlist-panel-renderer')[0];
- let elm=$('ytd-watch-flexy #tab-list:not(.tab-content-hidden) ytd-playlist-panel-renderer')[0];
- displayedPlaylist=elm;
- if(!!displayedPlaylist) $('ytd-watch-flexy').attr('userscript-auto-scroll-playlist',''); else $('ytd-watch-flexy').removeAttr('userscript-auto-scroll-playlist');
- scrollingVideosList=$('ytd-watch-flexy #tab-videos:not(.tab-content-hidden) [placeholder-videos]')[0]
- }
- for (const link of links) {
- let content = document.querySelector(link.getAttribute('userscript-tab-content'));
- if (link && content) {
- if (link !== activeLink) {
- $(link).removeClass("active");
- $(content).addClass("tab-content-hidden");
- } else {
- $(link).addClass("active");
- $(content).removeClass("tab-content-hidden");
- window.requestAnimationFrame(() => {
- content.focus()
- runAtEnd()
- })
- }
- }
- }
- if(!activeLink){
- runAtEnd();
- }
- }
- 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 = materialTab.querySelector('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) {
- if (!this.hasAttribute('userscript-tab-content')) return;
- switchTabActivity_lastTab = this.getAttribute('userscript-tab-content');
- if( isWideScreenWithTwoColumns() && !isTheater() && $(this).is(".tab-btn.active:not(.tab-btn-hidden)")){
- const sizeBtn=document.querySelector('ytd-watch-flexy #ytd-player button.ytp-size-button')
- if(sizeBtn) sizeBtn.click();
- }else if($(this).is(".tab-btn.active:not(.tab-btn-hidden)")){
- switchTabActivity(null);
- /*
- setTimeout(()=>{
- window.scrollTo(0, 0);
- },60)*/
- }else{
- new Promise(requestAnimationFrame).then(() => {
- if(isChatExpand() && isWideScreenWithTwoColumns()) ytBtnCollapseChat();
- else if(isWideScreenWithTwoColumns() && isTheater() ) ytBtnCancelTheater();
- }).then(() => {
- setTimeout(()=>{
- switchTabActivity(this)
- //setTimeout(makeBodyScroll,20);
- setTimeout(()=>{
- let rightTabs=document.querySelector('#right-tabs');
- if(!isWideScreenWithTwoColumns() && rightTabs && rightTabs.offsetTop>0 && $(this).is('.active')){
- window.scrollTo(0, rightTabs.offsetTop);
- }
- },60)
- }, clickInterval);
- })
- }
- evt.preventDefault();
- });
- }
- }
- // ---------------------------------------------------------------------------------------------
- window.addEventListener("yt-navigate-finish", onNavigationEnd)
- const singleColumnScrolling = (function() {
- var lastD = 0,
- lastF = 0;
- return function() {
- let pageY = pageYOffset;
- if (pageY < 10 && lastD === 0 && !lastF) return;
- let targetElm, header, navElm;
- Promise.resolve().then(() => {
- targetElm = document.querySelector("#right-tabs");
- if (!targetElm) return;
- header = targetElm.querySelector("header");
- if (!header) return;
- navElm = document.querySelector('#masthead-container, #masthead')
- if (!navElm) return;
- 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) && (lastD === 0 || lastF)) {
- lastD = 1;
- let {
- offsetHeight
- } = header
- let {
- offsetWidth
- } = targetElm
- targetElm.style.setProperty("--userscript-sticky-width", offsetWidth + 'px')
- targetElm.style.setProperty("--userscript-sticky", offsetHeight + 'px')
- setAttr(targetElm, 'userscript-sticky', true);
- } else if ((xyStatus == 1) && (lastD === 1 || lastF)) {
- lastD = 0;
- setAttr(targetElm, 'userscript-sticky', false);
- }
- targetElm = null;
- header = null;
- navElm = null;
- })
- }
- })();
- window.addEventListener("scroll", function() {
- if(!scriptEnable)return;
- singleColumnScrolling()
- }, {
- capture: false,
- passive: true
- })
- var lastTheatreStatus = 0
- window.addEventListener('resize', function() {
- if(!scriptEnable)return;
- requestAnimationFrame(() => {
- lastF = 1;
- singleColumnScrolling()
- lastF = 0;
- })
- }, {
- capture: false,
- passive: true
- })
- window.addEventListener('beforeunload', function() {
- if(!scriptEnable)return;
- console.log('beforeunload')
- resetBeforeNav();
- let video=document.querySelector('video');
- if(video && !video.paused) video.pause();
- }, {capture: true})
- window.addEventListener('hashchange', function() {
- if(!scriptEnable)return;
- console.log('hashchange')
- resetBeforeNav();
- }, {capture: true})
- window.addEventListener('popstate', function() {
- if(!scriptEnable)return;
- console.log('popstate')
- resetBeforeNav();
- }, {capture: true})
- function clearMutationObserver(o, key){
- if(o[key]) {
- o[key].takeRecords();
- o[key].disconnect();
- o[key]=null;
- }
- }
- function initMutationObserver(o, key, callback){
- clearMutationObserver(o,key)
- o[key]=new MutationObserver(callback)
- }
- /*
- async function resizer(){
- let full=true;
- (async ()=>{
- const primaryPlayer=$('ytd-watch-flexy #primary-inner>#player')[0]
- cssElm.style.setProperty('--userscript-resizing-primary-player-height', primaryPlayer?primaryPlayer.offsetHeight:'')
- if(!primaryPlayer)full=false;
- })();
- (async ()=>{
- const primaryInner=$('ytd-watch-flexy #primary-inner')[0]
- cssElm.style.setProperty('--userscript-resizing-primary-inner-height', primaryInner?primaryInner.offsetHeight:'')
- if(!primaryInner)full=false;
- })();
- (async ()=>{
- const infoContents=$('ytd-watch-flexy #primary-inner>#info>#info-contents')[0]
- cssElm.style.setProperty('--userscript-resizing-primary-info-height', infoContents?infoContents.offsetHeight:'')
- if(!infoContents)full=false;
- })();
- (async ()=>{
- const metaContents=$('ytd-watch-flexy #primary-inner>#meta>#meta-contents')[0]
- cssElm.style.setProperty('--userscript-resizing-primary-meta-height', metaContents?metaContents.offsetHeight:'')
- if(!metaContents)full=false;
- })();
- (async ()=>{
- const secondaryInner=$('ytd-watch-flexy #secondary-inner')[0]
- cssElm.style.setProperty('--userscript-resizing-secondary-inner-height', secondaryInner?secondaryInner.offsetHeight:'')
- if(!secondaryInner)full=false;
- })();
- }
- window.addEventListener('resize',function(){
- const cssElm=document.querySelector('ytd-watch-flexy');
- if(cssElm){
- }
- }) */
- document.addEventListener('wheel',function(evt){
- if(!scriptEnable)return;
- if(displayedPlaylist && displayedPlaylist.contains(evt.target)){
- evt.stopPropagation(); evt.stopImmediatePropagation()
- }
- },{capture:true,passive:true});
- function setVideosTwoColumns(flag, 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=$(cssSelector1)[0]
- if(bool) $(res.m1).attr('is-two-columns',''); else $(res.m1).removeAttr('is-two-columns')
- }
- if(flag&2){
- res.m2=$(cssSelector2)[0]
- if(bool) $(res.m2).removeAttr('hidden'); else $(res.m2).attr('hidden','')
- }
- if(flag&4){
- res.m3=$(cssSelector3)[0]
- if(bool) $(res.m3).attr('hidden',''); else $(res.m3).removeAttr('hidden');
- }
- return res
- }
- /*
- document.addEventListener('column1',function(evt){
- console.log(evt)
- document.aab=setVideosTwoColumns(1|2|4, false);
- })
- document.addEventListener('column2',function(evt){
- console.log(evt)
- document.aab=setVideosTwoColumns(1|2|4, true);
- })
- */
- 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;
- if( !scrollingVideosList ) 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=totalHeight - 40;
- let h_advanced= h - ((visibleHeight>5*40)?visibleHeight*0.5:0);
- if(z>h_advanced && !isWideScreenWithTwoColumns() ){
- if(new Date - lastScrollFetch<500) return; //prevent continuous calling
- lastScrollFetch=+new Date;
- let res= setVideosTwoColumns(2|4, true)
- if(res.m3 && res.m2){
- //wait for DOM change, just in case
- requestAnimationFrame(()=>{
- let {offsetTop}=res.m2 // as visiblity 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})
- // https://github.com/cyfung1031/Tabview-Youtube/raw/main/js/content.js
- }
- ;!(function $$() {
- 'use strict';
- if(document.documentElement==null) return window.requestAnimationFrame($$)
- var 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...
- })();