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-21. See the latest version.

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