// ==UserScript==
// @name YouTube RM3 - Reduce Memory Usage by Reusing Components
// @namespace Violentmonkey Scripts
// @version 0.1.0019
// @license MIT
// @match https://www.youtube.com/*
// @match https://studio.youtube.com/live_chat*
//
//
// @author CY Fung
// @run-at document-start
// @grant none
// @unwrap
// @allFrames true
// @inject-into page
//
// @compatible firefox Violentmonkey
// @compatible firefox Tampermonkey
// @compatible firefox FireMonkey
// @compatible chrome Violentmonkey
// @compatible chrome Tampermonkey
// @compatible opera Violentmonkey
// @compatible opera Tampermonkey
// @compatible safari Stay
// @compatible edge Violentmonkey
// @compatible edge Tampermonkey
// @compatible brave Violentmonkey
// @compatible brave Tampermonkey
//
// @description A simple tool that runs in the background to reuse YouTube components, thereby reducing memory usage over time.
// @description:en A simple tool that runs in the background to reuse YouTube components, thereby reducing memory usage over time.
// @description:ja YouTubeコンポーネントを再利用することで、長期的なメモリ使用量の削減を目指す、バックグラウンドで実行されるシンプルなツールです。
// @description:zh-TW 一個在背景執行的簡易工具,可重複利用 YouTube 元件,從而在長期減少記憶體使用量。
// @description:zh-CN 一个在后台运行的简单工具,通过重复利用 YouTube 组件,从而在长期减少内存使用量。
//
// ==/UserScript==
const rm3 = window.rm3 = {};
// console.log(3001);
(() => {
const DEBUG_OPT = false;
const CONFIRM_TIME = 4000;
const CHECK_INTERVAL = 400;
const DEBUG_dataChangeReflection = true;
/** @type {globalThis.PromiseConstructor} */
const Promise = (async () => { })().constructor; // YouTube hacks Promise in WaterFox Classic and "Promise.resolve(0)" nevers resolve.
// https://qiita.com/piroor/items/02885998c9f76f45bfa0
// https://gist.github.com/piroor/829ecb32a52c2a42e5393bbeebe5e63f
function uniq(array) {
return [...new Set(array)];
};
rm3.uniq = uniq; // [[debug]]
DEBUG_OPT && (rm3.location= location.href);
rm3.inspect = () => {
return uniq([...document.getElementsByTagName('*')].filter(e => e?.polymerController?.createComponent_).map(e => e.nodeName)); // [[debug]]
}
const insp = o => o ? (o.polymerController || o.inst || o || 0) : (o || 0);
const indr = o => insp(o).$ || o.$ || 0;
const getProto = (element) => {
if (element) {
const cnt = insp(element);
return cnt.constructor.prototype || null;
}
return null;
}
const LinkedArray = (() => {
class Node {
constructor(value) {
this.value = value;
this.next = null;
this.prev = null;
}
}
class LinkedArray {
constructor() {
this.head = null;
this.tail = null;
this.length = 0;
}
push(value) {
const newNode = new Node(value);
if (this.length === 0) {
this.head = newNode;
this.tail = newNode;
} else {
this.tail.next = newNode;
newNode.prev = this.tail;
this.tail = newNode;
}
this.length++;
return this.length;
}
pop() {
if (this.length === 0) return undefined;
const removedNode = this.tail;
if (this.length === 1) {
this.head = null;
this.tail = null;
} else {
this.tail = removedNode.prev;
this.tail.next = null;
removedNode.prev = null;
}
this.length--;
return removedNode.value;
}
unshift(value) {
const newNode = new Node(value);
if (this.length === 0) {
this.head = newNode;
this.tail = newNode;
} else {
newNode.next = this.head;
this.head.prev = newNode;
this.head = newNode;
}
this.length++;
return this.length;
}
shift() {
if (this.length === 0) return undefined;
const removedNode = this.head;
if (this.length === 1) {
this.head = null;
this.tail = null;
} else {
this.head = removedNode.next;
this.head.prev = null;
removedNode.next = null;
}
this.length--;
return removedNode.value;
}
size() {
return this.length;
}
// Get a node by index (0-based)
getNode(index) {
if (index < 0 || index >= this.length) return null;
let current;
let counter;
// Optimization: start from closest end
if (index < this.length / 2) {
current = this.head;
counter = 0;
while (counter !== index) {
current = current.next;
counter++;
}
} else {
current = this.tail;
counter = this.length - 1;
while (counter !== index) {
current = current.prev;
counter--;
}
}
return current;
}
// Get value by index
get(index) {
const node = this.getNode(index);
return node ? node.value : undefined;
}
// Find the first node with the given value and return both node and index
findNode(value) {
let current = this.head;
let idx = 0;
while (current) {
if (current.value === value) {
return { node: current, index: idx };
}
current = current.next;
idx++;
}
return { node: null, index: -1 };
}
toArray() {
const arr = [];
let current = this.head;
while (current) {
arr.push(current.value);
current = current.next;
}
return arr;
}
// Insert a new value before a given node (provided you already have the node reference)
insertBeforeNode(node, newValue) {
if (!node) {
this.unshift(newValue);
return true;
}
if (node === this.head) {
// If the target is the head, just unshift
this.unshift(newValue);
} else {
const newNode = new Node(newValue);
const prevNode = node.prev;
prevNode.next = newNode;
newNode.prev = prevNode;
newNode.next = node;
node.prev = newNode;
this.length++;
}
return true;
}
// Insert a new value after a given node (provided you already have the node reference)
insertAfterNode(node, newValue) {
if (!node) {
this.push(newValue);
return true;
}
if (node === this.tail) {
// If the target is the tail, just push
this.push(newValue);
} else {
const newNode = new Node(newValue);
const nextNode = node.next;
node.next = newNode;
newNode.prev = node;
newNode.next = nextNode;
nextNode.prev = newNode;
this.length++;
}
return true;
}
// Insert a new value before the first occurrence of an existing value (search by value)
insertBefore(existingValue, newValue) {
const { node } = this.findNode(existingValue);
if (!node) return false; // Not found
return this.insertBeforeNode(node, newValue);
}
// Insert a new value after the first occurrence of an existing value (search by value)
insertAfter(existingValue, newValue) {
const { node } = this.findNode(existingValue);
if (!node) return false; // Not found
return this.insertAfterNode(node, newValue);
}
// Delete a given node from the list
deleteNode(node) {
if (!node) return false;
if (this.length === 1 && node === this.head && node === this.tail) {
// Only one element in the list
this.head = null;
this.tail = null;
} else if (node === this.head) {
// Node is the head
this.head = node.next;
this.head.prev = null;
node.next = null;
} else if (node === this.tail) {
// Node is the tail
this.tail = node.prev;
this.tail.next = null;
node.prev = null;
} else {
// Node is in the middle
const prevNode = node.prev;
const nextNode = node.next;
prevNode.next = nextNode;
nextNode.prev = prevNode;
node.prev = null;
node.next = null;
}
this.length--;
return true;
}
}
LinkedArray.Node = Node;
return LinkedArray;
})();
class LimitedSizeSet extends Set {
constructor(n) {
super();
this.limit = n;
}
add(key) {
if (!super.has(key)) {
super.add(key);
let n = super.size - this.limit;
if (n > 0) {
const iterator = super.values();
do {
const firstKey = iterator.next().value; // Get the first (oldest) key
super.delete(firstKey); // Delete the oldest key
} while (--n > 0)
}
}
}
removeAdd(key) {
super.delete(key);
this.add(key);
}
}
if (!document.createElement9512 && typeof document.createElement === 'function' && document.createElement.length === 1) {
// sizing of Map / Set. Shall limit ?
const hookTos = new Set(); // [[debug]]
DEBUG_OPT && (rm3.hookTos = hookTos);
// const reusePool = new Map(); // xx858
const entryRecords = new WeakMap(); // a weak link between element and record
// rm3.list = [];
const operations = rm3.operations = new Set(); // to find out the "oldest elements"
const availablePools = rm3.availablePools = new Map(); // those "old elements" can be used
let lastTimeCheck = 0;
const reuseRecord_ = new LimitedSizeSet(256); // [[debug]]
const reuseCount_ = new Map();
let noTimeCheck = false;
// const defaultValues = new Map();
// const noValues = new Map();
const timeCheck = () => {
// regularly check elements are old enough to put into the available pools
// 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.
const ct = Date.now();
if (ct - lastTimeCheck < CHECK_INTERVAL || noTimeCheck) return;
lastTimeCheck = ct;
noTimeCheck = true;
// 16,777,216
if (hookTos.size > 777216) hookTos.clear(); // just debug usage, dont concern
if (operations.size > 7777216) {
// extremely old elements in operations mean they have no attach/detach action. so no reuse as well. they are just trash in memory.
// as no checking of the weakRef.deref() being null or not, those trash could be already cleaned. However we don't concern this.
// (not to count whether they are actual memory trash or not)
const half = operations.size >>> 1;
let i = 0;
for (const value of operations) {
if (i++ > half) break;
operations.delete(value);
}
}
// {
// // smallest to largest
// // past to recent
// const iterator = operations[Symbol.iterator]();
// console.log(1831, '------------------------')
// while (true) {
// const iteratorResult = iterator.next(); // 順番に値を取りだす
// if (iteratorResult.done) break; // 取り出し終えたなら、break
// console.log(1835, iteratorResult.value[3])
// }
// console.log(1839, '------------------------')
// }
// Set iterator
// s.add(2) s.add(6) s.add(1) s.add(3)
// next: 2 -> 6 -> 1 -> 3
// op1 (oldest) -> op2 -> op3 -> op4 (latest)
const iterator = operations[Symbol.iterator]();
const targetTime = ct - CONFIRM_TIME;
const pivotNodes = new WeakMap();
while (true) {
const iteratorResult = iterator.next(); // 順番に値を取りだす
if (iteratorResult.done) break; // 取り出し終えたなら、break
const entryRecord = iteratorResult.value;
if (entryRecord[3] > targetTime) break;
if (!entryRecord[4] && entryRecord[1] < 0 && entryRecord[2] > 0) {
const element = entryRecord[0].deref();
const eKey = (element || 0).__rm3Tag003__;
if (!eKey) {
operations.delete(entryRecord);
} else if (element.isConnected === false && insp(element).isAttached === false) {
entryRecord[4] = true;
let availablePool = availablePools.get(eKey);
if (!availablePool) availablePools.set(eKey, availablePool = new LinkedArray());
if (!(availablePool instanceof LinkedArray)) throw new Error();
DEBUG_OPT && console.log(3885,'add key', eKey, availablePools.size)
// rm3.showSize = ()=>availablePools.size
// setTimeout(()=>{
// // window?.euu1 = availablePools
// // window?.euu2 = availablePools.size
// console.log(availablePools.size)
// }, 8000)
let pivotNode = pivotNodes.get(availablePool);
if (!pivotNode) pivotNodes.set(availablePool, pivotNode = availablePool.head) // cached the previous newest node (head) as pivotNode
availablePool.insertBeforeNode(pivotNode, entryRecord); // head = newest, tail = oldest
}
}
}
noTimeCheck = false;
}
const attachedDefine = function () {
Promise.resolve().then(timeCheck);
try {
const hostElement = this?.hostElement;
if (hostElement instanceof HTMLElement) {
const entryRecord = entryRecords.get(hostElement);
if (entryRecord && entryRecord[0].deref() === hostElement && hostElement.isConnected === true && this?.isAttached === true) {
noTimeCheck = true;
const ct = Date.now();
entryRecord[1] = ct;
entryRecord[2] = -1;
entryRecord[3] = ct;
operations.delete(entryRecord);
operations.add(entryRecord);
noTimeCheck = false;
// note: because of performance prespective, deletion for availablePools[eKey]'s linked element would not be done here.
// entryRecord[4] is not required to be updated here.
}
}
} catch (e) { }
return this.attached9512();
}
const detachedDefine = function () {
Promise.resolve().then(timeCheck);
try {
const hostElement = this?.hostElement;
if (hostElement instanceof HTMLElement) {
const entryRecord = entryRecords.get(hostElement);
if (entryRecord && entryRecord[0].deref() === hostElement && hostElement.isConnected === false && this?.isAttached === false) {
noTimeCheck= true;
const ct = Date.now();
entryRecord[2] = ct;
entryRecord[1] = -1;
entryRecord[3] = ct;
operations.delete(entryRecord);
operations.add(entryRecord);
noTimeCheck= false;
// note: because of performance prespective, deletion for availablePools[eKey]'s linked element would not be done here.
// entryRecord[4] is not required to be updated here.
}
}
} catch (e) { }
return this.detached9512();
}
// function cpy(x) {
// if (!x) return x;
// try {
// if (typeof x === 'object' && typeof x.length ==='number' && typeof x.slice === 'function') {
// x = x.slice(0)
// } else if (typeof x === 'object' && !x.length) {
// x = JSON.parse(JSON.stringify(x));
// } else {
// return Object.assign({}, x);
// }
// } catch (e) { }
// return x;
// }
async function digestMessage(message) {
const msgUint8 = new TextEncoder().encode(message); // (utf-8 の) Uint8Array にエンコードする
const hashBuffer = await crypto.subtle.digest("SHA-256", msgUint8); // メッセージをハッシュする
const hashArray = Array.from(new Uint8Array(hashBuffer)); // バッファーをバイト列に変換する
const hashHex = hashArray
.map((b) => b.toString(16).padStart(2, "0"))
.join(""); // バイト列を 16 進文字列に変換する
return hashHex.toUpperCase();
}
let onPageContainer = null;
const createComponentDefine_ = function (a, b, c) {
Promise.resolve().then(timeCheck);
const creatorTag = this?.is || this?.nodeName?.toLowerCase() || '';
const componentTag = typeof a === 'string' ? a : ((a || 0).component || '');
const eKey = creatorTag && componentTag ? `${creatorTag}.${componentTag}` : '*'; // '*' for play-safe
const availablePool = availablePools.get(eKey);
try {
if (availablePool instanceof LinkedArray) {
noTimeCheck = true;
let node = availablePool.tail; // oldest
while (node instanceof LinkedArray.Node) {
const entryRecord = node.value;
const prevNode = node.prev;
let ok = false;
let elm = null;
if (entryRecord[1] < 0 && entryRecord[2] > 0 && entryRecord[4]) {
elm = entryRecord[0].deref();
// elm && console.log(3882, (elm.__shady_native_textContent || elm.textContent))
if (elm && elm instanceof HTMLElement && elm.isConnected === false && insp(elm).isAttached === false && elm.parentNode === null) {
ok = true;
}
}
if (ok) {
// useEntryRecord = entryRecord;
entryRecord[4] = false;
// console.log('nodeDeleted', 1, entryRecord[0].deref().nodeName)
availablePool.deleteNode(node);
// break;
if (!onPageContainer) {
let p = document.createElement('noscript');
document.body.prepend(p);
onPageContainer = p;
}
onPageContainer.appendChild(elm); // to fix some issues for the rendered elements
const cnt = insp(elm);
cnt.__dataInvalid = false;
// cnt._initializeProtoProperties(cnt.data)
// window.meaa = cnt.$.container;
if (typeof (cnt.__data || 0) === 'object') {
cnt.__data = Object.assign({}, cnt.__data);
}
cnt.__dataPending = {};
cnt.__dataOld = {};
try {
cnt.markDirty();
} catch (e) { }
try {
cnt.markDirtyVisibilityObserver();
} catch (e) { }
try{
cnt.wasPrescan = cnt.wasVisible = !1
}catch(e){}
// try{
// cnt._setPendingProperty('data', Object.assign({}, cntData), !0);
// }catch(e){}
// try {
// cnt._flushProperties();
// } catch (e) { }
if (DEBUG_OPT && DEBUG_dataChangeReflection) {
let jC1 = null;
let jC2 = null;
const jKey = `${Math.floor(Math.random() * 314159265359 + 314159265359).toString(36)}`;
try {
jC1 = (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent);
// console.log(83802, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
} catch (e) {
console.warn(e);
}
setTimeout(() => {
try {
jC2 = (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent);
// console.log(83804, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
} catch (e) {
console.warn(e);
}
(async () => {
jC1 = await digestMessage(jC1);
jC2 = await digestMessage(jC2);
console.log(83804, jKey, jC1.substring(0, 7), jC2.substring(0, 7))
})()
}, 1000);
}
// // try{
// // console.log(83801, JSON.stringify(cntData))
// // }catch(e){
// // console.warn(e);
// // }
// const jKey = `${Math.floor(Math.random() * 314159265359 + 314159265359).toString(36)}`;
// try{
// console.log(83802, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
// }catch(e){
// console.warn(e);
// }
// setTimeout(()=>{
// // try{
// // console.log(83803, JSON.stringify(cntData))
// // }catch(e){
// // console.warn(e);
// // }
// try{
// console.log(83804, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
// }catch(e){
// console.warn(e);
// }
// }, 1000);
// reference
// https://www.youtube.com/s/desktop/c01ea7e3/jsbin/live_chat_polymer.vflset/live_chat_polymer.js
// a.prototype._initializeProtoProperties = function(c) {
// this.__data = Object.create(c);
// this.__dataPending = Object.create(c);
// this.__dataOld = {}
// }
// ------- (NO USE) ------
//
// a.prototype._initializeProperties = function() {
// this.__dataProto && (this._initializeProtoProperties(this.__dataProto),
// this.__dataProto = null);
// b.prototype._initializeProperties.call(this)
// }
// ;
// a.prototype._initializeProtoProperties = function(c) {
// for (var d in c)
// this._setProperty(d, c[d])
// }
//
// ------- (NO USE) ------
// // cnt.__dataReady = false;
// cnt.__dataInvalid = true;
// cnt.__dataEnabled = false; // tbc
// // if('__dataEnabled' in cnt) cnt.__dataEnabled = false;
// // if ('__dataReady' in cnt && typeof cnt.__dataReady === 'boolean') cnt.__dataReady = false;
// // if ('__dataInvalid' in cnt && typeof cnt.__dataInvalid === 'boolean') cnt.__dataInvalid = true;
// // try {
// // if ('data' in cnt) cnt.data = null;
// // if ('__data' in cnt) cnt.__data = null;
// // } catch (e) {
// // console.warn(e)
// // }
// // try {
// // if ('data' in cnt) cnt.data = {};
// // if ('__data' in cnt) cnt.__data = {};
// // } catch (e) {
// // console.warn(e)
// // }
// // const noValue = noValues.get(eKey);
// // if(noValue){
// // if(!noValue.data) cnt.data = noValue.data;
// // if(!noValue.__data) cnt.data = noValue.__data;
// // }
// // const defaultValue = defaultValues.get(eKey);
// // if (defaultValue) {
// // try {
// // if ('data' in defaultValue) cnt.data = cpy(cnt.data);
// // if ('__data' in defaultValue) cnt.__data = cpy(cnt.__data);
// // } catch (e) {
// // console.warn(e)
// // }
// // }
// // const flg001 = elm.__rm3Flg001__;
// // if (cnt.__dataEnabled !== flg001) cnt.__dataEnabled = flg001;
// // const flg001 = elm.__rm3Flg001__;
// // if (cnt.__dataEnabled !== flg001) cnt.__dataEnabled = flg001;
// if (cnt.__dataPending && typeof cnt.__dataPending === 'object') cnt.__dataPending = null;
// if (cnt.__dataOld && typeof cnt.__dataOld === 'object') cnt.__dataOld = null;
// // cnt.__dataInstanceProps = null;
// if (cnt.__dataCounter && typeof cnt.__dataCounter === 'number') cnt.__dataCounter = 0;
// // cnt.__serializing = !1;
// if ('__dataClientsInitialized' in cnt || '__dataClientsReady' in cnt) {
// if ('__dataClientsInitialized' in cnt !== '__dataClientsReady' in cnt) {
// console.log('[rm3-warning] __dataClientsInitialized and __dataClientsReady should exist in the controller');
// }
// cnt.__dataClientsReady = !1;
// cnt.__dataLinkedPaths = cnt.__dataToNotify = cnt.__dataPendingClients = null;
// cnt.__dataHasPaths = !1;
// cnt.__dataCompoundStorage = null; // cnt.__dataCompoundStorage = cnt.__dataCompoundStorage || null;
// cnt.__dataHost = null; // cnt.__dataHost = cnt.__dataHost || null;
// if (!cnt.__dataTemp) cnt.__dataTemp = {}; // cnt.__dataTemp = {};
// cnt.__dataClientsInitialized = !1;
// }
if (entryRecord[5] < 1e9) entryRecord[5] += 1;
DEBUG_OPT && Promise.resolve().then(() => console.log(`${eKey} reuse`, entryRecord)); // give some time for attach process
DEBUG_OPT && reuseRecord_.add([Date.now(), cnt.is, entryRecord]);
DEBUG_OPT && reuseCount_.set(cnt.is, (reuseCount_.get(cnt.is) || 0) + 1)
if (rm3.reuseCount < 1e9) rm3.reuseCount++;
return elm;
}
// console.log('condi88', entryRecord[1] < 0 , entryRecord[2] > 0 , !!entryRecord[4], !!entryRecord[0].deref())
entryRecord[4] = false;
// console.log(entryRecord);
// console.log('nodeDeleted',2, entryRecord[0]?.deref()?.nodeName)
availablePool.deleteNode(node);
node = prevNode;
}
// for(const ) availablePool
// noTimeCheck = false;
}
} catch (e) {
console.warn(e)
}
noTimeCheck = false;
// console.log('createComponentDefine_', a, b, c)
// if (!reusePool.has(componentTag)) reusePool.set(componentTag, new LinkedArray()); // xx858
// const pool = reusePool.get(componentTag); // xx858
// if (!(pool instanceof LinkedArray)) throw new Error(); // xx858
const newElement = this.createComponent9512_(a, b, c);
// if(componentTag.indexOf( 'ticker')>=0)console.log(1883, a,newElement)
try {
const cntE = insp(newElement);
if (!cntE.attached9512 && cntE.attached) {
const cProtoE = getProto(newElement);
if (cProtoE.attached === cntE.attached) {
if (!cProtoE.attached9512 && typeof cProtoE.attached === 'function' && cProtoE.attached.length === 0) {
cProtoE.attached9512 = cProtoE.attached;
cProtoE.attached = attachedDefine;
// hookTos.add(a);
}
} else {
if (typeof cntE.attached === 'function' && cntE.attached.length === 3) {
cntE.attached9512 = cntE.attached;
cntE.attached = attachedDefine;
// hookTos.add(a);
}
}
}
if (!cntE.detached9512 && cntE.detached) {
const cProtoE = getProto(newElement);
if (cProtoE.detached === cntE.detached) {
if (!cProtoE.detached9512 && typeof cProtoE.detached === 'function' && cProtoE.detached.length === 0) {
cProtoE.detached9512 = cProtoE.detached;
cProtoE.detached = detachedDefine;
// hookTos.add(a);
}
} else {
if (typeof cntE.detached === 'function' && cntE.detached.length === 3) {
cntE.detached9512 = cntE.detached;
cntE.detached = detachedDefine;
// hookTos.add(a);
}
}
}
const acceptance = true;
// const acceptance = !cntE.__dataReady && cntE.__dataInvalid !== false; // we might need to change the acceptance condition along with YouTube Coding updates.
if (acceptance) {
// [[ weak ElementNode, attached time, detached time, time of change, inside availablePool, reuse count ]]
const entryRecord = [new WeakRef(newElement), -1, -1, -1, false, 0];
newElement.__rm3Tag003__ = eKey;
// pool.push(entryRecord);
entryRecords.set(newElement, entryRecord);
// newElement.__rm3Tag001__ = creatorTag;
// newElement.__rm3Tag002__ = componentTag;
// newElement.__rm3Flg001__ = cntE.__dataEnabled;
// // console.log(5928, cntE.data, cntE.__data)
// if (!defaultValues.has(eKey)){
// const o = {};
// if('data' in cntE) o.data = cpy(cntE.data);
// if('__data' in cntE) o.__data = cpy(cntE.__data);
// defaultValues.set(eKey, o);
// }
// if(!noValues.has(eKey)){
// const o = {};
// o.data = true;
// try {
// if (!cntE.data) o.data = cntE.data;
// } catch (e) { }
// o.__data = true;
// try {
// if (!cntE.__data) o.__data = cntE.__data;
// } catch (e) { }
// noValues.set(eKey, o)
// }
} else {
// console.log(5920, cntE.__dataReady, cntE.__dataInvalid)
}
} catch (e) {
console.warn(e);
}
return newElement;
}
document.createElement9512 = document.createElement;
document.createElement = function (a) {
const r = document.createElement9512(a);
try {
const cnt = insp(r);
if (cnt.createComponent_ && !cnt.createComponent9512_) {
const cProto = getProto(r);
if (cProto.createComponent_ === cnt.createComponent_) {
if (!cProto.createComponent9512_ && typeof cProto.createComponent_ === 'function' && cProto.createComponent_.length === 3) {
cProto.createComponent9512_ = cProto.createComponent_;
cProto.createComponent_ = createComponentDefine_;
DEBUG_OPT && hookTos.add(a);
}
} else {
if (typeof cnt.createComponent_ === 'function' && cnt.createComponent_.length === 3) {
cnt.createComponent9512_ = cnt.createComponent_;
cnt.createComponent_ = createComponentDefine_;
DEBUG_OPT && hookTos.add(a);
}
}
}
} catch (e) {
console.warn(e)
}
return r;
}
rm3.checkWhetherUnderParent = () => {
const r = [];
for (const operation of operations) {
const elm = operation[0].deref();
if (operation[2] > 0) {
r.push([!!elm, elm?.nodeName.toLowerCase(), elm?.parentNode === null]);
}
}
return r;
}
rm3.hookTags = () => {
const r = new Set();
for (const operation of operations) {
const elm = operation[0].deref();
if (elm && elm.is) {
r.add(elm.is)
}
}
return [...r];
}
DEBUG_OPT && (rm3.reuseRecord = () => {
return [...reuseRecord_]; // [[debug]]
});
DEBUG_OPT && (rm3.reuseCount_ = reuseCount_);
}
(rm3.reuseCount = 0); // window.rm3 will be zero initially if this script has no runtime complier error in the initialization phase.
})();