YouTube RM3 - Reduce Memory Usage by Reusing Components

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

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

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址