YouTube RM3 - Reduce Memory Usage by Reusing Components

A simple tool that runs in the background to reuse YouTube components, thereby reducing memory usage over time.

As of 2024-12-23. See the latest version.

  1. // ==UserScript==
  2. // @name YouTube RM3 - Reduce Memory Usage by Reusing Components
  3. // @namespace Violentmonkey Scripts
  4. // @version 0.1.0017
  5. // @license MIT
  6. // @match https://www.youtube.com/*
  7. // @match https://studio.youtube.com/live_chat*
  8. //
  9. //
  10. // @author CY Fung
  11. // @run-at document-start
  12. // @grant none
  13. // @unwrap
  14. // @allFrames true
  15. // @inject-into page
  16. //
  17. // @compatible firefox Violentmonkey
  18. // @compatible firefox Tampermonkey
  19. // @compatible firefox FireMonkey
  20. // @compatible chrome Violentmonkey
  21. // @compatible chrome Tampermonkey
  22. // @compatible opera Violentmonkey
  23. // @compatible opera Tampermonkey
  24. // @compatible safari Stay
  25. // @compatible edge Violentmonkey
  26. // @compatible edge Tampermonkey
  27. // @compatible brave Violentmonkey
  28. // @compatible brave Tampermonkey
  29. //
  30. // @description A simple tool that runs in the background to reuse YouTube components, thereby reducing memory usage over time.
  31. // @description:en A simple tool that runs in the background to reuse YouTube components, thereby reducing memory usage over time.
  32. // @description:ja YouTubeコンポーネントを再利用することで、長期的なメモリ使用量の削減を目指す、バックグラウンドで実行されるシンプルなツールです。
  33. // @description:zh-TW 一個在背景執行的簡易工具,可重複利用 YouTube 元件,從而在長期減少記憶體使用量。
  34. // @description:zh-CN 一个在后台运行的简单工具,通过重复利用 YouTube 组件,从而在长期减少内存使用量。
  35. //
  36. // ==/UserScript==
  37.  
  38. const rm3 = window.rm3 = {};
  39. // console.log(3001);
  40.  
  41. (() => {
  42.  
  43. const DEBUG_OPT = false;
  44. const CONFIRM_TIME = 4000;
  45. const CHECK_INTERVAL = 400;
  46. const DEBUG_dataChangeReflection = true;
  47.  
  48.  
  49. /** @type {globalThis.PromiseConstructor} */
  50. const Promise = (async () => { })().constructor; // YouTube hacks Promise in WaterFox Classic and "Promise.resolve(0)" nevers resolve.
  51.  
  52. // https://qiita.com/piroor/items/02885998c9f76f45bfa0
  53. // https://gist.github.com/piroor/829ecb32a52c2a42e5393bbeebe5e63f
  54. function uniq(array) {
  55. return [...new Set(array)];
  56. };
  57.  
  58.  
  59. rm3.uniq = uniq; // [[debug]]
  60. DEBUG_OPT && (rm3.location= location.href);
  61.  
  62.  
  63. rm3.inspect = () => {
  64. return uniq([...document.getElementsByTagName('*')].filter(e => e?.polymerController?.createComponent_).map(e => e.nodeName)); // [[debug]]
  65. }
  66.  
  67.  
  68. const insp = o => o ? (o.polymerController || o.inst || o || 0) : (o || 0);
  69. const indr = o => insp(o).$ || o.$ || 0;
  70.  
  71. const getProto = (element) => {
  72. if (element) {
  73. const cnt = insp(element);
  74. return cnt.constructor.prototype || null;
  75. }
  76. return null;
  77. }
  78.  
  79.  
  80. const LinkedArray = (() => {
  81.  
  82.  
  83. class Node {
  84. constructor(value) {
  85. this.value = value;
  86. this.next = null;
  87. this.prev = null;
  88. }
  89. }
  90.  
  91. class LinkedArray {
  92. constructor() {
  93. this.head = null;
  94. this.tail = null;
  95. this.length = 0;
  96. }
  97.  
  98. push(value) {
  99. const newNode = new Node(value);
  100. if (this.length === 0) {
  101. this.head = newNode;
  102. this.tail = newNode;
  103. } else {
  104. this.tail.next = newNode;
  105. newNode.prev = this.tail;
  106. this.tail = newNode;
  107. }
  108. this.length++;
  109. return this.length;
  110. }
  111.  
  112. pop() {
  113. if (this.length === 0) return undefined;
  114. const removedNode = this.tail;
  115. if (this.length === 1) {
  116. this.head = null;
  117. this.tail = null;
  118. } else {
  119. this.tail = removedNode.prev;
  120. this.tail.next = null;
  121. removedNode.prev = null;
  122. }
  123. this.length--;
  124. return removedNode.value;
  125. }
  126.  
  127. unshift(value) {
  128. const newNode = new Node(value);
  129. if (this.length === 0) {
  130. this.head = newNode;
  131. this.tail = newNode;
  132. } else {
  133. newNode.next = this.head;
  134. this.head.prev = newNode;
  135. this.head = newNode;
  136. }
  137. this.length++;
  138. return this.length;
  139. }
  140.  
  141. shift() {
  142. if (this.length === 0) return undefined;
  143. const removedNode = this.head;
  144. if (this.length === 1) {
  145. this.head = null;
  146. this.tail = null;
  147. } else {
  148. this.head = removedNode.next;
  149. this.head.prev = null;
  150. removedNode.next = null;
  151. }
  152. this.length--;
  153. return removedNode.value;
  154. }
  155.  
  156. size() {
  157. return this.length;
  158. }
  159.  
  160. // Get a node by index (0-based)
  161. getNode(index) {
  162. if (index < 0 || index >= this.length) return null;
  163.  
  164. let current;
  165. let counter;
  166.  
  167. // Optimization: start from closest end
  168. if (index < this.length / 2) {
  169. current = this.head;
  170. counter = 0;
  171. while (counter !== index) {
  172. current = current.next;
  173. counter++;
  174. }
  175. } else {
  176. current = this.tail;
  177. counter = this.length - 1;
  178. while (counter !== index) {
  179. current = current.prev;
  180. counter--;
  181. }
  182. }
  183. return current;
  184. }
  185.  
  186. // Get value by index
  187. get(index) {
  188. const node = this.getNode(index);
  189. return node ? node.value : undefined;
  190. }
  191.  
  192. // Find the first node with the given value and return both node and index
  193. findNode(value) {
  194. let current = this.head;
  195. let idx = 0;
  196. while (current) {
  197. if (current.value === value) {
  198. return { node: current, index: idx };
  199. }
  200. current = current.next;
  201. idx++;
  202. }
  203. return { node: null, index: -1 };
  204. }
  205.  
  206. toArray() {
  207. const arr = [];
  208. let current = this.head;
  209. while (current) {
  210. arr.push(current.value);
  211. current = current.next;
  212. }
  213. return arr;
  214. }
  215.  
  216. // Insert a new value before a given node (provided you already have the node reference)
  217. insertBeforeNode(node, newValue) {
  218. if (!node) {
  219. this.unshift(newValue);
  220. return true;
  221. }
  222.  
  223. if (node === this.head) {
  224. // If the target is the head, just unshift
  225. this.unshift(newValue);
  226. } else {
  227. const newNode = new Node(newValue);
  228. const prevNode = node.prev;
  229.  
  230. prevNode.next = newNode;
  231. newNode.prev = prevNode;
  232. newNode.next = node;
  233. node.prev = newNode;
  234.  
  235. this.length++;
  236. }
  237. return true;
  238. }
  239.  
  240. // Insert a new value after a given node (provided you already have the node reference)
  241. insertAfterNode(node, newValue) {
  242.  
  243. if (!node) {
  244. this.push(newValue);
  245. return true;
  246. }
  247.  
  248. if (node === this.tail) {
  249. // If the target is the tail, just push
  250. this.push(newValue);
  251. } else {
  252. const newNode = new Node(newValue);
  253. const nextNode = node.next;
  254.  
  255. node.next = newNode;
  256. newNode.prev = node;
  257. newNode.next = nextNode;
  258. nextNode.prev = newNode;
  259.  
  260. this.length++;
  261. }
  262. return true;
  263. }
  264.  
  265. // Insert a new value before the first occurrence of an existing value (search by value)
  266. insertBefore(existingValue, newValue) {
  267. const { node } = this.findNode(existingValue);
  268. if (!node) return false; // Not found
  269. return this.insertBeforeNode(node, newValue);
  270. }
  271.  
  272. // Insert a new value after the first occurrence of an existing value (search by value)
  273. insertAfter(existingValue, newValue) {
  274. const { node } = this.findNode(existingValue);
  275. if (!node) return false; // Not found
  276. return this.insertAfterNode(node, newValue);
  277. }
  278.  
  279.  
  280.  
  281. // Delete a given node from the list
  282. deleteNode(node) {
  283. if (!node) return false;
  284.  
  285. if (this.length === 1 && node === this.head && node === this.tail) {
  286. // Only one element in the list
  287. this.head = null;
  288. this.tail = null;
  289. } else if (node === this.head) {
  290. // Node is the head
  291. this.head = node.next;
  292. this.head.prev = null;
  293. node.next = null;
  294. } else if (node === this.tail) {
  295. // Node is the tail
  296. this.tail = node.prev;
  297. this.tail.next = null;
  298. node.prev = null;
  299. } else {
  300. // Node is in the middle
  301. const prevNode = node.prev;
  302. const nextNode = node.next;
  303. prevNode.next = nextNode;
  304. nextNode.prev = prevNode;
  305. node.prev = null;
  306. node.next = null;
  307. }
  308.  
  309. this.length--;
  310. return true;
  311. }
  312.  
  313. }
  314.  
  315.  
  316. LinkedArray.Node = Node;
  317. return LinkedArray;
  318. })();
  319.  
  320.  
  321.  
  322. class LimitedSizeSet extends Set {
  323. constructor(n) {
  324. super();
  325. this.limit = n;
  326. }
  327.  
  328. add(key) {
  329. if (!super.has(key)) {
  330. super.add(key);
  331. let n = super.size - this.limit;
  332. if (n > 0) {
  333. const iterator = super.values();
  334. do {
  335. const firstKey = iterator.next().value; // Get the first (oldest) key
  336. super.delete(firstKey); // Delete the oldest key
  337. } while (--n > 0)
  338. }
  339. }
  340. }
  341.  
  342. removeAdd(key) {
  343. super.delete(key);
  344. this.add(key);
  345. }
  346.  
  347. }
  348.  
  349.  
  350.  
  351. if (!document.createElement9512 && typeof document.createElement === 'function' && document.createElement.length === 1) {
  352.  
  353. // sizing of Map / Set. Shall limit ?
  354.  
  355. const hookTos = new Set(); // [[debug]]
  356. DEBUG_OPT && (rm3.hookTos = hookTos);
  357.  
  358. // const reusePool = new Map(); // xx858
  359. const entryRecords = new WeakMap(); // a weak link between element and record
  360.  
  361. // rm3.list = [];
  362.  
  363. const operations = rm3.operations = new Set(); // to find out the "oldest elements"
  364.  
  365. const availablePools = rm3.availablePools = new Map(); // those "old elements" can be used
  366. let lastTimeCheck = 0;
  367.  
  368. const reuseRecord_ = new LimitedSizeSet(256); // [[debug]]
  369. const reuseCount_ = new Map();
  370.  
  371. let noTimeCheck = false;
  372.  
  373. // const defaultValues = new Map();
  374. // const noValues = new Map();
  375.  
  376. const timeCheck = () => {
  377. // regularly check elements are old enough to put into the available pools
  378. // note: the characterists of YouTube components are non-volatile. So don't need to waste time to check weakRef.deref() is null or not for removing in operations.
  379.  
  380. const ct = Date.now();
  381. if (ct - lastTimeCheck < CHECK_INTERVAL || noTimeCheck) return;
  382. lastTimeCheck = ct;
  383. noTimeCheck = true;
  384.  
  385. // 16,777,216
  386. if (hookTos.size > 777216) hookTos.clear(); // just debug usage, dont concern
  387. if (operations.size > 7777216) {
  388. // extremely old elements in operations mean they have no attach/detach action. so no reuse as well. they are just trash in memory.
  389. // as no checking of the weakRef.deref() being null or not, those trash could be already cleaned. However we don't concern this.
  390. // (not to count whether they are actual memory trash or not)
  391. const half = operations.size >>> 1;
  392. let i = 0;
  393. for (const value of operations) {
  394. if (i++ > half) break;
  395. operations.delete(value);
  396. }
  397. }
  398.  
  399. // {
  400. // // smallest to largest
  401. // // past to recent
  402.  
  403. // const iterator = operations[Symbol.iterator]();
  404. // console.log(1831, '------------------------')
  405. // while (true) {
  406. // const iteratorResult = iterator.next(); // 順番に値を取りだす
  407. // if (iteratorResult.done) break; // 取り出し終えたなら、break
  408. // console.log(1835, iteratorResult.value[3])
  409. // }
  410.  
  411. // console.log(1839, '------------------------')
  412. // }
  413.  
  414. // Set iterator
  415. // s.add(2) s.add(6) s.add(1) s.add(3)
  416. // next: 2 -> 6 -> 1 -> 3
  417. // op1 (oldest) -> op2 -> op3 -> op4 (latest)
  418. const iterator = operations[Symbol.iterator]();
  419.  
  420. const targetTime = ct - CONFIRM_TIME;
  421.  
  422. const pivotNodes = new WeakMap();
  423.  
  424. while (true) {
  425. const iteratorResult = iterator.next(); // 順番に値を取りだす
  426. if (iteratorResult.done) break; // 取り出し終えたなら、break
  427. const entryRecord = iteratorResult.value;
  428. if (entryRecord[3] > targetTime) break;
  429.  
  430. if (!entryRecord[4] && entryRecord[1] < 0 && entryRecord[2] > 0) {
  431. const element = entryRecord[0].deref();
  432. const eKey = (element || 0).__rm3Tag003__;
  433. if (!eKey) {
  434. operations.delete(entryRecord);
  435. } else if (element.isConnected === false && insp(element).isAttached === false) {
  436. entryRecord[4] = true;
  437.  
  438. let availablePool = availablePools.get(eKey);
  439. if (!availablePool) availablePools.set(eKey, availablePool = new LinkedArray());
  440. if (!(availablePool instanceof LinkedArray)) throw new Error();
  441. DEBUG_OPT && console.log(3885,'add key', eKey, availablePools.size)
  442. // rm3.showSize = ()=>availablePools.size
  443. // setTimeout(()=>{
  444. // // window?.euu1 = availablePools
  445. // // window?.euu2 = availablePools.size
  446. // console.log(availablePools.size)
  447. // }, 8000)
  448. let pivotNode = pivotNodes.get(availablePool);
  449. if (!pivotNode) pivotNodes.set(availablePool, pivotNode = availablePool.head) // cached the previous newest node (head) as pivotNode
  450.  
  451. availablePool.insertBeforeNode(pivotNode, entryRecord); // head = newest, tail = oldest
  452.  
  453. }
  454. }
  455.  
  456. }
  457. noTimeCheck = false;
  458.  
  459. }
  460.  
  461. const attachedDefine = function () {
  462.  
  463. Promise.resolve().then(timeCheck);
  464. try {
  465. const hostElement = this?.hostElement;
  466. if (hostElement instanceof HTMLElement) {
  467. const entryRecord = entryRecords.get(hostElement);
  468. if (entryRecord && entryRecord[0].deref() === hostElement && hostElement.isConnected === true && this?.isAttached === true) {
  469. noTimeCheck = true;
  470. const ct = Date.now();
  471. entryRecord[1] = ct;
  472. entryRecord[2] = -1;
  473. entryRecord[3] = ct;
  474. operations.delete(entryRecord);
  475. operations.add(entryRecord);
  476. noTimeCheck = false;
  477. // note: because of performance prespective, deletion for availablePools[eKey]'s linked element would not be done here.
  478. // entryRecord[4] is not required to be updated here.
  479. }
  480. }
  481. } catch (e) { }
  482. return this.attached9512();
  483. }
  484. const detachedDefine = function () {
  485.  
  486. Promise.resolve().then(timeCheck);
  487. try {
  488. const hostElement = this?.hostElement;
  489. if (hostElement instanceof HTMLElement) {
  490. const entryRecord = entryRecords.get(hostElement);
  491. if (entryRecord && entryRecord[0].deref() === hostElement && hostElement.isConnected === false && this?.isAttached === false) {
  492. noTimeCheck= true;
  493. const ct = Date.now();
  494. entryRecord[2] = ct;
  495. entryRecord[1] = -1;
  496. entryRecord[3] = ct;
  497. operations.delete(entryRecord);
  498. operations.add(entryRecord);
  499. noTimeCheck= false;
  500. // note: because of performance prespective, deletion for availablePools[eKey]'s linked element would not be done here.
  501. // entryRecord[4] is not required to be updated here.
  502. }
  503. }
  504. } catch (e) { }
  505.  
  506. return this.detached9512();
  507. }
  508.  
  509.  
  510. // function cpy(x) {
  511. // if (!x) return x;
  512. // try {
  513. // if (typeof x === 'object' && typeof x.length ==='number' && typeof x.slice === 'function') {
  514. // x = x.slice(0)
  515. // } else if (typeof x === 'object' && !x.length) {
  516. // x = JSON.parse(JSON.stringify(x));
  517. // } else {
  518. // return Object.assign({}, x);
  519. // }
  520. // } catch (e) { }
  521. // return x;
  522. // }
  523.  
  524. async function digestMessage(message) {
  525. const msgUint8 = new TextEncoder().encode(message); // (utf-8 の) Uint8Array にエンコードする
  526. const hashBuffer = await crypto.subtle.digest("SHA-256", msgUint8); // メッセージをハッシュする
  527. const hashArray = Array.from(new Uint8Array(hashBuffer)); // バッファーをバイト列に変換する
  528. const hashHex = hashArray
  529. .map((b) => b.toString(16).padStart(2, "0"))
  530. .join(""); // バイト列を 16 進文字列に変換する
  531. return hashHex.toUpperCase();
  532. }
  533.  
  534.  
  535.  
  536. const createComponentDefine_ = function (a, b, c) {
  537.  
  538. Promise.resolve().then(timeCheck);
  539.  
  540.  
  541. const creatorTag = this?.is || this?.nodeName?.toLowerCase() || '';
  542.  
  543. const componentTag = typeof a === 'string' ? a : ((a || 0).component || '');
  544.  
  545.  
  546.  
  547. const eKey = creatorTag && componentTag ? `${creatorTag}.${componentTag}` : '*'; // '*' for play-safe
  548. const availablePool = availablePools.get(eKey);
  549.  
  550. try {
  551.  
  552. if (availablePool instanceof LinkedArray) {
  553. noTimeCheck = true;
  554.  
  555. let node = availablePool.tail; // oldest
  556.  
  557. while (node instanceof LinkedArray.Node) {
  558. const entryRecord = node.value;
  559. const prevNode = node.prev;
  560.  
  561. let ok = false;
  562. let elm = null;
  563. if (entryRecord[1] < 0 && entryRecord[2] > 0 && entryRecord[4]) {
  564. elm = entryRecord[0].deref();
  565. // elm && console.log(3882, (elm.__shady_native_textContent || elm.textContent))
  566. if (elm && elm instanceof HTMLElement && elm.isConnected === false && insp(elm).isAttached === false && elm.parentNode === null) {
  567. ok = true;
  568. }
  569. }
  570.  
  571. if (ok) {
  572.  
  573. // useEntryRecord = entryRecord;
  574. entryRecord[4] = false;
  575. // console.log('nodeDeleted', 1, entryRecord[0].deref().nodeName)
  576. availablePool.deleteNode(node);
  577. // break;
  578.  
  579.  
  580. const cnt = insp(elm);
  581.  
  582.  
  583. cnt.__dataInvalid = false;
  584. // cnt._initializeProtoProperties(cnt.data)
  585.  
  586. // window.meaa = cnt.$.container;
  587. const cntData = cnt.data;
  588. cnt.__data = Object.create(cntData);
  589. cnt.__dataPending = Object.create(cntData);
  590. cnt.__dataOld = {}
  591.  
  592. try {
  593. cnt.markDirty();
  594. } catch (e) { }
  595. try {
  596. cnt.markDirtyVisibilityObserver();
  597. } catch (e) { }
  598.  
  599. try{
  600. cnt.wasPrescan = cnt.wasVisible = !1
  601. }catch(e){}
  602.  
  603. if (DEBUG_OPT && DEBUG_dataChangeReflection) {
  604.  
  605. let jC1 = null;
  606. let jC2 = null;
  607. const jKey = `${Math.floor(Math.random() * 314159265359 + 314159265359).toString(36)}`;
  608. try {
  609. jC1 = (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent);
  610. // console.log(83802, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
  611. } catch (e) {
  612. console.warn(e);
  613. }
  614.  
  615. setTimeout(() => {
  616. try {
  617. jC2 = (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent);
  618. // console.log(83804, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
  619. } catch (e) {
  620. console.warn(e);
  621. }
  622.  
  623. (async () => {
  624.  
  625.  
  626.  
  627. jC1 = await digestMessage(jC1);
  628. jC2 = await digestMessage(jC2);
  629.  
  630. console.log(83804, jKey, jC1.substring(0, 7), jC2.substring(0, 7))
  631.  
  632. })()
  633.  
  634.  
  635. }, 1000);
  636. }
  637.  
  638. // // try{
  639.  
  640. // // console.log(83801, JSON.stringify(cntData))
  641. // // }catch(e){
  642. // // console.warn(e);
  643. // // }
  644.  
  645. // const jKey = `${Math.floor(Math.random() * 314159265359 + 314159265359).toString(36)}`;
  646. // try{
  647.  
  648. // console.log(83802, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
  649. // }catch(e){
  650. // console.warn(e);
  651. // }
  652.  
  653. // setTimeout(()=>{
  654. // // try{
  655.  
  656. // // console.log(83803, JSON.stringify(cntData))
  657. // // }catch(e){
  658. // // console.warn(e);
  659. // // }
  660. // try{
  661.  
  662. // console.log(83804, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
  663. // }catch(e){
  664. // console.warn(e);
  665. // }
  666. // }, 1000);
  667.  
  668.  
  669. // reference
  670. // https://www.youtube.com/s/desktop/c01ea7e3/jsbin/live_chat_polymer.vflset/live_chat_polymer.js
  671. // a.prototype._initializeProtoProperties = function(c) {
  672. // this.__data = Object.create(c);
  673. // this.__dataPending = Object.create(c);
  674. // this.__dataOld = {}
  675. // }
  676.  
  677. // ------- (NO USE) ------
  678. //
  679. // a.prototype._initializeProperties = function() {
  680. // this.__dataProto && (this._initializeProtoProperties(this.__dataProto),
  681. // this.__dataProto = null);
  682. // b.prototype._initializeProperties.call(this)
  683. // }
  684. // ;
  685. // a.prototype._initializeProtoProperties = function(c) {
  686. // for (var d in c)
  687. // this._setProperty(d, c[d])
  688. // }
  689. //
  690. // ------- (NO USE) ------
  691.  
  692.  
  693. // // cnt.__dataReady = false;
  694. // cnt.__dataInvalid = true;
  695. // cnt.__dataEnabled = false; // tbc
  696. // // if('__dataEnabled' in cnt) cnt.__dataEnabled = false;
  697. // // if ('__dataReady' in cnt && typeof cnt.__dataReady === 'boolean') cnt.__dataReady = false;
  698. // // if ('__dataInvalid' in cnt && typeof cnt.__dataInvalid === 'boolean') cnt.__dataInvalid = true;
  699.  
  700. // // try {
  701. // // if ('data' in cnt) cnt.data = null;
  702. // // if ('__data' in cnt) cnt.__data = null;
  703. // // } catch (e) {
  704. // // console.warn(e)
  705. // // }
  706.  
  707. // // try {
  708. // // if ('data' in cnt) cnt.data = {};
  709. // // if ('__data' in cnt) cnt.__data = {};
  710. // // } catch (e) {
  711. // // console.warn(e)
  712. // // }
  713.  
  714.  
  715.  
  716.  
  717.  
  718.  
  719.  
  720.  
  721.  
  722.  
  723.  
  724. // // const noValue = noValues.get(eKey);
  725. // // if(noValue){
  726. // // if(!noValue.data) cnt.data = noValue.data;
  727. // // if(!noValue.__data) cnt.data = noValue.__data;
  728. // // }
  729.  
  730. // // const defaultValue = defaultValues.get(eKey);
  731. // // if (defaultValue) {
  732.  
  733. // // try {
  734. // // if ('data' in defaultValue) cnt.data = cpy(cnt.data);
  735. // // if ('__data' in defaultValue) cnt.__data = cpy(cnt.__data);
  736. // // } catch (e) {
  737. // // console.warn(e)
  738. // // }
  739. // // }
  740.  
  741. // // const flg001 = elm.__rm3Flg001__;
  742. // // if (cnt.__dataEnabled !== flg001) cnt.__dataEnabled = flg001;
  743.  
  744.  
  745.  
  746.  
  747.  
  748.  
  749.  
  750. // // const flg001 = elm.__rm3Flg001__;
  751. // // if (cnt.__dataEnabled !== flg001) cnt.__dataEnabled = flg001;
  752.  
  753.  
  754. // if (cnt.__dataPending && typeof cnt.__dataPending === 'object') cnt.__dataPending = null;
  755. // if (cnt.__dataOld && typeof cnt.__dataOld === 'object') cnt.__dataOld = null;
  756.  
  757. // // cnt.__dataInstanceProps = null;
  758. // if (cnt.__dataCounter && typeof cnt.__dataCounter === 'number') cnt.__dataCounter = 0;
  759. // // cnt.__serializing = !1;
  760.  
  761.  
  762.  
  763. // if ('__dataClientsInitialized' in cnt || '__dataClientsReady' in cnt) {
  764.  
  765. // if ('__dataClientsInitialized' in cnt !== '__dataClientsReady' in cnt) {
  766.  
  767. // console.log('[rm3-warning] __dataClientsInitialized and __dataClientsReady should exist in the controller');
  768.  
  769. // }
  770.  
  771. // cnt.__dataClientsReady = !1;
  772. // cnt.__dataLinkedPaths = cnt.__dataToNotify = cnt.__dataPendingClients = null;
  773. // cnt.__dataHasPaths = !1;
  774. // cnt.__dataCompoundStorage = null; // cnt.__dataCompoundStorage = cnt.__dataCompoundStorage || null;
  775. // cnt.__dataHost = null; // cnt.__dataHost = cnt.__dataHost || null;
  776. // if (!cnt.__dataTemp) cnt.__dataTemp = {}; // cnt.__dataTemp = {};
  777. // cnt.__dataClientsInitialized = !1;
  778.  
  779. // }
  780.  
  781. if (entryRecord[5] < 1e9) entryRecord[5] += 1;
  782. DEBUG_OPT && Promise.resolve().then(() => console.log(`${eKey} reuse`, entryRecord)); // give some time for attach process
  783. DEBUG_OPT && reuseRecord_.add([Date.now(), cnt.is, entryRecord]);
  784. DEBUG_OPT && reuseCount_.set(cnt.is, (reuseCount_.get(cnt.is) || 0) + 1)
  785. if (rm3.reuseCount < 1e9) rm3.reuseCount++;
  786.  
  787. return elm;
  788.  
  789.  
  790. }
  791.  
  792. // console.log('condi88', entryRecord[1] < 0 , entryRecord[2] > 0 , !!entryRecord[4], !!entryRecord[0].deref())
  793.  
  794. entryRecord[4] = false;
  795.  
  796. // console.log(entryRecord);
  797. // console.log('nodeDeleted',2, entryRecord[0]?.deref()?.nodeName)
  798. availablePool.deleteNode(node);
  799. node = prevNode;
  800.  
  801. }
  802. // for(const ) availablePool
  803. // noTimeCheck = false;
  804. }
  805.  
  806. } catch (e) {
  807. console.warn(e)
  808. }
  809. noTimeCheck = false;
  810.  
  811.  
  812. // console.log('createComponentDefine_', a, b, c)
  813.  
  814. // if (!reusePool.has(componentTag)) reusePool.set(componentTag, new LinkedArray()); // xx858
  815.  
  816. // const pool = reusePool.get(componentTag); // xx858
  817. // if (!(pool instanceof LinkedArray)) throw new Error(); // xx858
  818.  
  819.  
  820. const newElement = this.createComponent9512_(a, b, c);
  821. // if(componentTag.indexOf( 'ticker')>=0)console.log(1883, a,newElement)
  822.  
  823. try {
  824.  
  825. const cntE = insp(newElement);
  826. if (!cntE.attached9512 && cntE.attached) {
  827.  
  828. const cProtoE = getProto(newElement);
  829.  
  830.  
  831. if (cProtoE.attached === cntE.attached) {
  832.  
  833. if (!cProtoE.attached9512 && typeof cProtoE.attached === 'function' && cProtoE.attached.length === 0) {
  834.  
  835. cProtoE.attached9512 = cProtoE.attached;
  836.  
  837. cProtoE.attached = attachedDefine;
  838. // hookTos.add(a);
  839. }
  840. } else {
  841.  
  842. if (typeof cntE.attached === 'function' && cntE.attached.length === 3) {
  843. cntE.attached9512 = cntE.attached;
  844.  
  845. cntE.attached = attachedDefine;
  846. // hookTos.add(a);
  847. }
  848. }
  849.  
  850.  
  851. }
  852.  
  853. if (!cntE.detached9512 && cntE.detached) {
  854.  
  855. const cProtoE = getProto(newElement);
  856.  
  857.  
  858. if (cProtoE.detached === cntE.detached) {
  859.  
  860. if (!cProtoE.detached9512 && typeof cProtoE.detached === 'function' && cProtoE.detached.length === 0) {
  861.  
  862. cProtoE.detached9512 = cProtoE.detached;
  863.  
  864. cProtoE.detached = detachedDefine;
  865. // hookTos.add(a);
  866. }
  867. } else {
  868.  
  869. if (typeof cntE.detached === 'function' && cntE.detached.length === 3) {
  870. cntE.detached9512 = cntE.detached;
  871.  
  872. cntE.detached = detachedDefine;
  873. // hookTos.add(a);
  874. }
  875. }
  876.  
  877.  
  878. }
  879.  
  880.  
  881. const acceptance = true;
  882. // const acceptance = !cntE.__dataReady && cntE.__dataInvalid !== false; // we might need to change the acceptance condition along with YouTube Coding updates.
  883. if (acceptance) {
  884.  
  885. // [[ weak ElementNode, attached time, detached time, time of change, inside availablePool, reuse count ]]
  886. const entryRecord = [new WeakRef(newElement), -1, -1, -1, false, 0];
  887.  
  888. newElement.__rm3Tag003__ = eKey;
  889. // pool.push(entryRecord);
  890. entryRecords.set(newElement, entryRecord);
  891. // newElement.__rm3Tag001__ = creatorTag;
  892. // newElement.__rm3Tag002__ = componentTag;
  893.  
  894. // newElement.__rm3Flg001__ = cntE.__dataEnabled;
  895. // // console.log(5928, cntE.data, cntE.__data)
  896. // if (!defaultValues.has(eKey)){
  897.  
  898. // const o = {};
  899.  
  900. // if('data' in cntE) o.data = cpy(cntE.data);
  901. // if('__data' in cntE) o.__data = cpy(cntE.__data);
  902. // defaultValues.set(eKey, o);
  903.  
  904. // }
  905.  
  906. // if(!noValues.has(eKey)){
  907. // const o = {};
  908. // o.data = true;
  909. // try {
  910.  
  911. // if (!cntE.data) o.data = cntE.data;
  912. // } catch (e) { }
  913.  
  914. // o.__data = true;
  915. // try {
  916.  
  917. // if (!cntE.__data) o.__data = cntE.__data;
  918. // } catch (e) { }
  919. // noValues.set(eKey, o)
  920. // }
  921.  
  922. } else {
  923. // console.log(5920, cntE.__dataReady, cntE.__dataInvalid)
  924. }
  925.  
  926.  
  927. } catch (e) {
  928. console.warn(e);
  929. }
  930. return newElement;
  931.  
  932.  
  933. }
  934.  
  935. document.createElement9512 = document.createElement;
  936. document.createElement = function (a) {
  937. const r = document.createElement9512(a);
  938. try {
  939. const cnt = insp(r);
  940. if (cnt.createComponent_ && !cnt.createComponent9512_) {
  941. const cProto = getProto(r);
  942. if (cProto.createComponent_ === cnt.createComponent_) {
  943.  
  944. if (!cProto.createComponent9512_ && typeof cProto.createComponent_ === 'function' && cProto.createComponent_.length === 3) {
  945.  
  946. cProto.createComponent9512_ = cProto.createComponent_;
  947.  
  948. cProto.createComponent_ = createComponentDefine_;
  949. DEBUG_OPT && hookTos.add(a);
  950. }
  951. } else {
  952.  
  953. if (typeof cnt.createComponent_ === 'function' && cnt.createComponent_.length === 3) {
  954. cnt.createComponent9512_ = cnt.createComponent_;
  955.  
  956. cnt.createComponent_ = createComponentDefine_;
  957. DEBUG_OPT && hookTos.add(a);
  958. }
  959. }
  960.  
  961. }
  962.  
  963. } catch (e) {
  964. console.warn(e)
  965. }
  966.  
  967.  
  968. return r;
  969. }
  970.  
  971. rm3.checkWhetherUnderParent = () => {
  972. const r = [];
  973. for (const operation of operations) {
  974. const elm = operation[0].deref();
  975. if (operation[2] > 0) {
  976.  
  977. r.push([!!elm, elm?.nodeName.toLowerCase(), elm?.parentNode === null]);
  978. }
  979. }
  980. return r;
  981. }
  982.  
  983. rm3.hookTags = () => {
  984.  
  985. const r = new Set();
  986. for (const operation of operations) {
  987. const elm = operation[0].deref();
  988. if (elm && elm.is) {
  989.  
  990. r.add(elm.is)
  991. }
  992. }
  993. return [...r];
  994. }
  995.  
  996.  
  997. DEBUG_OPT && (rm3.reuseRecord = () => {
  998. return [...reuseRecord_]; // [[debug]]
  999. });
  1000.  
  1001. DEBUG_OPT && (rm3.reuseCount_ = reuseCount_);
  1002.  
  1003. }
  1004.  
  1005. (rm3.reuseCount = 0); // window.rm3 will be zero initially if this script has no runtime complier error in the initialization phase.
  1006.  
  1007. })();