Greasy Fork

Tabview Youtube

把Youtube Videos中的评论及视频列表制作成Tabs

目前为 2022-05-22 提交的版本。查看 最新版本

// ==UserScript==
// @name         Tabview Youtube
// @name:en      Tabview Youtube
// @name:jp      Tabview Youtube
// @name:zh-tw   Tabview Youtube
// @name:zh-cn   Tabview Youtube
// @namespace    http://tampermonkey.net/
// @version     2.3.0
// @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:jp  YouTube動画のコメントやリストなどをタブに作成します
// @description:zh-tw  把Youtube Videos中的評論及影片清單製作成Tabs
// @description:zh-cn  把Youtube Videos中的评论及视频列表制作成Tabs
// @author       CY Fung
// @include     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/f61bad127d6db71ee0433d92971db247ab9b5d98/css/style_content.css
// @resource     injectionJS1 https://raw.githubusercontent.com/cyfung1031/Tabview-Youtube/f61bad127d6db71ee0433d92971db247ab9b5d98/js/injection_script_1.js
// @resource     injectionFixAutoComplete https://raw.githubusercontent.com/cyfung1031/Tabview-Youtube/f61bad127d6db71ee0433d92971db247ab9b5d98/js/injectionScript_fixAutoComplete.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/cash/8.1.0/cash.min.js
// @noframes
// ==/UserScript==

