YouTube RM3 - Reduce Memory Usage by Reusing Components

一个在后台运行的简单工具,通过重复利用 YouTube 组件,从而在长期减少内存使用量。

目前为 2024-12-22 提交的版本。查看 最新版本

  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.0014
  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 = false;
  543. let elm = null;
  544. if (entryRecord[1] < 0 && entryRecord[2] > 0 && entryRecord[4]) {
  545. elm = entryRecord[0].deref();
  546. if (elm && elm instanceof HTMLElement && elm.isConnected === false && insp(elm).isAttached === false && elm.parentNode === null) {
  547. ok = true;
  548. }
  549. }
  550.  
  551. if (ok) {
  552.  
  553. // useEntryRecord = entryRecord;
  554. entryRecord[4] = false;
  555. availablePool.deleteNode(node);
  556. // break;
  557.  
  558.  
  559. const cnt = insp(elm);
  560.  
  561. // cnt.__dataReady = false;
  562. cnt.__dataInvalid = true;
  563. cnt.__dataEnabled = false; // tbc
  564. // if('__dataEnabled' in cnt) cnt.__dataEnabled = false;
  565. // if ('__dataReady' in cnt && typeof cnt.__dataReady === 'boolean') cnt.__dataReady = false;
  566. // if ('__dataInvalid' in cnt && typeof cnt.__dataInvalid === 'boolean') cnt.__dataInvalid = true;
  567.  
  568. // try {
  569. // if ('data' in cnt) cnt.data = null;
  570. // if ('__data' in cnt) cnt.__data = null;
  571. // } catch (e) {
  572. // console.warn(e)
  573. // }
  574.  
  575. // try {
  576. // if ('data' in cnt) cnt.data = {};
  577. // if ('__data' in cnt) cnt.__data = {};
  578. // } catch (e) {
  579. // console.warn(e)
  580. // }
  581.  
  582.  
  583.  
  584.  
  585.  
  586.  
  587.  
  588.  
  589.  
  590.  
  591.  
  592. // const noValue = noValues.get(eKey);
  593. // if(noValue){
  594. // if(!noValue.data) cnt.data = noValue.data;
  595. // if(!noValue.__data) cnt.data = noValue.__data;
  596. // }
  597.  
  598. // const defaultValue = defaultValues.get(eKey);
  599. // if (defaultValue) {
  600.  
  601. // try {
  602. // if ('data' in defaultValue) cnt.data = cpy(cnt.data);
  603. // if ('__data' in defaultValue) cnt.__data = cpy(cnt.__data);
  604. // } catch (e) {
  605. // console.warn(e)
  606. // }
  607. // }
  608.  
  609. // const flg001 = elm.__rm3Flg001__;
  610. // if (cnt.__dataEnabled !== flg001) cnt.__dataEnabled = flg001;
  611.  
  612.  
  613.  
  614.  
  615.  
  616.  
  617.  
  618. // const flg001 = elm.__rm3Flg001__;
  619. // if (cnt.__dataEnabled !== flg001) cnt.__dataEnabled = flg001;
  620.  
  621.  
  622. if (cnt.__dataPending && typeof cnt.__dataPending === 'object') cnt.__dataPending = null;
  623. if (cnt.__dataOld && typeof cnt.__dataOld === 'object') cnt.__dataOld = null;
  624.  
  625. // cnt.__dataInstanceProps = null;
  626. if (cnt.__dataCounter && typeof cnt.__dataCounter === 'number') cnt.__dataCounter = 0;
  627. // cnt.__serializing = !1;
  628.  
  629.  
  630.  
  631. if ('__dataClientsInitialized' in cnt || '__dataClientsReady' in cnt) {
  632.  
  633. if ('__dataClientsInitialized' in cnt !== '__dataClientsReady' in cnt) {
  634.  
  635. console.log('[rm3-warning] __dataClientsInitialized and __dataClientsReady should exist in the controller');
  636.  
  637. }
  638.  
  639. cnt.__dataClientsReady = !1;
  640. cnt.__dataLinkedPaths = cnt.__dataToNotify = cnt.__dataPendingClients = null;
  641. cnt.__dataHasPaths = !1;
  642. cnt.__dataCompoundStorage = null; // cnt.__dataCompoundStorage = cnt.__dataCompoundStorage || null;
  643. cnt.__dataHost = null; // cnt.__dataHost = cnt.__dataHost || null;
  644. if (!cnt.__dataTemp) cnt.__dataTemp = {}; // cnt.__dataTemp = {};
  645. cnt.__dataClientsInitialized = !1;
  646.  
  647. }
  648.  
  649. if (entryRecord[5] < 1e9) entryRecord[5] += 1;
  650. DEBUG_OPT && Promise.resolve().then(() => console.log(`${eKey} reuse`, entryRecord)); // give some time for attach process
  651. DEBUG_OPT && reuseRecord_.add([Date.now(), entryRecord]);
  652. if (rm3.reuseCount < 1e9) rm3.reuseCount++;
  653.  
  654. return elm;
  655.  
  656.  
  657. }
  658.  
  659. entryRecord[4] = false;
  660. availablePool.deleteNode(node);
  661. node = prevNode;
  662.  
  663. }
  664. // for(const ) availablePool
  665. // noTimeCheck = false;
  666. }
  667.  
  668. } catch (e) {
  669. console.warn(e)
  670. }
  671. noTimeCheck = false;
  672.  
  673.  
  674. // console.log('createComponentDefine_', a, b, c)
  675.  
  676. // if (!reusePool.has(componentTag)) reusePool.set(componentTag, new LinkedArray()); // xx858
  677.  
  678. // const pool = reusePool.get(componentTag); // xx858
  679. // if (!(pool instanceof LinkedArray)) throw new Error(); // xx858
  680.  
  681.  
  682. const newElement = this.createComponent9512_(a, b, c);
  683. // if(componentTag.indexOf( 'ticker')>=0)console.log(1883, a,newElement)
  684.  
  685. try {
  686.  
  687. const cntE = insp(newElement);
  688. if (!cntE.attached9512 && cntE.attached) {
  689.  
  690. const cProtoE = getProto(newElement);
  691.  
  692.  
  693. if (cProtoE.attached === cntE.attached) {
  694.  
  695. if (!cProtoE.attached9512 && typeof cProtoE.attached === 'function' && cProtoE.attached.length === 0) {
  696.  
  697. cProtoE.attached9512 = cProtoE.attached;
  698.  
  699. cProtoE.attached = attachedDefine;
  700. // hookTos.add(a);
  701. }
  702. } else {
  703.  
  704. if (typeof cntE.attached === 'function' && cntE.attached.length === 3) {
  705. cntE.attached9512 = cntE.attached;
  706.  
  707. cntE.attached = attachedDefine;
  708. // hookTos.add(a);
  709. }
  710. }
  711.  
  712.  
  713. }
  714.  
  715. if (!cntE.detached9512 && cntE.detached) {
  716.  
  717. const cProtoE = getProto(newElement);
  718.  
  719.  
  720. if (cProtoE.detached === cntE.detached) {
  721.  
  722. if (!cProtoE.detached9512 && typeof cProtoE.detached === 'function' && cProtoE.detached.length === 0) {
  723.  
  724. cProtoE.detached9512 = cProtoE.detached;
  725.  
  726. cProtoE.detached = detachedDefine;
  727. // hookTos.add(a);
  728. }
  729. } else {
  730.  
  731. if (typeof cntE.detached === 'function' && cntE.detached.length === 3) {
  732. cntE.detached9512 = cntE.detached;
  733.  
  734. cntE.detached = detachedDefine;
  735. // hookTos.add(a);
  736. }
  737. }
  738.  
  739.  
  740. }
  741.  
  742.  
  743. const acceptance = true;
  744. // const acceptance = !cntE.__dataReady && cntE.__dataInvalid !== false; // we might need to change the acceptance condition along with YouTube Coding updates.
  745. if (acceptance) {
  746.  
  747. // [[ weak ElementNode, attached time, detached time, time of change, inside availablePool, reuse count ]]
  748. const entryRecord = [new WeakRef(newElement), -1, -1, -1, false, 0];
  749.  
  750. newElement.__rm3Tag003__ = eKey;
  751. // pool.push(entryRecord);
  752. entryRecords.set(newElement, entryRecord);
  753. // newElement.__rm3Tag001__ = creatorTag;
  754. // newElement.__rm3Tag002__ = componentTag;
  755.  
  756. // newElement.__rm3Flg001__ = cntE.__dataEnabled;
  757. // // console.log(5928, cntE.data, cntE.__data)
  758. // if (!defaultValues.has(eKey)){
  759.  
  760. // const o = {};
  761.  
  762. // if('data' in cntE) o.data = cpy(cntE.data);
  763. // if('__data' in cntE) o.__data = cpy(cntE.__data);
  764. // defaultValues.set(eKey, o);
  765.  
  766. // }
  767.  
  768. // if(!noValues.has(eKey)){
  769. // const o = {};
  770. // o.data = true;
  771. // try {
  772.  
  773. // if (!cntE.data) o.data = cntE.data;
  774. // } catch (e) { }
  775.  
  776. // o.__data = true;
  777. // try {
  778.  
  779. // if (!cntE.__data) o.__data = cntE.__data;
  780. // } catch (e) { }
  781. // noValues.set(eKey, o)
  782. // }
  783.  
  784. } else {
  785. // console.log(5920, cntE.__dataReady, cntE.__dataInvalid)
  786. }
  787.  
  788.  
  789. } catch (e) {
  790. console.warn(e);
  791. }
  792. return newElement;
  793.  
  794.  
  795. }
  796.  
  797. document.createElement9512 = document.createElement;
  798. document.createElement = function (a) {
  799. const r = document.createElement9512(a);
  800. try {
  801. const cnt = insp(r);
  802. if (cnt.createComponent_ && !cnt.createComponent9512_) {
  803. const cProto = getProto(r);
  804. if (cProto.createComponent_ === cnt.createComponent_) {
  805.  
  806. if (!cProto.createComponent9512_ && typeof cProto.createComponent_ === 'function' && cProto.createComponent_.length === 3) {
  807.  
  808. cProto.createComponent9512_ = cProto.createComponent_;
  809.  
  810. cProto.createComponent_ = createComponentDefine_;
  811. DEBUG_OPT && hookTos.add(a);
  812. }
  813. } else {
  814.  
  815. if (typeof cnt.createComponent_ === 'function' && cnt.createComponent_.length === 3) {
  816. cnt.createComponent9512_ = cnt.createComponent_;
  817.  
  818. cnt.createComponent_ = createComponentDefine_;
  819. DEBUG_OPT && hookTos.add(a);
  820. }
  821. }
  822.  
  823. }
  824.  
  825. } catch (e) {
  826. console.warn(e)
  827. }
  828.  
  829.  
  830. return r;
  831. }
  832.  
  833. }
  834.  
  835. (rm3.reuseCount = 0); // window.rm3 will be zero initially if this script has no runtime complier error in the initialization phase.
  836.  
  837. })();