Greasy Fork is available in English.

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.

Verze ze dne 23. 12. 2024. Zobrazit nejnovější verzi.

  1. // ==UserScript==
  2. // @name YouTube RM3 - Reduce Memory Usage by Reusing Components
  3. // @namespace Violentmonkey Scripts
  4. // @version 0.1.0016
  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. if (DEBUG_OPT && DEBUG_dataChangeReflection) {
  593.  
  594. let jC1 = null;
  595. let jC2 = null;
  596. const jKey = `${Math.floor(Math.random() * 314159265359 + 314159265359).toString(36)}`;
  597. try {
  598. jC1 = (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent);
  599. // console.log(83802, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
  600. } catch (e) {
  601. console.warn(e);
  602. }
  603.  
  604. setTimeout(() => {
  605. try {
  606. jC2 = (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent);
  607. // console.log(83804, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
  608. } catch (e) {
  609. console.warn(e);
  610. }
  611.  
  612. (async () => {
  613.  
  614.  
  615.  
  616. jC1 = await digestMessage(jC1);
  617. jC2 = await digestMessage(jC2);
  618.  
  619. console.log(83804, jKey, jC1.substring(0, 7), jC2.substring(0, 7))
  620.  
  621. })()
  622.  
  623.  
  624. }, 1000);
  625. }
  626.  
  627. // // try{
  628.  
  629. // // console.log(83801, JSON.stringify(cntData))
  630. // // }catch(e){
  631. // // console.warn(e);
  632. // // }
  633.  
  634. // const jKey = `${Math.floor(Math.random() * 314159265359 + 314159265359).toString(36)}`;
  635. // try{
  636.  
  637. // console.log(83802, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
  638. // }catch(e){
  639. // console.warn(e);
  640. // }
  641.  
  642. // setTimeout(()=>{
  643. // // try{
  644.  
  645. // // console.log(83803, JSON.stringify(cntData))
  646. // // }catch(e){
  647. // // console.warn(e);
  648. // // }
  649. // try{
  650.  
  651. // console.log(83804, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
  652. // }catch(e){
  653. // console.warn(e);
  654. // }
  655. // }, 1000);
  656.  
  657.  
  658. // reference
  659. // https://www.youtube.com/s/desktop/c01ea7e3/jsbin/live_chat_polymer.vflset/live_chat_polymer.js
  660. // a.prototype._initializeProtoProperties = function(c) {
  661. // this.__data = Object.create(c);
  662. // this.__dataPending = Object.create(c);
  663. // this.__dataOld = {}
  664. // }
  665.  
  666. // ------- (NO USE) ------
  667. //
  668. // a.prototype._initializeProperties = function() {
  669. // this.__dataProto && (this._initializeProtoProperties(this.__dataProto),
  670. // this.__dataProto = null);
  671. // b.prototype._initializeProperties.call(this)
  672. // }
  673. // ;
  674. // a.prototype._initializeProtoProperties = function(c) {
  675. // for (var d in c)
  676. // this._setProperty(d, c[d])
  677. // }
  678. //
  679. // ------- (NO USE) ------
  680.  
  681.  
  682. // // cnt.__dataReady = false;
  683. // cnt.__dataInvalid = true;
  684. // cnt.__dataEnabled = false; // tbc
  685. // // if('__dataEnabled' in cnt) cnt.__dataEnabled = false;
  686. // // if ('__dataReady' in cnt && typeof cnt.__dataReady === 'boolean') cnt.__dataReady = false;
  687. // // if ('__dataInvalid' in cnt && typeof cnt.__dataInvalid === 'boolean') cnt.__dataInvalid = true;
  688.  
  689. // // try {
  690. // // if ('data' in cnt) cnt.data = null;
  691. // // if ('__data' in cnt) cnt.__data = null;
  692. // // } catch (e) {
  693. // // console.warn(e)
  694. // // }
  695.  
  696. // // try {
  697. // // if ('data' in cnt) cnt.data = {};
  698. // // if ('__data' in cnt) cnt.__data = {};
  699. // // } catch (e) {
  700. // // console.warn(e)
  701. // // }
  702.  
  703.  
  704.  
  705.  
  706.  
  707.  
  708.  
  709.  
  710.  
  711.  
  712.  
  713. // // const noValue = noValues.get(eKey);
  714. // // if(noValue){
  715. // // if(!noValue.data) cnt.data = noValue.data;
  716. // // if(!noValue.__data) cnt.data = noValue.__data;
  717. // // }
  718.  
  719. // // const defaultValue = defaultValues.get(eKey);
  720. // // if (defaultValue) {
  721.  
  722. // // try {
  723. // // if ('data' in defaultValue) cnt.data = cpy(cnt.data);
  724. // // if ('__data' in defaultValue) cnt.__data = cpy(cnt.__data);
  725. // // } catch (e) {
  726. // // console.warn(e)
  727. // // }
  728. // // }
  729.  
  730. // // const flg001 = elm.__rm3Flg001__;
  731. // // if (cnt.__dataEnabled !== flg001) cnt.__dataEnabled = flg001;
  732.  
  733.  
  734.  
  735.  
  736.  
  737.  
  738.  
  739. // // const flg001 = elm.__rm3Flg001__;
  740. // // if (cnt.__dataEnabled !== flg001) cnt.__dataEnabled = flg001;
  741.  
  742.  
  743. // if (cnt.__dataPending && typeof cnt.__dataPending === 'object') cnt.__dataPending = null;
  744. // if (cnt.__dataOld && typeof cnt.__dataOld === 'object') cnt.__dataOld = null;
  745.  
  746. // // cnt.__dataInstanceProps = null;
  747. // if (cnt.__dataCounter && typeof cnt.__dataCounter === 'number') cnt.__dataCounter = 0;
  748. // // cnt.__serializing = !1;
  749.  
  750.  
  751.  
  752. // if ('__dataClientsInitialized' in cnt || '__dataClientsReady' in cnt) {
  753.  
  754. // if ('__dataClientsInitialized' in cnt !== '__dataClientsReady' in cnt) {
  755.  
  756. // console.log('[rm3-warning] __dataClientsInitialized and __dataClientsReady should exist in the controller');
  757.  
  758. // }
  759.  
  760. // cnt.__dataClientsReady = !1;
  761. // cnt.__dataLinkedPaths = cnt.__dataToNotify = cnt.__dataPendingClients = null;
  762. // cnt.__dataHasPaths = !1;
  763. // cnt.__dataCompoundStorage = null; // cnt.__dataCompoundStorage = cnt.__dataCompoundStorage || null;
  764. // cnt.__dataHost = null; // cnt.__dataHost = cnt.__dataHost || null;
  765. // if (!cnt.__dataTemp) cnt.__dataTemp = {}; // cnt.__dataTemp = {};
  766. // cnt.__dataClientsInitialized = !1;
  767.  
  768. // }
  769.  
  770. if (entryRecord[5] < 1e9) entryRecord[5] += 1;
  771. DEBUG_OPT && Promise.resolve().then(() => console.log(`${eKey} reuse`, entryRecord)); // give some time for attach process
  772. DEBUG_OPT && reuseRecord_.add([Date.now(), cnt.is, entryRecord]);
  773. DEBUG_OPT && reuseCount_.set(cnt.is, (reuseCount_.get(cnt.is) || 0) + 1)
  774. if (rm3.reuseCount < 1e9) rm3.reuseCount++;
  775.  
  776. return elm;
  777.  
  778.  
  779. }
  780.  
  781. // console.log('condi88', entryRecord[1] < 0 , entryRecord[2] > 0 , !!entryRecord[4], !!entryRecord[0].deref())
  782.  
  783. entryRecord[4] = false;
  784.  
  785. // console.log(entryRecord);
  786. // console.log('nodeDeleted',2, entryRecord[0]?.deref()?.nodeName)
  787. availablePool.deleteNode(node);
  788. node = prevNode;
  789.  
  790. }
  791. // for(const ) availablePool
  792. // noTimeCheck = false;
  793. }
  794.  
  795. } catch (e) {
  796. console.warn(e)
  797. }
  798. noTimeCheck = false;
  799.  
  800.  
  801. // console.log('createComponentDefine_', a, b, c)
  802.  
  803. // if (!reusePool.has(componentTag)) reusePool.set(componentTag, new LinkedArray()); // xx858
  804.  
  805. // const pool = reusePool.get(componentTag); // xx858
  806. // if (!(pool instanceof LinkedArray)) throw new Error(); // xx858
  807.  
  808.  
  809. const newElement = this.createComponent9512_(a, b, c);
  810. // if(componentTag.indexOf( 'ticker')>=0)console.log(1883, a,newElement)
  811.  
  812. try {
  813.  
  814. const cntE = insp(newElement);
  815. if (!cntE.attached9512 && cntE.attached) {
  816.  
  817. const cProtoE = getProto(newElement);
  818.  
  819.  
  820. if (cProtoE.attached === cntE.attached) {
  821.  
  822. if (!cProtoE.attached9512 && typeof cProtoE.attached === 'function' && cProtoE.attached.length === 0) {
  823.  
  824. cProtoE.attached9512 = cProtoE.attached;
  825.  
  826. cProtoE.attached = attachedDefine;
  827. // hookTos.add(a);
  828. }
  829. } else {
  830.  
  831. if (typeof cntE.attached === 'function' && cntE.attached.length === 3) {
  832. cntE.attached9512 = cntE.attached;
  833.  
  834. cntE.attached = attachedDefine;
  835. // hookTos.add(a);
  836. }
  837. }
  838.  
  839.  
  840. }
  841.  
  842. if (!cntE.detached9512 && cntE.detached) {
  843.  
  844. const cProtoE = getProto(newElement);
  845.  
  846.  
  847. if (cProtoE.detached === cntE.detached) {
  848.  
  849. if (!cProtoE.detached9512 && typeof cProtoE.detached === 'function' && cProtoE.detached.length === 0) {
  850.  
  851. cProtoE.detached9512 = cProtoE.detached;
  852.  
  853. cProtoE.detached = detachedDefine;
  854. // hookTos.add(a);
  855. }
  856. } else {
  857.  
  858. if (typeof cntE.detached === 'function' && cntE.detached.length === 3) {
  859. cntE.detached9512 = cntE.detached;
  860.  
  861. cntE.detached = detachedDefine;
  862. // hookTos.add(a);
  863. }
  864. }
  865.  
  866.  
  867. }
  868.  
  869.  
  870. const acceptance = true;
  871. // const acceptance = !cntE.__dataReady && cntE.__dataInvalid !== false; // we might need to change the acceptance condition along with YouTube Coding updates.
  872. if (acceptance) {
  873.  
  874. // [[ weak ElementNode, attached time, detached time, time of change, inside availablePool, reuse count ]]
  875. const entryRecord = [new WeakRef(newElement), -1, -1, -1, false, 0];
  876.  
  877. newElement.__rm3Tag003__ = eKey;
  878. // pool.push(entryRecord);
  879. entryRecords.set(newElement, entryRecord);
  880. // newElement.__rm3Tag001__ = creatorTag;
  881. // newElement.__rm3Tag002__ = componentTag;
  882.  
  883. // newElement.__rm3Flg001__ = cntE.__dataEnabled;
  884. // // console.log(5928, cntE.data, cntE.__data)
  885. // if (!defaultValues.has(eKey)){
  886.  
  887. // const o = {};
  888.  
  889. // if('data' in cntE) o.data = cpy(cntE.data);
  890. // if('__data' in cntE) o.__data = cpy(cntE.__data);
  891. // defaultValues.set(eKey, o);
  892.  
  893. // }
  894.  
  895. // if(!noValues.has(eKey)){
  896. // const o = {};
  897. // o.data = true;
  898. // try {
  899.  
  900. // if (!cntE.data) o.data = cntE.data;
  901. // } catch (e) { }
  902.  
  903. // o.__data = true;
  904. // try {
  905.  
  906. // if (!cntE.__data) o.__data = cntE.__data;
  907. // } catch (e) { }
  908. // noValues.set(eKey, o)
  909. // }
  910.  
  911. } else {
  912. // console.log(5920, cntE.__dataReady, cntE.__dataInvalid)
  913. }
  914.  
  915.  
  916. } catch (e) {
  917. console.warn(e);
  918. }
  919. return newElement;
  920.  
  921.  
  922. }
  923.  
  924. document.createElement9512 = document.createElement;
  925. document.createElement = function (a) {
  926. const r = document.createElement9512(a);
  927. try {
  928. const cnt = insp(r);
  929. if (cnt.createComponent_ && !cnt.createComponent9512_) {
  930. const cProto = getProto(r);
  931. if (cProto.createComponent_ === cnt.createComponent_) {
  932.  
  933. if (!cProto.createComponent9512_ && typeof cProto.createComponent_ === 'function' && cProto.createComponent_.length === 3) {
  934.  
  935. cProto.createComponent9512_ = cProto.createComponent_;
  936.  
  937. cProto.createComponent_ = createComponentDefine_;
  938. DEBUG_OPT && hookTos.add(a);
  939. }
  940. } else {
  941.  
  942. if (typeof cnt.createComponent_ === 'function' && cnt.createComponent_.length === 3) {
  943. cnt.createComponent9512_ = cnt.createComponent_;
  944.  
  945. cnt.createComponent_ = createComponentDefine_;
  946. DEBUG_OPT && hookTos.add(a);
  947. }
  948. }
  949.  
  950. }
  951.  
  952. } catch (e) {
  953. console.warn(e)
  954. }
  955.  
  956.  
  957. return r;
  958. }
  959.  
  960. rm3.checkWhetherUnderParent = () => {
  961. const r = [];
  962. for (const operation of operations) {
  963. const elm = operation[0].deref();
  964. if (operation[2] > 0) {
  965.  
  966. r.push([!!elm, elm?.nodeName.toLowerCase(), elm?.parentNode === null]);
  967. }
  968. }
  969. return r;
  970. }
  971.  
  972. rm3.hookTags = () => {
  973.  
  974. const r = new Set();
  975. for (const operation of operations) {
  976. const elm = operation[0].deref();
  977. if (elm && elm.is) {
  978.  
  979. r.add(elm.is)
  980. }
  981. }
  982. return [...r];
  983. }
  984.  
  985.  
  986. DEBUG_OPT && (rm3.reuseRecord = () => {
  987. return [...reuseRecord_]; // [[debug]]
  988. });
  989.  
  990. DEBUG_OPT && (rm3.reuseCount_ = reuseCount_);
  991.  
  992. }
  993.  
  994. (rm3.reuseCount = 0); // window.rm3 will be zero initially if this script has no runtime complier error in the initialization phase.
  995.  
  996. })();