/* jshint esversion:6 */

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:
   * <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 = '2022/05/07';

  //const githubURLBase = "https://raw.githubusercontent.com/cyfung1031/Tabview-Youtube";
  //const githubURLCommit = "bdf401045266e5224663f80b276bc7f56d122b8d";

  const isMyScriptInChromeRuntime = () => typeof((((window || 0).chrome || 0).runtime || 0).getURL) == 'function'


  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"/>
    `.trim();

  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"/>`.trim();

  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"/>`.trim();

  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"/>
    `.trim();

  // --- Youtube Video Testing : 
  // Square Video: https://www.youtube.com/watch?v=L0RXVnRbFg8 
  // Square Video: https://www.youtube.com/watch?v=bK_rKhMIotU
  // ---


  const LAYOUT_TWO_COLUMNS = 1;
  const LAYOUT_THEATER = 2;
  const LAYOUT_FULLSCREEN = 4;
  const LAYOUT_CHATROOM = 8;
  const LAYOUT_CHATROOM_COLLASPED = 16;
  const LAYOUT_TAB_EXPANDED = 32;
  const LAYOUT_ENGAGEMENT_PANEL_EXPAND = 64;

  const mtoInterval1 = 40;
  const mtoInterval2 = 150;

  let lastVideoURL = null; // for less attribute set only
  
  /** @type {WeakRef | null} */ 
  const WeakRef = window.WeakRef;
  const mWeakRef = WeakRef ? (o => o ? new WeakRef(o) : null) : (o => o || null);
  /** @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_watchFlexy = new ObserverRegister(()=>{
    return new MutationObserver(FP.mtoNavF)
  });
  const sa_wflexy = mtoMutation_watchFlexy.uid;

  const mtoMutation_body = new ObserverRegister(()=>{
    return new MutationObserver(FP.mtoBodyF)
  });
  const sa_body = mtoMutation_body.uid;

  const mtoFlexyAttr = new ObserverRegister(()=>{
    return new MutationObserver(mtf_attrFlexy)
  });
  const sa_flexyAttr = mtoFlexyAttr.uid;

  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;





  function tracer(key, cmp) {
    if (cmp > 0) return tracer[key] === cmp;
    return (tracer[key] = Date.now());
  }

  function racer(key, f) {
    let now = Date.now();
    const kTime = `${key}$$1`
    let t = racer[kTime] || 0;

    if (now < t) {
      const kCount = `${key}$$2`;
      racer[kCount] = (racer[kCount] || 0) + 1;
      if (racer[kCount] === 1) {
        let g = f;
        requestAnimationFrame(() => {
          racer[kCount] = 0;
          g();
          g = null;
        })
      }
    } else {
      racer[kTime] = now + 16;
      f();
    }
  }

  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 && evt.target && evt.target.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 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 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 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);

    


    function showTabOrChat() {

      layoutStatusMutex.lockWith(unlock => {

        if (lastShowTab == '#chatroom') {

          if (new_isTabExpanded) switchTabActivity(null)
          if (!new_isExpandedChat) ytBtnExpandChat();

        } else if (lastShowTab && lastShowTab.indexOf('#engagement-panel-') == 0) {

          if (new_isTabExpanded) switchTabActivity(null)
          if (!new_isExpandEPanel) ytBtnOpenEngagementPanel(lastShowTab);

        } 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);

      })

    }


    if (new_isExpandedChat || new_isTabExpanded || new_isExpandEPanel) {
      if (statusCollasped !== 1) statusCollasped = 1;
    } else {
      if (statusCollasped === 1) statusCollasped = 2;
    }

    let changes = 0;

    if (old_layoutStatus !== null) changes = old_layoutStatus ^ new_layoutStatus;

    let chat_collasped_changed = !!(changes & LAYOUT_CHATROOM_COLLASPED)
    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 tab_change = (tab_expanded_changed ? 1 : 0) | (chat_collasped_changed ? 2 : 0) | (epanel_expanded_changed ? 4 : 0);

    let isChatOrTabExpandTriggering = tab_change == 0 ? false : (
      (tab_expanded_changed && new_isTabExpanded) ||
      (chat_collasped_changed && new_isExpandedChat) ||
      (epanel_expanded_changed && new_isExpandEPanel)
    );

    let isChatOrTabCollaspeTriggering = tab_change == 0 ? false : (
      (tab_expanded_changed && !new_isTabExpanded) ||
      (chat_collasped_changed && new_isCollaspedChat) ||
      (epanel_expanded_changed && !new_isExpandEPanel)
    );



    let moreThanOneShown = (new_isTabExpanded + new_isExpandedChat + new_isExpandEPanel) > 1

    let requestVideoResize = false;

    if (fullscreen_mode_changed || new_isFullScreen) {

    } else if(new_isTwoColumns && !new_isTheater && !column_mode_changed && (tab_change==2 || tab_change==4) && !new_isTabExpanded && new_isExpandedChat && new_isExpandEPanel){

      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 == 0 && column_mode_changed && new_isTwoColumns && !new_isTheater && statusCollasped === 1 && moreThanOneShown) {

      showTabOrChat();
      requestVideoResize = true;

    } else if (tab_change == 2 && new_isExpandedChat && new_isTwoColumns && !new_isTheater && statusCollasped === 1 && new_isTabExpanded && !column_mode_changed) {

      switchTabActivity(null);
      requestVideoResize = true;

    } else if (isChatOrTabExpandTriggering && new_isTwoColumns && new_isTheater && statusCollasped === 1 && !theater_mode_changed && !column_mode_changed) {

      ytBtnCancelTheater();
      requestVideoResize = true;

    } else if (new_isTwoColumns && new_isTheater && statusCollasped === 1) {

      hideTabAndChat();
      requestVideoResize = true;

    } else if (isChatOrTabCollaspeTriggering && new_isTwoColumns && !new_isTheater && statusCollasped === 2 && !column_mode_changed) {

      ytBtnSetTheater();
      requestVideoResize = true;

    } else if (tab_change == 0 && (column_mode_changed || theater_mode_changed) && new_isTwoColumns && !new_isTheater && statusCollasped !== 1) {

      showTabOrChat();
      requestVideoResize = true;

    } else if (!new_isFullScreen && new_isTwoColumns && !new_isTheater && !new_isTabExpanded &&
      (new_isCollaspedChat || !new_isExpandedChat) &&
      !new_isExpandEPanel
    ) {
      // 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 (column_mode_changed && !chat_collasped_changed && new_isExpandedChat) {

      runAfterExpandChat();

    }



    if (requestVideoResize) {

      timeout_resize_for_layout_change.clear();
      timeout_resize_for_layout_change.set(() => {
        dispatchWindowResize();
      }, 92)

    } else if (timeout_resize_for_layout_change.isEmpty() && (Date.now()) - lastResizeAt > 600) {
      timeout_resize_for_layout_change.set(() => {
        if ((Date.now()) - lastResizeAt > 600) dispatchWindowResize();
      }, 62)
    }




  }



  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 === null) {
          statusCollasped = 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) => `<svg width="${w}" height="${h}" viewBox="0 0 ${vw} ${vh}" preserveAspectRatio="xMidYMid meet">${p}</svg>`

  let settings = {
    defaultTab: "#tab-videos"
  };


  let mtoInterval = mtoInterval1;

  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) {
    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 btn = s.querySelector('ytd-watch-flexy ytd-engagement-panel-title-header-renderer #header > #visibility-button > ytd-button-renderer');
    if (btn) {
      btn.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.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 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 = {}



  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 = queryElement.parentNode; // NOT NULL

      let right_tabs = document.querySelector('#right-tabs');
      let tab_videos = right_tabs.querySelector("#tab-videos");

      if (!right_tabs || !tab_videos) return;

      for (const s of relocatedRelated.querySelectorAll('#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 = relocatedRelated.querySelector("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", makeBodyScrollByEvt);




    }



    /** @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)

    if(document.querySelector('script#userscript-tabview-injection-facp')) return;

    if (isMyScriptInChromeRuntime()){
      addScriptByURL(window.chrome.runtime.getURL('js/injectionScript_fixAutoComplete.js')).id = 'userscript-tabview-injection-facp';;
    } else {
      let injection_script = GM_getResourceText("injectionFixAutoComplete")
      addScript(`${injection_script}`).id = 'userscript-tabview-injection-facp';;
    }

  }

  function mtf_AfterFixTabs() {


    /** @type {HTMLElement | null} */ 
    let ytdFlexyElm = kRef(ytdFlexy);
    if (!scriptEnable || !ytdFlexyElm) return;

    /** @type {HTMLElement | null} */
    const rootElement = Q.mutationTarget || ytdFlexyElm;



    const autocomplete = rootElement.querySelector('[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 = rootElement.querySelector('[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 renderer.querySelector('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 |= 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) {
          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 }

  }


  function mtf_ChatExist() {

    let ytdFlexyElm = kRef(ytdFlexy);
    if (!scriptEnable || !ytdFlexyElm) return;

    if (deferredVarYTDHidden) return;

    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) {
      Q.mtf_chatBlockQ = chatBlockR
      let rChatExist = base_ChatExist();
      //console.log(2446, rChatExist)
      if (rChatExist) {
        let { attr_chatblock, attr_chatcollapsed } = rChatExist;
        wAttr(ytdFlexyElm, 'userscript-chatblock', attr_chatblock)
        wAttr(ytdFlexyElm, 'userscript-chat-collapsed', attr_chatcollapsed)
        
        if(attr_chatblock=='chat-live') _disableComments();
      }
    }

  }




  let lastScrollAt1 = 0;

  function makeBodyScrollByEvt() {
    let ct = Date.now();
    if (ct - lastScrollAt1 < 6) return; // avoid duplicate calling
    lastScrollAt1 = ct;
    window.dispatchEvent(new Event("scroll")); // dispatch Scroll Event to Window for content display
  }

  let lastScrollAt2 = 0;

  function makeBodyScroll() {
    let ct = Date.now();
    if (ct - lastScrollAt2 < 30) return; // avoid over triggering
    lastScrollAt2 = ct;
    requestAnimationFrame(() => {
      window.dispatchEvent(new Event("scroll")); // ask youtube to display content
    })
  }

  //let requestingComments = null
  //let scrollForComments_lastStart = 0;
   /*
  function scrollForComments_TF() {
 
    let comments = requestingComments;
    if (!comments) return;
    if (comments.hasAttribute('hidden')) {
      window.dispatchEvent(new Event("scroll"));
    } else requestingComments = null;

  }
  function scrollForComments() {
    scrollForComments_TF();
    if (!requestingComments) return;
    requestAnimationFrame(scrollForComments_TF);
    let ct = Date.now();
    if (ct - scrollForComments_lastStart < 60) return;
    scrollForComments_lastStart = ct;
    timeline.setTimeout(scrollForComments_TF, 80);
    timeline.setTimeout(scrollForComments_TF, 240);
    timeline.setTimeout(scrollForComments_TF, 870);
  
  }
  */



  const mtoCs = { mtoNav: null, mtoBody: null };


  const mtoVs = {}

  const mutation_target_id_list = ['ytp-caption-window-container', 'items', 'button', 'movie_player', 'player-ads', 'hover-overlays', 'replies'];
  const mutation_target_class_list = ['ytp-panel-menu', 'ytp-endscreen-content'];

  function isMtoOverallSkip(dTarget) {

    if (!dTarget || dTarget.nodeType !== 1) return true;

    if (mutation_target_id_list.includes(dTarget.id)) return true;

    for (const c of dTarget.classList) {
      if (mutation_target_class_list.includes(c)) return true;
    }

    return false;
  }


  const mutation_div_id_ignorelist = [
        'metadata-line',
        'ytp-caption-window-container',
        'top-level-buttons-computed',
        'microformat',
        'visibility-button',
        'info-strings',
        'action-menu',
        'reply-button-end'
    ];

  const mutation_div_class_ignorelist = [
        'badge', 'tp-yt-paper-tooltip', 'ytp-autonav-endscreen-upnext-header',
        'ytp-bound-time-left', 'ytp-bound-time-right', 'ytp-share-icon',
        'ytp-tooltip-title', 'annotation', 'ytp-copylink-icon', 'ytd-thumbnail',
        'paper-ripple',
        //caption
        'captions-text', 'caption-visual-line', 'ytp-caption-segment', 'ytp-caption-window-container',
        //menu
        'ytp-playlist-menu-button-text',

        'ytp-bezel-icon', 'ytp-bezel-text',
        'dropdown-content',
        'tp-yt-paper-menu-button', 'tp-yt-iron-dropdown',

        'ytd-metadata-row-renderer', // #content.ytd-metadata-row-renderer inside each of ytd-metadata-row-renderer (ytd-expander)
        'ytd-engagement-panel-section-list-renderer', // {div#content.style-scope.ytd-engagement-panel-section-list-renderer} inside each of ytd-engagement-panel-section-list-renderer

        'autocomplete-suggestions' // autocomplete-suggestions
    ];

  const mutation_target_tag_ignorelist = [
        'ytd-channel-name', 'tp-yt-iron-dropdown', 'tp-yt-paper-tooltip',
        'tp-yt-paper-listbox', 'yt-img-shadow', 'ytd-thumbnail', 'ytd-video-meta-block',

        'yt-icon-button', 'tp-yt-paper-button', 'yt-formatted-string', 'yt-icon', 'button', 'paper-ripple',

        'ytd-player-microformat-renderer',
        'ytd-engagement-panel-section-list-renderer', 'ytd-engagement-panel-title-header-renderer',
        'ytd-comment-renderer', 'ytd-menu-renderer', 'ytd-badge-supported-renderer',
        'ytd-subscribe-button-renderer', 'ytd-subscription-notification-toggle-button-renderer',
        'ytd-button-renderer', 'ytd-toggle-button-renderer',
        'yt-pdg-comment-chip-renderer', 'ytd-comment-action-buttons-renderer', 'ytd-comment-thread-renderer',
        'ytd-compact-radio-renderer', 'ytd-compact-video-renderer',
        'ytd-video-owner-renderer',
        'ytd-metadata-row-renderer', //ytd-metadata-row-renderer is part of the #collapsible inside ytd-expander

        'ytd-moving-thumbnail-renderer',
        'ytd-thumbnail-overlay-toggle-button-renderer',
        'ytd-thumbnail-overlay-bottom-panel-renderer', 'ytd-thumbnail-overlay-equalizer',
        'ytd-thumbnail-overlay-now-playing-renderer', 'ytd-thumbnail-overlay-resume-playback-renderer',
        'ytd-thumbnail-overlay-side-panel-renderer', 'ytd-thumbnail-overlay-time-status-renderer',
        'ytd-thumbnail-overlay-hover-text-renderer',

        'yt-interaction',
        'tp-yt-paper-spinner-lite', 'tp-yt-paper-spinner',

        'h1', 'h2', 'h3', 'h4', 'h5', 'span', 'a',

        'meta', 'br', 'script', 'style', 'link', 'dom-module', 'template'
    ];

  function isMtoTargetSkip(mutation) {
    //skip not important mutation tartget

    if (!mutation) return true;
    let { type, target } = mutation

    if (!target || target.nodeType !== 1 || type != 'childList') return true;

    let tagName = target.nodeName.toLowerCase();

    if (mutation_target_tag_ignorelist.includes(tagName)) return true;

    switch (tagName) {


      case 'ytd-expander':
        if (target.id == 'expander' && Q.comments_section_loaded == 1 && target.classList.contains('ytd-comment-renderer')) return true; // load comments
        return false;

      case 'div':

        if (target.id == 'contents') {
          return false;
        }
        if (mutation_div_id_ignorelist.includes(target.id)) return true;

        for (const c of target.classList) {
          if (mutation_div_class_ignorelist.includes(c)) return true;
        }

        return false;

    }

    return false;

  }


  function mtf_forceCheckLiveVideo() {
    // once per $$player-playback-timestamp$$ {#ytd-player .ytp-time-display} &&  $$chat-frame$$ {ytd-live-chat-frame#chat} detection
    // reset after popstatechange / videochange

    if(mtf_forceCheckLiveVideo_disable) return ;

    let ytdFlexyElm = kRef(ytdFlexy);
    if (!scriptEnable || !ytdFlexyElm) return ;

    const playerLabel = document.querySelector('ytd-watch-flexy:not([hidden]) #ytd-player .ytp-time-display') && document.querySelector('ytd-watch-flexy:not([hidden]) ytd-live-chat-frame#chat')
    if (!playerLabel) return ;
    mtf_forceCheckLiveVideo_disable = 1;
    timeline.setTimeout(FP.fireOnce_forceCheckLiveVideo_tf, 170)
    return ;
  }

  // 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 = Q.mutationTarget || ytdFlexyElm;

    let comments = rootElement.querySelector('#primary ytd-watch-metadata ~ 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() {
    
    /** @type {HTMLElement | null} */
    let ytdFlexyElm = kRef(ytdFlexy);
    if (!scriptEnable || !ytdFlexyElm) return;

    /** @type {HTMLElement | null} */
    const rootElement = Q.mutationTarget || ytdFlexyElm;

    let button = rootElement.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() {

    /** @type {HTMLElement | null} */
    let ytdFlexyElm = kRef(ytdFlexy);
    if (!scriptEnable || !ytdFlexyElm) return;

    /** @type {HTMLElement | null} */
    const rootElement = Q.mutationTarget || ytdFlexyElm;

    let ple1 = rootElement.querySelector("*:not(#ytd-userscript-playlist) > ytd-playlist-panel-renderer#playlist");
    if (ple1) {
      let ct = Date.now();
      let truePlaylist = null;
      let truePlaylist_items = document.querySelector('ytd-playlist-panel-renderer#playlist #items:not(:empty)');
      if (truePlaylist_items) {

        let pElm = truePlaylist_items.parentNode;
        while (pElm && pElm.nodeType === 1) {
          if (pElm.id == 'playlist') {
            pElm.setAttribute('tabview-true-playlist', ct)
            truePlaylist = pElm;
            break;
          }
          pElm = pElm.parentNode;
        }

      }

      if (!truePlaylist) truePlaylist = ple1; // NOT NULL

      for (const s of document.querySelectorAll(`*:not(#ytd-userscript-playlist) > ytd-playlist-panel-renderer#playlist:not([tabview-true-playlist="${ct}"])`))
        s.parentNode.removeChild(s);

      let $wrapper = getWrapper('ytd-userscript-playlist')
      $wrapper.append(truePlaylist).appendTo(document.querySelector("#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;

    if (no_fix_contents_until < Date.now()) {
      const content = document.querySelector('#meta-contents ytd-expander > #content, #tab-info ytd-expander > #content')
      if (content) {
        no_fix_contents_until = Date.now() + 3000;
        timeline.setTimeout(function() {
          const expander = content.parentNode;

          if (expander.hasAttribute('collapsed')) wAttr(expander, 'collapsed', false);
          expander.style.setProperty('--ytd-expander-collapsed-height', '');

          let btn1 = expander.querySelector('tp-yt-paper-button#less:not([hidden])');
          let btn2 = expander.querySelector('tp-yt-paper-button#more:not([hidden])');

          if (btn1) wAttr(btn1, 'hidden', true);
          if (btn2) wAttr(btn2, 'hidden', true);
        }, 40);

      }
    }

    if (no_fix_playlist_until < Date.now()) {
      // just in case the playlist is collapsed
      const playlist = document.querySelector('#tab-list ytd-playlist-panel-renderer#playlist')
      if (playlist) {
        no_fix_playlist_until = Date.now() + 3000;
        timeline.setTimeout(function() {
          if (playlist.hasAttribute('collapsed')) wAttr(playlist, 'collapsed', false);
          if (playlist.hasAttribute('collapsible')) wAttr(playlist, 'collapsible', false);
        }, 40)
      }
    }


  }



  function isNullComments() {

    let comments = document.querySelector('ytd-comments#comments')
    if (!comments || comments.hasAttribute('hidden')) return true;

  }

  function _innerCommentsLoader( /** @type {HTMLElement} */ rootElement) {

    /** @type {HTMLElement | null} */
    let ytdFlexyElm = kRef(ytdFlexy);
    if (!scriptEnable || !ytdFlexyElm) return;
    
    if (deferredVarYTDHidden) return;


    let messageElm, messageStr, commentRenderer;
    let diffCSS = `[tabview-cache-time="${sect_hTime}"]`

    if (commentRenderer = rootElement.querySelector(`ytd-comments#comments #count.ytd-comments-header-renderer:not(${diffCSS})`)) {

      scheduledCommentRefresh=false;
      let eTime = Date.now();

      sect_holder = mWeakRef(commentRenderer);
      sect_hText = commentRenderer.textContent;
      sect_hTime = eTime;
      commentRenderer.setAttribute('tabview-cache-time', eTime);

      return {
        status: 1,
        f: () => {

          let span = document.querySelector("span#tab3-txt-loader")
          let r = '0';
          let txt = commentRenderer.textContent
          if (typeof txt == 'string') {
            let m = txt.match(/[\d\,\s]+/)
            if (m) {
              r = m[0].trim()


            }
          }
          
          if (span){

            let tab_btn = span.closest('.tab-btn[userscript-tab-content="#tab-comments"]')
            if(tab_btn)tab_btn.setAttribute('loaded-comment','normal')
            span.textContent = r;
          } 
          //console.log(754)
          mtoInterval = mtoInterval2;
          scheduledCommentRefresh=false;
          setCommentSection(1);
          comments_section_loaded_elm = mWeakRef(document.querySelector('ytd-comments#comments div#contents'))
        }
      }

    } else if ((messageElm = rootElement.querySelector(`ytd-item-section-renderer#sections #header ~ #contents > ytd-message-renderer:not(${diffCSS})`)) && (messageStr = (messageElm.textContent || '').trim())) { //ytd-message-renderer
      // 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

      scheduledCommentRefresh=false;
      let eTime = Date.now();

      sect_holder = mWeakRef(messageElm);
      sect_hText = messageElm.textContent;
      sect_hTime = eTime;
      messageElm.setAttribute('tabview-cache-time', eTime);

      return {
        status: 2,
        f: () => {
          timeline.setTimeout(function() {
            let span = document.querySelector("span#tab3-txt-loader")
            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
                }
              }
            }
            if (span){
              let tab_btn = span.closest('.tab-btn[userscript-tab-content="#tab-comments"]')
              if(tab_btn)tab_btn.setAttribute('loaded-comment','message')
              span.textContent ='\u200B';
            } 
            mtoInterval = mtoInterval2;
            scheduledCommentRefresh=false;
            setCommentSection(1);
            comments_section_loaded_elm = mWeakRef(document.querySelector('ytd-comments#comments div#contents'))
          }, 40);
        }
      }

    }

  }

  const atChange = async ()=>{

    // call when video change / popstate change / switch back from mini-view

    let ytdFlexyElm = kRef(ytdFlexy);
    if(!ytdFlexyElm) return;

    mtf_forceCheckLiveVideo_disable = 0; 
    mtf_forceCheckLiveVideo();

    if(Q.comments_section_loaded===0){

      const tabBtn = document.querySelector('[userscript-tab-content="#tab-comments"].tab-btn-hidden')
      if(tabBtn) {
        tabBtn.classList.remove("tab-btn-hidden") //if contains
      }

    }
  }



  function hookSection(){


    //console.log(3300)

    let ytdFlexyElm = kRef(ytdFlexy);
    if (!scriptEnable || !ytdFlexyElm) return ;

    const comments = ytdFlexyElm.querySelector('ytd-comments#comments')
    if (!comments) return ;

    if(deferredVarYTDHidden) return;

    //console.log(3310)

    //new MutationObserver(function(){console.log(comments.querySelectorAll('*').length)}).observe(comments,{ attributes: true, childList: true, subtree: true })
    

    function foundSection(sections){

      
      //console.log(3320)

      function ulfx() {
        //console.log(1220)


        const rootElement = comments.parentNode


        if (pendingOne && comments.querySelectorAll('[tabview-cache-time]').length > 1) return;
        pendingOne = false;
        if(!rootElement) return; //prevent unknown condition
        


        let innerCommentsLoaderRet = _innerCommentsLoader(rootElement);

        //console.log(1222,_innerCommentsLoader)

        if (!innerCommentsLoaderRet) {


          let holder = kRef(sect_holder);
          if (holder && comments.contains(holder)) {

            if (sect_lastUpdate) return;
            sect_lastUpdate = 1;

            timeline.setTimeout(function() {
              //console.log(2633)
              if (holder.textContent !== sect_hText && sect_hTime > 0) {

                if (comments.querySelectorAll('[tabview-cache-time]').length === 1) {

                  sect_hTime--;

                  let innerCommentsLoaderRet = _innerCommentsLoader(rootElement);

                  if (innerCommentsLoaderRet) {
                    //console.log(9442)
                    innerCommentsLoaderRet.f();
                    fetchCommentsFinished();
                  }

                  pendingOne = false;

                }


              }
              sect_lastUpdate = 0
            }, 400)

          } else if (sect_holder) {
            sect_holder = null
            sect_hTime = 0;
            sect_lastUpdate = 0;
            pendingOne = false;
            timeline.setTimeout(()=>{
              if(!deferredVarYTDHidden && scheduledCommentRefresh && Q.comments_section_loaded>0 && comments.hasAttribute('hidden')){

                scheduledCommentRefresh=false; // logic tbc
                resetCommentSection();
                
              }
            },142);
            
            Q.mutationTarget=null;
            FP.mtf_attrComments(); // in case for popstate
          }


        } else {

          //console.log(9443)
          innerCommentsLoaderRet.f();
          fetchCommentsFinished();

          pendingOne = true;

        }



      }

      function observerHook(mutationsList, observer){

        let valid = false
        for (const mutation of mutationsList) {

          let target = mutation.target;
          if (!target) break;
          if (!target.id) break;
          let classList = target.classList;


          if (
            classList.contains('ytd-item-section-renderer') ||
            classList.contains('ytd-comments-header-renderer') ||
            classList.contains('ytd-message-renderer')) {
            valid = true
            break;
          }

          break; // only outest mutation


        }
        if (valid) ulfx();
      }

      
      if(sections.hasAttribute('tabview-comment-section-checked')){

        //console.log(3612)
        ulfx();
        

      }else{

        pendingOne = false;
        //console.log(3611)
        const observer = new MutationObserver(observerHook)
        sections.setAttribute('tabview-comment-section-checked','')
        const config = { childList: true, subtree: true }; 
        observer.observe(sections, config);
        ulfx();


      }



    }
    //console.log(3241, location.href)
    
    let cid_render_section = timeline.setInterval(function() {

     // console.log(3244, location.href)


      const rootElement = comments.parentNode

      let sections = rootElement.querySelector('ytd-comments#comments > ytd-item-section-renderer#sections');


      if (sections) {

        timeline.clearInterval(cid_render_section);
        foundSection(sections);

      }

    }, 30)



  }


  let mtc_store=0;
  let mtc_cid = 0;
  const cssOnceFunc = {
    [`ytd-comments#comments:not([o3r-${sa_comments}])`]:(comments)=>{
      
      // once per {ytd-comments#comments} detection

      let ytdFlexyElm = kRef(ytdFlexy);
      if (!scriptEnable || !ytdFlexyElm) return;

      if (!comments) return;




      if(mtoVisibility_Comments.bindElement(comments)){
        mtoVisibility_Comments.observer.check(9);
      }
      
      pageInit_attrComments = true
      hookSection();

      //let dComments = comments
      //timeline.setTimeout(() => nativeFunc(dComments, "loadComments"), 20)
      //comments = null;

    },
    [`ytd-playlist-panel-renderer#playlist:not([o3r-${sa_playlist}])`]:(playlist)=>{

        // once per {ytd-playlist-panel-renderer#playlist} detection

        let ytdFlexyElm = kRef(ytdFlexy);
        if (!scriptEnable || !ytdFlexyElm) return;

        if (!playlist) return;

        if(mtoVisibility_Playlist.bindElement(playlist)){
          mtoVisibility_Playlist.observer.check(9)  //delay check required for browser bug - hidden changed not triggered 
        }
        playlist = null;

        return;

    },
    '#meta-contents ytd-expander:not([tabview-info-expander])':(expander)=>{

        // once per $$native-info-description$$ {#meta-contents ytd-expander} detection

        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)
  
  

    },
    '#description-and-actions.style-scope.ytd-watch-metadata > #description ytd-text-inline-expander:not([tabview-removed-duplicate])': (teaserInfo)=>{


      // 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;

      teaserInfo.setAttribute('tabview-removed-duplicate','')


      teaserInfo.dispatchEvent(new CustomEvent('tabview-no-duplicate-info'))
      //console.log(56)

    }
  }
  const cssOnce = Object.keys(cssOnceFunc).join(', ')

  const regularCheck = (addP, removeP, mutationTarget)=>{


    let isInvalidAdding = mutationTarget && !mutationTarget.parentNode

      
    let promisesForAddition = !scriptEnable ? [] : addP > 0 && !isInvalidAdding ? [

        (async () => {

          
          //$callOnce('mtf_checkFlexy');

          const rootElement = Q.mutationTarget || kRef(ytdFlexy);

          if(rootElement){

            let fElms = rootElement.querySelectorAll(cssOnce)
            if(fElms.length>0){

              for(const fElm of fElms){
                for(let cssSelctor in cssOnceFunc){
                  if(fElm.matches(cssSelctor)){
                    let f= cssOnceFunc[cssSelctor]
                    if(typeof f=='function') f(fElm);
                    break;
                  }

                }
              }

              //$callOnce('mtf_initalAttr_comments');
              //$callOnce('mtf_initalAttr_playlist');
              // $callOnce('mtf_checkDescriptionLoaded');
            }

          }

        })(),
    
        (async () => {


          FP.mtf_initalAttr_chatroom();
          FP.mtf_initalAttr_engagement_panel();
          
          //$callOnce('mtf_initalAttr_chatroom');
         // $callOnce('mtf_initalAttr_engagement_panel');

          mtf_forceCheckLiveVideo();
            mtf_append_comments();
            mtf_liveChatBtnF();
            fixTabs();
            mtf_AfterFixTabs();
            mtf_append_playlist();

        })()

    ] : [];
    
  
    
    let promisesForEveryMutation = !scriptEnable ? [] : [
        (async () => {
            mtf_fix_details();
            mtf_ChatExist();
        })()
    ];

    return [...promisesForAddition, ...promisesForEveryMutation]


  }

  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)

    })

  }

  const FP = {

    mtoNav_delayedF: () => {

      let { addP, removeP, mutationTarget } = Q;

      Q.addP = 0;
      Q.removeP = 0;
      
      mtc_store=Date.now()+1870


      let regularChecks = regularCheck(addP, removeP, mutationTarget);

      Promise.all(regularChecks).then(() => {
        regularChecks = null;
        
        mtc_store= Date.now() + 3870 // pending Comments start to load
        
        Q.mutationTarget = null;

        Q.mtoNav_requestNo--;
        //console.log('motnav reduced to', mtoNav_requestNo)
        if (Q.mtoNav_requestNo > 0) {
          Q.mtoNav_requestNo = 1;
          setTimeout(FP.mtoNav_delayedF, mtoInterval);
        }
      })


    },

    mtoNavF: ( /** @type {MutationRecord[]} */ mutations,  /** @type {MutationObserver} */   observer) => {
      //subtree DOM mutation checker - {ytd-watch-flexy} \single \subtree

      newVideoPageCheck()

      if (!scriptEnable) return;
      if (deferredVarYTDHidden) return;

      let ch = false;

      let reg = [];
      let dTarget = null;

      let wAddP = 0,
        wRemoveP = 0;

      let _last_mto_target = null;
      let _last_mto_target_valid = null;

      for (const mutation of mutations) {
        if (!mutation || !mutation.target || !mutation.target.parentNode) continue;

        let elementalMutation = false;
        let tAddP = 0,
          tRemoveP = 0;

        for (const addedNode of mutation.addedNodes) { //theoretically faster: only reading of states
          if (addedNode.nodeType === 1) {
            tAddP++;
            elementalMutation = true;
          }
        }

        for (const removedNode of mutation.removedNodes) { //theoretically faster: only reading of states
          if (removedNode.nodeType === 1) {
            tRemoveP++;
            elementalMutation = true;
          }
        }

        if (elementalMutation) { //skip all addition and removal operations without elemental changes (e.g. textNode modification)

          if (_last_mto_target === mutation.target) {
            // due to addition and removal operations to the same DOM
            if (_last_mto_target_valid) {
              // AddP & RemoveP is still valid
              wAddP += tAddP;
              wRemoveP += tRemoveP;
            }
            continue;
          }
          _last_mto_target = mutation.target;

          if (isMtoTargetSkip(mutation)) {
            _last_mto_target_valid = false;
            continue; //theoretically slower: creation of string variables
          } else {
            _last_mto_target_valid = true;
            wAddP += tAddP;
            wRemoveP += tRemoveP;
          }



          ch = true;

          reg.push(mutation);

          if (dTarget === null) dTarget = mutation.target; //first
          else if (dTarget === true) {} //ytdFlexy
          else if (dTarget.contains(mutation.target)) {} //first node is the container to all subsequential targets
          else { dTarget = true; } //the target is not the child of first node

        }

      }

      if (!ch) return; // dTarget must be true OR HTMLElement

      if (dTarget === true) dTarget = kRef(ytdFlexy); // major mutation occurance
      else if (dTarget === kRef(comments_section_loaded_elm) && wAddP > wRemoveP) return true; // ignore if comments are loaded (adding comments)
      else if (isMtoOverallSkip(dTarget)) return; // allow for multiple mutations at the same time - determinated after looping



      // 4 ~ 16 times per full page loading

      Q.addP += wAddP;
      Q.removeP += wRemoveP;

      if (Q.mutationTarget === null) Q.mutationTarget = dTarget;
      else if (Q.mutationTarget != dTarget) Q.mutationTarget = kRef(ytdFlexy);

      //console.log(prettyElm(dTarget), wAddP , wRemoveP, mtoInterval)
      //console.log(prettyElm(dTarget), reg.map(m=>prettyElm(m.target)))
      //console.log(7015, performance.now())



      Q.mtoNav_requestNo++;
      if (Q.mtoNav_requestNo == 1) setTimeout(FP.mtoNav_delayedF, mtoInterval);

    },


    mtoBodyF: ( /** @type {MutationRecord[]} */ mutations,  /** @type {MutationObserver} */   observer) => {
      //subtree DOM mutation checker - {body} \single \subtree

      if (!scriptEnable) return;
      if (deferredVarYTDHidden) 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)

      if (!scriptEnable) return;
      if (deferredVarYTDHidden) return;
      let cssElm = kRef(ytdFlexy);
      if (!cssElm) return;

      let playlist = document.querySelector('ytd-playlist-panel-renderer#playlist')
      const tabBtn = document.querySelector('[userscript-tab-content="#tab-list"]');
      let isPlaylistHidden = playlist.hasAttribute('hidden')
      //console.log(1212.2, isPlaylistHidden, playlist.getAttribute('hidden'))
      if (tabBtn) {
        //console.log('attr playlist changed')
        if (tabBtn.classList.contains('tab-btn-hidden') && !isPlaylistHidden) {
          //console.log('attr playlist changed - no hide')
          tabBtn.classList.remove("tab-btn-hidden");
        } else if (!tabBtn.classList.contains('tab-btn-hidden') && isPlaylistHidden) {
          //console.log('attr playlist changed - add hide')
          hideTabBtn(tabBtn);
        }
      }
      /* visible layout for triggering hidden removal */
      akAttr(cssElm, 'tabview-youtube-playlist', isPlaylistHidden);
    },
    mtf_attrComments: (attrName, newValue) => {
      //attr mutation checker - {ytd-comments#comments} \single
      //::attr ~ hidden

      let ytdFlexyElm = kRef(ytdFlexy);
      if (!scriptEnable || !ytdFlexyElm) return;
      if (deferredVarYTDHidden) 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')

      mtoInterval = mtoInterval1;
      
      mtc_store=Date.now()+2870

      if( mtf_forceCheckLiveVideo_disable === 2 ){

      }else if (!isCommentHidden) {
        

        akAttr(ytdFlexyElm, 'tabview-youtube-comments', false, 'K');



        //console.log('attr comments changed - no hide')
        tabBtn.classList.remove("tab-btn-hidden") //if contains

            
        
        //console.log(703)

        mtc_cid&&timeline.clearInterval(mtc_cid);

      } else if (isCommentHidden) {

        akAttr(ytdFlexyElm, 'tabview-youtube-comments', true, 'K'); 
        
        timeline.setTimeout(function(){
          if(!deferredVarYTDHidden && scheduledCommentRefresh && Q.comments_section_loaded>0 && comments.hasAttribute('hidden')){
            //console.log(3434)
            scheduledCommentRefresh=false;  //logic tbc
            resetCommentSection();
            //console.log(8022)
          }
        },142)
 

        if(!mtc_cid) mtc_cid=timeline.setInterval(()=>{
          if(mtc_store>Date.now()) return;
            timeline.clearInterval(mtc_cid)
            mtc_cid=0;
            if(mtf_forceCheckLiveVideo_disable===2)return;
            if(isNullComments()) _disableComments();

        },80)

      }


    },


    mtf_attrChatroom: (attrName, newValue) => {
      //attr mutation checker - {ytd-live-chat-frame#chat} \single
      //::attr ~ collapsed

      let ytdFlexyElm = kRef(ytdFlexy);
      if (!scriptEnable || !ytdFlexyElm) return;
      if (deferredVarYTDHidden) return;

      layoutStatusMutex.lockWith(unlock => {

        const chatBlock = document.querySelector('ytd-live-chat-frame#chat')
        const cssElm = kRef(ytdFlexy)

        if (!chatBlock || !cssElm) {
          unlock();
          return;
        }

        if (deferredVarYTDHidden) {
          unlock();
          return;
        }

        if (!cssElm.hasAttribute('userscript-chatblock')) wAttr(cssElm, 'userscript-chatblock', true);
        let isCollapsed = !!chatBlock.hasAttribute('collapsed');
        wAttr(cssElm, 'userscript-chat-collapsed', isCollapsed);

        if (cssElm.hasAttribute('userscript-chatblock') && !isCollapsed) lastShowTab = '#chatroom';

        if (!isCollapsed && document.querySelector('#right-tabs .tab-btn.active') && isWideScreenWithTwoColumns() && !isTheater()) {
          switchTabActivity(null);
          timeline.setTimeout(unlock, 40);
        } else {
          unlock();
        }

        if (!isCollapsed) {
          runAfterExpandChat();
        } else {
          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 (deferredVarYTDHidden) 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) {
            lastShowTab = `#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_playlist: () => {
      
    },

    mtf_initalAttr_comments: () => {
      
    },

    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 = Q.mutationTarget || ytdFlexyElm;


      if (!mgChatFrame.inPage()) {

        mtoVisibility_Chatroom.clear(true);

        let chatroom = rootElement.querySelector(`ytd-live-chat-frame#chat:not([${sa_chatroom}]`)
        if (chatroom) {
          mgChatFrame.setVar(chatroom);

          if(mtoVisibility_Chatroom.bindElement(chatroom)){
            mtoVisibility_Chatroom.observer.check(9)
          }

          chatroom = null
        }
      }
      return true;

    },

    mtf_initalAttr_engagement_panel: () => {
      // every per $$non-checked-section-list$$ {ytd-watch-flexy ytd-engagement-panel-section-list-renderer:not([o3r-XXXXX])} detection
      
      let ytdFlexyElm = kRef(ytdFlexy);
      if (!scriptEnable || !ytdFlexyElm) return true;

      const rootElement = Q.mutationTarget || ytdFlexyElm;

      let toCheck = false;
      for (const engagement_panel of rootElement.querySelectorAll(
        `ytd-watch-flexy ytd-engagement-panel-section-list-renderer:not([o3r-${sa_epanel}])`
        )) {



        let bindRes = mtoVisibility_EngagementPanel.bindElement(engagement_panel, {
          attributes: true,
          attributeFilter: ['visibility'],
          attributeOldValue: true
        })
        

        if(bindRes) toCheck = true;
      }

      if (toCheck) FP.mtf_attrEngagementPanel()

      return true;
    },



    //live-chat / chat-replay

    fireOnce_forceCheckLiveVideo_tf: () => {
      // once per $$player-playback-timestamp$$ {#ytd-player .ytp-time-display} &&  $$chat-frame$$ {ytd-live-chat-frame#chat} detection

      let ytdFlexyElm = kRef(ytdFlexy);
      if (!scriptEnable || !ytdFlexyElm) return;

      let elm = document.querySelector('#ytd-player .ytp-time-display');
      if (elm && elm.classList.contains('ytp-live')) {
        //console.log(7006)
        ytdFlexyElm.setAttribute('userscript-chatblock', 'chat-live')
        //console.log(2441)
        mtf_forceCheckLiveVideo_disable = 2;
        
        _disableComments();
        
       //console.log(701)
        //disableComments_LiveChat();
      }
    },

    

    //comments
/*
    mtf_advancedComments: () => {
      // once per {ytd-comments#comments #continuations} detection

      //let ytdFlexyElm = kRef(ytdFlexy);
      //if (!scriptEnable || !ytdFlexyElm) return true;

      //const rootElement = Q.mutationTarget || ytdFlexyElm;
      //const continuations = document.querySelector("ytd-comments#comments #continuations");
      //if (!continuations) return true;
      //requestingComments = document.querySelector('ytd-comments#comments');
      //scrollForComments();
      return false;
    }
    */


  }





  /** @type {WeakRef | null} */ 
  let displayedPlaylist = null;
  
  /** @type {WeakRef | null} */ 
  let scrollingVideosList = null;

  let scriptEnable = false;
  let scriptEC = 0;
  let lastShowTab = null;

  let _cachedLastVideo = null;
  let videoListBeforeSearch = null;
  let no_fix_contents_until = 0;
  let no_fix_playlist_until = 0;
  let statusCollasped = 0;

  /** @type {WeakRef | null} */ 
  let ytdFlexy = null;

  function pluginUnhook() {
    _pluginUnhook();
    resetCommentSection();
  }

  function _pluginUnhook() {

    sect_lastUpdate = 0;
    sect_holder = null;
    sect_hTime = 0;

    //console.log(8001)

    videoListBeforeSearch = null;
    statusCollasped = 0;
    _cachedLastVideo = null;
    lastShowTab = null;
    displayedPlaylist = null;
    scrollingVideosList = null;
    scriptEnable = false;
    scriptEC++;
    if (scriptEC > 788888888) scriptEC = 188888888;
    ytdFlexy = null;
    wls.layoutStatus = null;

    //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)
    mtoMutation_watchFlexy.clear(true)
    //FOnce.mtf_checkFlexy = null;
    //FOnce.mtf_initalAttr_comments = null;
    //FOnce.mtf_initalAttr_playlist = null;
    //FOnce.mtf_initalAttr_chatroom = null;
    //FOnce.mtf_initalAttr_engagement_panel = null;
    //FOnce.mtf_advancedComments = null;
    //FOnce.mtf_checkDescriptionLoaded = null;
    //FOnce.mtf_forceCheckLiveVideo = null;
    Q.mtf_chatBlockQ = null;


    mtoInterval = mtoInterval1;

  }

  let comments_section_loaded_elm = null;



  function getTabsHTML() {

    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>`

    let 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 = [
            `<a id="tab-btn1" data-name="info" userscript-tab-content="#tab-info" class="tab-btn">${sTabBtnInfo}${str1}</a>`,
            `<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>`,
            `<a id="tab-btn4" userscript-tab-content="#tab-videos" data-name="videos" class="tab-btn">${sTabBtnVideos}${str1}</a>`,
            `<a id="tab-btn5" userscript-tab-content="#tab-list" class="tab-btn">${sTabBtnPlayList}${str1}</a>`
        ].join('')

    let 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 tab-content-hidden" userscript-scrollbar-render></div>
                <div id="tab-comments" class="tab-content-cld tab-content-hidden" userscript-scrollbar-render></div>
                <div id="tab-videos" class="tab-content-cld tab-content-hidden" userscript-scrollbar-render></div>
                <div id="tab-list" class="tab-content-cld tab-content-hidden" userscript-scrollbar-render></div>
            </div>
        </div>
        `;

    return addHTML

  }


  function onVideoChange() {
    // for popstate & next video

    newVideoPageCheck();

    scheduledCommentRefresh=true;
    let comments = document.querySelector('ytd-comments#comments')
    
    if(!deferredVarYTDHidden && scheduledCommentRefresh && Q.comments_section_loaded>0 && comments.hasAttribute('hidden')){

      scheduledCommentRefresh=false; 
      
      // comment tab btn - hide by timer in attribute change
      resetCommentSection();
    }

    mtf_forceCheckLiveVideo_disable = 0
    mtc_store= Date.now()+3870

    if (deferredVarYTDHidden) {  // reset info when hidden
      
      resetCommentSection();
    }else{
      checkVisibleEngagementPanel();
      atChange(); // in case no mutation occurance
    }



  }

  function onNavigationEnd(evt) {
    forceConfig();
    /*
    console.log(window.ytcfg)
    try{
    window.ytcfg.set({
      "EXPERIMENT_FLAGS": {"kevlar_watch_metadata_refresh":false}})
    }catch(e){}
    */

    newVideoPageCheck(); // required for init

    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('loadedmetadata', function(evt) {
        if (evt.target.matches('video[tabview-mainvideo]')) {
          setTimeout(onVideoChange, 30)
        }
      }, 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);

        if (!document.querySelector('script#userscript-tabview-injection-1')) {

          if (isMyScriptInChromeRuntime()){

            addScriptByURL(window.chrome.runtime.getURL('js/injection_script_1.js')).id = 'userscript-tabview-injection-1';
          }
          else{

            let injection_script = GM_getResourceText("injectionJS1")
            addScript(`${injection_script}`).id = 'userscript-tabview-injection-1';
          }
        }

        let ytdFlexyElm = document.querySelector('ytd-watch-flexy')

        if(!ytdFlexyElm) return;


        scriptEnable = true;
        scriptEC++;

        no_fix_contents_until = 0;
        no_fix_playlist_until = 0;

        ytdFlexy = mWeakRef(ytdFlexyElm)

        let timeoutR_findRelated = new Timeout();
        timeoutR_findRelated.set(function() {
          let ytdFlexyElm = kRef(ytdFlexy);
          if(!ytdFlexyElm) return true;
          let related = ytdFlexyElm.querySelector("#related");
          if (!related) return true;
          foundRelated(related);
        }, 100, 10)

        function foundRelated(related) {
          let promise = Promise.resolve();
          if (!document.querySelector("#right-tabs")) {
            promise = promise.then(() => {
              $(getTabsHTML()).insertBefore(related).attr('data-dom-created-by-tabview-youtube', scriptVersionForExternal);
            })
          }
          promise.then(runAfterTabAppended)
        }

        /*
        setTimeout(() => {
          for (const s of document.querySelectorAll('#right-tabs [userscript-scrollbar-render]')) {
            Promise.resolve(s).then(s => {
              if (s && s.scrollTop > 0) s.scrollTop = 0;
              let child = s.firstElementChild;
              if (child && child.scrollTop > 0) child.scrollTop = 0;
            });
          }
        }, 90)
        */


      }

      tf();

    } else if (mRet === 3) {

      if(!scriptEnable) return;

      let elmComments = document.querySelector('ytd-comments#comments')

      if (elmComments) {
        nativeFunc(elmComments, "loadComments")
      }

    } else if (mRet === 0) {
      

      pluginUnhook(); // in case not triggered by popstate - say mini playing

    }

    if(mRet & 1){
      
      setTimeout(()=>{
        
        Q.mutationTarget=null;
        atChange(); // in case no mutation occurance
        FP.mtf_attrComments()
      },160);
        
    }

  }


  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 = $(`<div id="${wrapperId}"></div>`)
    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', '')

    

    
    //console.log('unc02')

    // append the next videos 
    // it exists as "related" is already here
    fixTabs();

    // just switch to the default tab
    setToActiveTab();


    prepareTabBtn();


    // append the detailed meta contents to the tab-info
    // ** FOnce.mtf_checkDescriptionLoaded = FP.mtf_checkDescriptionLoaded;
    // ** if (Q.mutationTarget === null) $callOnceAsync('mtf_checkDescriptionLoaded');

    // force window scroll when #continuations is first detected and #comments still [hidden]
    //FOnce.mtf_advancedComments = FP.mtf_advancedComments;
    //if (Q.mutationTarget === null) $callOnceAsync('mtf_advancedComments');

    // 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
    //FOnce.mtf_forceCheckLiveVideo = FP.mtf_forceCheckLiveVideo;
    //if (Q.mutationTarget === null) $callOnceAsync('mtf_forceCheckLiveVideo');
    //mtf_forceCheckLiveVideo();


    // Attr Mutation Observer - #playlist - hidden
    //clearMutationObserver(mtoVs, 'mtoVisibility_Playlist')
    // Attr Mutation Observer callback - #playlist - hidden

    // pending for #playlist and set Attribute Observer
    // ** FOnce.mtf_initalAttr_playlist = FP.mtf_initalAttr_playlist
    // ** if (Q.mutationTarget === null) $callOnceAsync('mtf_initalAttr_playlist');

    // Attr Mutation Observer - ytd-comments#comments - hidden
    //clearMutationObserver(mtoVs, 'mtoVisibility_Comments')
    // Attr Mutation Observer callback - ytd-comments#comments - hidden

    // pending for #comments and set Attribute Observer
    // ** FOnce.mtf_initalAttr_comments = FP.mtf_initalAttr_comments;
    // ** if (Q.mutationTarget === null) $callOnceAsync('mtf_initalAttr_comments');


    //clearMutationObserver(mtoVs, 'mtoVisibility_Chatroom');
    //FOnce.mtf_initalAttr_chatroom = FP.mtf_initalAttr_chatroom
    //if (Q.mutationTarget === null) $callOnceAsync('mtf_initalAttr_chatroom');

    // clearMutationObserver(mtoVs, 'mtoVisibility_EngagementPanel');
    // for (const engagement_panel of document.querySelectorAll('ytd-watch-flexy ytd-engagement-panel-section-list-renderer:not([tabview-attr-checked])')) {
    //   engagement_panel.removeAttribute('tabview-attr-checked');
    // }
    //FOnce.mtf_initalAttr_engagement_panel = FP.mtf_initalAttr_engagement_panel
    //if (Q.mutationTarget === null) $callOnceAsync('mtf_initalAttr_engagement_panel');


    mtoFlexyAttr.clear(true)
    mtf_checkFlexy()


    document.querySelector("#right-tabs .tab-content").addEventListener('scroll', makeBodyScrollByEvt, true);


    Q.addP = 0;
    Q.removeP = 0;
    Q.mutationTarget = null;

    Q.mtoNav_requestNo = 0;


    mtoMutation_watchFlexy.bindElement(ytdFlexyElm, {
      childList: true,
      subtree: true,
      attributes: false
    })

    // for automcomplete plugin or other userscript plugins
    // document.body for Firefox >= 60
    mtoMutation_body.bindElement(document.querySelector('body'), {
      childList: true,
      subtree: false,
      attributes: false
    })


    regularCheck(1, 0, null);

    



  }


  function fetchCommentsFinished() {
    let ytdFlexyElm = kRef(ytdFlexy);
    if (!scriptEnable || !ytdFlexyElm) return;
    if(mtf_forceCheckLiveVideo_disable===2) return;
    akAttr(ytdFlexyElm, 'tabview-youtube-comments', false, 'LS')
  }

  function setCommentSection( /** @type {number} */ value) {

    Q.comments_section_loaded = value;

    if (value === 0) {
      sect_lastUpdate = 0;
      comments_section_loaded_elm = null;
    }

  }

  function resetCommentSection(){
    

    let tab_btn = document.querySelector('.tab-btn[userscript-tab-content="#tab-comments"]')


    if(tab_btn){

      let span = tab_btn.querySelector('span#tab3-txt-loader');

      tab_btn.removeAttribute('loaded-comment')
      tab_btn.classList.remove('tab-btn-hidden')
        
      if(span){
        span.textContent='';
      }
    }


    setCommentSection(0);


  }

  function _disableComments() {
    
    //requestingComments = null;
    mtc_cid && timeline.clearInterval(mtc_cid)
    mtc_cid=0;

    if (!scriptEnable) return;
    let cssElm = kRef(ytdFlexy);
    if (!cssElm) return;

    mtoInterval = mtoInterval2;

    if(Q.comments_section_loaded>0) return; //already displayed / disabled

    scheduledCommentRefresh=false;
    setCommentSection(2);

    comments_section_loaded_elm = mWeakRef(document.querySelector('ytd-comments#comments div#contents') || null)

    let tabBtn = document.querySelector('.tab-btn[userscript-tab-content="#tab-comments"]');
    if(tabBtn) {
      tabBtn.removeAttribute('loaded-comment')
      if (!tabBtn.classList.contains('tab-btn-hidden')) {
        hideTabBtn(tabBtn)
      }
    }

    akAttr(cssElm, 'tabview-youtube-comments', true, 'D');


  }




  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 = ytd_player.querySelector('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 runAfterExpandChat() {


    if(runAfterExpandChat.cid_chatFrameCheck1) timeline.clearInterval(runAfterExpandChat.cid_chatFrameCheck1);
    if(runAfterExpandChat.cid_chatFrameCheck2) timeline.clearInterval(runAfterExpandChat.cid_chatFrameCheck2);
    runAfterExpandChat.cid_chatFrameCheck1=0;
    runAfterExpandChat.cid_chatFrameCheck2=0;

    new Promise(resolve => {

      let chatFrame_st = Date.now();
      runAfterExpandChat.cid_chatFrameCheck1 = 0;

      let sEF = new ScriptEF();
      runAfterExpandChat.cid_chatFrameCheck1 = timeline.setInterval(() => {
        if (!sEF.isValid()) return runAfterExpandChat.cid_chatFrameCheck1 = timeline.clearInterval(runAfterExpandChat.cid_chatFrameCheck1);
        let cDoc = chatFrameContentDocument();
        if (cDoc) {
          runAfterExpandChat.cid_chatFrameCheck1 = timeline.clearInterval(runAfterExpandChat.cid_chatFrameCheck1);
          resolve();
        } else if (!scriptEnable || !isChatExpand() || Date.now() - chatFrame_st > 6750) {
          runAfterExpandChat.cid_chatFrameCheck1 = timeline.clearInterval(runAfterExpandChat.cid_chatFrameCheck1);
        }
      }, 60);


    }).then(() => new Promise(resolve => {


      let chatFrame_st = Date.now();
      runAfterExpandChat.cid_chatFrameCheck2 = 0;

      let sEF = new ScriptEF();
      runAfterExpandChat.cid_chatFrameCheck2 = timeline.setInterval(() => {
        if (!sEF.isValid()) return runAfterExpandChat.cid_chatFrameCheck2 = timeline.clearInterval(runAfterExpandChat.cid_chatFrameCheck2);
        let app = chatFrameElement('yt-live-chat-app');
        if (app) {
          runAfterExpandChat.cid_chatFrameCheck2 = timeline.clearInterval(runAfterExpandChat.cid_chatFrameCheck2);
          resolve(app);
        } else if (!scriptEnable || !isChatExpand() || Date.now() - chatFrame_st > 6750*3) {
          runAfterExpandChat.cid_chatFrameCheck2 = timeline.clearInterval(runAfterExpandChat.cid_chatFrameCheck2);
        }
      }, 60);


    })).then(app => {


      let cDoc = app.ownerDocument;

      if (!scriptEnable || !isChatExpand()) return;
      if(cDoc.querySelector('#userscript-tabview-chatroom-css')) return;
      addStyle(`      
                body #input-panel.yt-live-chat-renderer::after {
                    background: transparent;
                }
                #items.yt-live-chat-item-list-renderer{
                    contain: content;
                }
                yt-live-chat-text-message-renderer{
                    contain: content;
                }
                #item-offset.yt-live-chat-item-list-renderer{
                    contain: content;
                }
                #item-scroller.yt-live-chat-item-list-renderer{
                    contain: strict;
                }
                img[width][height]{
                    contain: strict;
                }
                #item-list > yt-live-chat-item-list-renderer, #item-list > yt-live-chat-item-list-renderer > #contents{
                    contain: strict;
                }


                #chat.style-scope.yt-live-chat-renderer,
                yt-live-chat-text-message-renderer.style-scope.yt-live-chat-item-list-renderer,
                #item-scroller,
                yt-live-chat-text-message-renderer.style-scope.yt-live-chat-item-list-renderer .style-scope.yt-live-chat-text-message-renderer,
                yt-live-chat-ticker-paid-message-item-renderer
                {
                  contain: layout paint style;
                }
                 
                yt-img-shadow#author-photo.style-scope{
                  contain: layout paint style;
                  content-visibility: auto;
                  contain-intrinsic-size: 24px 24px;
                }
                 
                #item-offset.style-scope.yt-live-chat-item-list-renderer,
                #items.style-scope.yt-live-chat-item-list-renderer {
                  contain: layout paint;
                }
                 
                .style-scope.yt-live-chat-text-message-renderer {
                  cursor: default;
                }
                 
                #author-photo.style-scope.yt-live-chat-text-message-renderer,
                yt-live-chat-author-chip.style-scope.yt-live-chat-text-message-renderer,
                yt-live-chat-author-chip.style-scope.yt-live-chat-text-message-renderer ~ span#message.style-scope.yt-live-chat-text-message-renderer
                {
                  pointer-events: none;
                }
                 
                span#message.style-scope.yt-live-chat-text-message-renderer > img.emoji.yt-formatted-string.style-scope.yt-live-chat-text-message-renderer{
                  contain: layout paint style;
                  cursor: default;
                  pointer-events: none;
                }
                 
                body yt-live-chat-app{
                  contain: size layout paint style;
                  content-visibility: auto;
                  transform: translate3d(0,0,0);
                  overflow: hidden;
                }


                /*
                yt-live-chat-text-message-renderer.style-scope.yt-live-chat-item-list-renderer:nth-last-of-type(n+20){
                  content-visibility: auto;
                }
                */

                /*
                @supports (contain-intrinsic-size: auto 1px){

                  yt-live-chat-text-message-renderer.style-scope.yt-live-chat-item-list-renderer{
                    content-visibility: auto;
                    contain-intrinsic-size: auto 24px; 
                  }

                }
                */
                 
                .style-scope.yt-live-chat-item-list-renderer{
                  box-sizing: border-box; 
                }

                /*
                .style-scope.yt-live-chat-item-list-renderer[style*="--tabview-crm-height"]{
                  content-visibility: auto;
                  contain-intrinsic-size: 1px var(--tabview-crm-height, 24px); 
                }

                #item-offset.style-scope.yt-live-chat-item-list-renderer{
                  height: auto !important;
                }
                #item-offset.style-scope.yt-live-chat-item-list-renderer > #items:only-child{
                  position: relative !important;
                }*/

                #item-offset.style-scope.yt-live-chat-item-list-renderer{
                  contain: size layout paint style;
                }
                #item-offset.style-scope.yt-live-chat-item-list-renderer > #items:only-child{
                  contain: layout paint style;
                }
 

            `, cDoc.documentElement).id='userscript-tabview-chatroom-css'

            let frc= 0;
      let fullReady = ()=>{


        if(!cDoc.documentElement.hasAttribute('style') && ++frc<900) return timeline.setTimeout(fullReady,10);

        if (cDoc.querySelector('yt-live-chat-renderer #continuations')) {
          timeline.setTimeout(() => mtf_ChatExist(), 40);
          $(document.querySelector('ytd-live-chat-frame#chat')).attr('yt-userscript-iframe-loaded', '')
        }

        forceDisplayChatReplay();

        let iframe = document.querySelector('ytd-live-chat-frame iframe#chatframe');


        iframe.dispatchEvent(new CustomEvent("tabview-chatroom-ready"))



      }
      fullReady();



    })

  }

  function flexyAttr_toggleFlag(mFlag, b, flag) {
    if (b) mFlag = mFlag | flag;
    else mFlag = mFlag & ~flag;
    return mFlag;
  }

  function flexAttr_toLayoutStatus(nls, attributeName) {

    let attrElm, b, v;
    switch (attributeName) {
      case 'theater':
        nls = flexyAttr_toggleFlag(nls, isTheater(), 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_':
        nls = flexyAttr_toggleFlag(nls, isWideScreenWithTwoColumns(), LAYOUT_TWO_COLUMNS);
        break;

      case 'tabview-selection':
        b = isNonEmptyString(kRef(ytdFlexy).getAttribute('tabview-selection'));
        nls = flexyAttr_toggleFlag(nls, b, LAYOUT_TAB_EXPANDED);
        break;

      case 'fullscreen':
        b = isNonEmptyString(kRef(ytdFlexy).getAttribute('fullscreen'));
        nls = flexyAttr_toggleFlag(nls, b, LAYOUT_FULLSCREEN);
        break;

      case 'userscript-engagement-panel':
        v = kRef(ytdFlexy).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;

    //console.log(15340)
    ytdFlexyHiddenCheck(mutations);

    const old_layoutStatus = wls.layoutStatus
    if (old_layoutStatus === null) 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);
        }
      }
    }

    if (new_layoutStatus !== old_layoutStatus) wls.layoutStatus = new_layoutStatus

  }

  const mtf_checkFlexy = () => {
    // once per $$native-ytd-watch-flexy$$ {ytd-watch-flexy} detection

    let ytdFlexyElm = kRef(ytdFlexy);
    if (!scriptEnable || !ytdFlexyElm) return true;


    wls.layoutStatus = null;

    let isFlexyHidden = (deferredVarYTDHidden = 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)
        wAttr(ytdFlexyElm, 'userscript-chat-collapsed', attr_chatcollapsed)
      }
    }

    let rTabSelection = [...ytdFlexyElm.querySelectorAll('.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 = 0;
    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')

    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
    })

    ytdFlexyHiddenCheckBasic(ytdFlexyElm);


    let columns = document.querySelector('ytd-page-manager#page-manager #columns')
    if (columns) {
      wAttr(columns, 'userscript-scrollbar-render', true);
    }

    return false;
  }
  

  function ytdFlexyHiddenCheckBasic(ytdFlexyElm){
    // for both hidden and non-hidden ytd-watch-flexy

    deferredVarYTDHidden = ytdFlexyElm.hasAttribute('hidden');

   // console.log(15403)
    document.documentElement.classList.toggle('tabview-normal-player',!deferredVarYTDHidden)

  }

  function checkVisibleEngagementPanel(){
    
    if(storeLastPanel){

      let elm_storeLastPanel = kRef(storeLastPanel);

      if(elm_storeLastPanel && !isDOMVisible(elm_storeLastPanel) ){
        storeLastPanel=null;
        ytBtnCloseEngagementPanels();
      }

    }

  }

  function ytdFlexyHiddenCheck(mutations) {

    //console.log(15350)

    const ytdFlexyElm = kRef(ytdFlexy)
    if (!ytdFlexyElm) return;
    let muHidden = false

    for (const mutation of mutations) {
      if (mutation.attributeName === 'hidden') {
        //console.log(343)
        muHidden = true;
        break;
      }
    }

    if (!muHidden) return;
    

    
    mtc_store= Date.now()+2870
    //console.log(7004)
    deferredVarYTDHidden = ytdFlexyElm.hasAttribute('hidden');

    
    if(deferredVarYTDHidden && scheduledCommentRefresh && Q.comments_section_loaded>0){
      scheduledCommentRefresh=false;
    } 

    //console.log(15400)
    if(!deferredVarYTDHidden){
      mtf_forceCheckLiveVideo_disable = 0  
      timeline.setTimeout(function() {
        mtf_ChatExist();  
        layoutStatusMutex.lockWith(unlock=>{
          atChange(); // in case no mutation occurance
          ytdFlexyHiddenCheckBasic(ytdFlexyElm);
          dispatchWindowResize(); // player control positioning
          timeline.setTimeout(unlock, 40);          
        })        
      }, 40);
    }else{
      //console.log(15401)
      timeline.setTimeout(function() {
        //console.log(15402)
        ytdFlexyHiddenCheckBasic(ytdFlexyElm);
      }, 40);
    }

  }




  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') || null);
  }


  
  let recordScrollTop = 0

  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;

    /*
    let isPrevTabComments = document.querySelector('.tab-content-cld:not(.tab-content-hidden)');
    isPrevTabComments=isPrevTabComments&&isPrevTabComments.matches('#tab-comments')

    if(isPrevTabComments){
    
      let comments_tab = document.querySelector('#tab-comments');
      if(comments_tab){
        let st = comments_tab.scrollTop;
        if(st>=0) recordScrollTop = st;
      }


    }
    */

    function runAtEnd() {

      
    //console.log(12312)

      if (activeLink) lastShowTab = activeLink.getAttribute('userscript-tab-content')

      displayedPlaylist = null;
      scrollingVideosList = null;

      if (activeLink && lastShowTab == '#tab-list') {
        setDisplayedPlaylist();
      } else if (activeLink && lastShowTab == '#tab-videos') {
        scrollingVideosList = mWeakRef(document.querySelector('ytd-watch-flexy #tab-videos:not(.tab-content-hidden) [placeholder-videos]'));
      }


      ytdFlexyElm.setAttribute('tabview-selection', activeLink ? lastShowTab : '')

      if (lastShowTab == '#tab-comments' && (ytdFlexyElm.getAttribute('tabview-youtube-comments') || '').lastIndexOf('S') >= 0) {

        
        if(mtf_forceCheckLiveVideo_disable===2) {}
        else{
          akAttr(ytdFlexyElm, 'tabview-youtube-comments', false, 'L');

          /*
          requestAnimationFrame(() => {
            let comments_tab = document.querySelector('#tab-comments');
            if (comments_tab && comments_tab.scrollTop > 0) comments_tab.scrollTop = 0;
          });
          */
        }

      }

      /*
    let isNewTabComments = document.querySelector('.tab-content-cld:not(.tab-content-hidden)')
    
    isNewTabComments=isNewTabComments&&isNewTabComments.matches('#tab-comments')

    if(isNewTabComments){
      
      let comments_tab = document.querySelector('#tab-comments');
      if(comments_tab){
        let st = comments_tab.scrollTop;
        if(Math.abs(st-recordScrollTop)>0.9) comments_tab.scrollTop=recordScrollTop;
      }

      
      
    }
    */


    }

    const links = document.querySelectorAll('#material-tabs a[userscript-tab-content]');



    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 {
          link.classList.add("active");
          content.classList.remove("tab-content-hidden");
          //timeline.setTimeout(()=>content.focus(),400);

        }
      }
    }

    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) {

        //console.log(8510)
        let ytdFlexyElm = kRef(ytdFlexy);
        if (!scriptEnable || !ytdFlexyElm) return null;

        if (!this.hasAttribute('userscript-tab-content')) return;


        evt.preventDefault();

        //console.log(8511)

/*
        if (this.getAttribute('userscript-tab-content') == '#tab-comments' && parseInt(ytdFlexyElm.getAttribute('tabview-youtube-comments') || '') < 0) {
          
        console.log(8512)
          return;
        }*/

        //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 (isWideScreenWithTwoColumns() && !isTheater() && isActiveAndVisible) {
              //optional
              timeline.setTimeout(unlock, 80);
              switchTabActivity(null);
              ytBtnSetTheater();
            } else if (isActiveAndVisible) {
              timeline.setTimeout(unlock, 80);
              switchTabActivity(null);
            } else {
              let pInterval = 60;
              if (isChatExpand() && isWideScreenWithTwoColumns()) {
                ytBtnCollapseChat();
              } else if (isEngagementPanelExpanded() && isWideScreenWithTwoColumns()) {
                ytBtnCloseEngagementPanels();
              } else if (isWideScreenWithTwoColumns() && isTheater() && !isFullScreen()) {
                ytBtnCancelTheater();
              } else {
                pInterval = 20;
              }
              //console.log(8518)
              timeline.setTimeout(() => {
                timeline.setTimeout(makeBodyScroll, 20); // this is to make the image render

                timeline.setTimeout(() => {
                  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)

              }, pInterval);
            }


          })

        })




      });

    }

  }


  // ---------------------------------------------------------------------------------------------
  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')
  })

  function forceConfig(){
    
    let trial = false;
    let f=()=>{
      if(trial) return;
      let b = true;
      try{
        document.dispatchEvent(new CustomEvent("me-func"))
        window.yt.config_.EXPERIMENT_FLAGS.kevlar_watch_metadata_refresh=true
      }catch(e){
        b=false;
      }
      if(b) trial = true;

    }
    f();
    requestAnimationFrame(f)
    setTimeout(f,30)
    setTimeout(f,300)
    
  }

  document.addEventListener("yt-navigate-start",()=>{
    console.log('yt-navigate-start')
    forceConfig();
  })

  document.addEventListener("yt-page-manager-navigate-start",()=>{
    console.log('yt-page-manager-navigate-start')
    forceConfig();
  })


  document.addEventListener("loadstart", (evt) => {
    if (!evt || !evt.target || evt.target.nodeName !== "VIDEO") return;
    let elm = evt.target;
    if (!elm.matches('#player video, #movie_player video, video[tabview-mainvideo]')) return;

    
    mtc_store= Date.now()+2870

    let src = elm.src;
    if (src !== lastVideoURL) {
      lastVideoURL = elm.src;
      elm.setAttribute('tabview-mainvideo', ''); // mainly for mini playing
    }

  }, true)

  // ---------------------------------------------------------------------------------------------

  let scrolling_lastD = 0;

  const singleColumnScrolling = function(/** @type {boolean} */  scrolling_lastF) {
    
    if (!scriptEnable || deferredVarYTDHidden) return;

    let pageY = scrollY;
    if (pageY < 10 && scrolling_lastD === 0 && !scrolling_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;
      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 (deferredVarYTDHidden) return;
    lastResizeAt = Date.now();

    requestAnimationFrame(() => {
      singleColumnScrolling(true)
    })

  }, {
    capture: false,
    passive: true
  })



  
  let hist = null
  const getHistState = (history)=>`${history.length}|${history.state?history.state.entryTime:null}`;
  function newVideoPageCheck(){
    let hState = getHistState(history)
    if(hist!==hState) {
      hist=hState;
      if(scriptEnable){
        //scriptEnable = false;
        let callTimeout = !newVideoPage.entryTime
        newVideoPage.entryTime = 1
        if(callTimeout) setTimeout(newVideoPage,0)
        
      }
      return true;
    }
  }
  function newVideoPage(){
    newVideoPage.entryTime = 0;

    console.log('newVideoPage')
    
    //console.log('newVideoPage-', 150, location.href)

    let ytdFlexyElm = kRef(ytdFlexy);
    if(!ytdFlexyElm) return;

    timeline.reset();
    layoutStatusMutex = new Mutex();

    //console.log('newVideoPage-', 350, location.href)
    
    let flag1= /^https?\:\/\/(\w+\.)*youtube\.com\/watch\?(\w+\=[^\/\?\&]+\&|\w+\=?\&)*v=[\w\-\_]+/.test(location.href)
    let tf = ()=>{

      let isFlexyHidden = (deferredVarYTDHidden = ytdFlexyElm.hasAttribute('hidden'));

      let flag2 = !isFlexyHidden;

      if(flag1^flag2) return timeline.setTimeout(tf,7); else funcVisibleWatch();

      tf= null;
      funcVisibleWatch = null;
      ytdFlexyElm=null

    }

    let funcVisibleWatch = ()=>{
      
      //scriptEnable = true;

      let isFlexyHidden = (deferredVarYTDHidden = ytdFlexyElm.hasAttribute('hidden'));
      ytdFlexyHiddenCheckBasic(ytdFlexyElm)

      if(!isFlexyHidden) {

        mtf_forceCheckLiveVideo_disable = 0;
        mtc_store= Date.now()+2870

        sect_lastUpdate = 0;
        if(pendingOne){
          // reset pendingOne = false if the element is already removed.
          pendingOne = document.querySelectorAll(`ytd-watch-flexy ytd-comments#comments [tabview-cache-time]`).length>1;
        }

        timeline.setTimeout(()=>{
          Q.mutationTarget=null;
          FP.mtf_attrComments()
        },2870)
    
        if(pageInit_attrComments) hookSection();





        
        let new_layoutStatus = wls.layoutStatus 

        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 && ytdFlexyElm.querySelectorAll('ytd-engagement-panel-section-list-renderer[visibility="ENGAGEMENT_PANEL_VISIBILITY_EXPANDED"]').length===0){
          wls.layoutStatus = new_layoutStatus & (~LAYOUT_ENGAGEMENT_PANEL_EXPAND)
        }

        //mtf_attrFlexy();

        FP.mtf_attrEngagementPanel();
      
        FP.mtf_attrPlaylist();
      
        FP.mtf_attrComments();   
      
        FP.mtf_attrChatroom();


      }

      regularCheck(1,0,null); // mutation happens when the page is not ready; call again as the page is ready.



      

    }
    

    
    tf();
    


  }



  function browserHistoryState(){
    let maxCount = 5

    if (!scriptEnable) return;
    
    let ytdFlexyElm = kRef(ytdFlexy);
    if(!ytdFlexyElm) return;

    let cid = timeline.setInterval(function(){
      if(newVideoPageCheck()){
        timeline.clearInterval(cid);
      }
      if(--maxCount===0) timeline.clearInterval(cid);
    },7)

  }


  window.addEventListener('hashchange', function() {
    if (!scriptEnable) return;
    console.log('hashchange');
    browserHistoryState();
  }, { capture: true })

  window.addEventListener('popstate', function() {
    if (!scriptEnable) return;
    console.log('popstate');
    browserHistoryState();
  }, { capture: true })


  // function clearMutationObserver(o, key) {
  //   if (o[key]) {
  //     o[key].takeRecords();
  //     o[key].disconnect();
  //     o[key] = null;
  //     return true;
  //   }
  // }

  // function initMutationObserver(o, key, callback) {
  //   clearMutationObserver(o, key);
  //   const mto = new MutationObserver(callback);
  //   o[key] = mto;
  //   return mto;
  // }
  // function initMutationObserver2(o, key, mtoFunc) {
  //   clearMutationObserver(o, key);
  //   const mto = mtoFunc();
  //   o[key] = mto;
  //   return mto;
  // }

  document.addEventListener('wheel', function(evt) {

    if (!scriptEnable) return;
    const displayedPlaylist_element = kRef(displayedPlaylist);
    if (displayedPlaylist_element && displayedPlaylist_element.contains(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;


    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 && !isWideScreenWithTwoColumns()) {

      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 sect_lastUpdate = 0;

  let sect_holder = null;
  let sect_hText = null;
  let sect_hTime = 0;

  let pendingOne = false;
  let mtf_forceCheckLiveVideo_disable = 0;

  let storeLastPanel = null;

  /** @type {boolean | null} */
  let deferredVarYTDHidden = null;
  let scheduledCommentRefresh = false;

  let pageInit_attrComments = false;



  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 ytdFlexyElm.contains(elm)
    }
  };

  const timeline = {
    // after initialized (initObserver)
    cn1:{},
    cn2:{},
    setTimeout( /** @type {TimerHandler} */ f,/** @type {number} */ d){
      return timeline.cn1[setTimeout(f,d)]=true
    },
    clearTimeout(/** @type {number} */ cid){
      timeline.cn1[cid]=false; return clearTimeout(cid)
    },
    setInterval(/** @type {TimerHandler} */ f,/** @type {number} */ d){
      return timeline.cn2[setInterval(f,d)]=true
    },
    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; 
      this._target = mWeakRef(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)
        for(const key of Object.keys(this.flist)){
          this.checker(target,key)
        }
        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)



  }


  /*

  To be implement: timeline

  timeline.setTimeout
  timeline.clearTimeout
  timeline.setInterval
  timeline.clearInterval

  */



  //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.offsetHeight<this.$.content.scrollHeight:this.alwaysToggleable||this.$.content.scrollHeight>this.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...
})();