@@ -92,31 +92,117 @@ export function registerElement(
9292 Document . prototype ,
9393 "embeds" ,
9494 ) ;
95-
96- if ( orig && orig . get ) {
95+ if ( orig ?. get ) {
96+ const CACHE_SYM : unique symbol = Symbol (
97+ "ruffle_embeds_cache" ,
98+ ) ;
99+ interface CachedCollection extends HTMLCollection {
100+ [ CACHE_SYM ] ?: true ;
101+ }
97102 Object . defineProperty ( Document . prototype , "embeds" , {
98- get ( ) {
99- const nodes = this . querySelectorAll (
100- "embed, ruffle-embed" ,
101- ) ;
102-
103- const list = Array . from ( nodes ) as Element [ ] ;
104- ( list as unknown as HTMLCollection ) . item = (
105- i : number ,
106- ) : Element | null => list [ i ] ?? null ;
107-
108- ( list as unknown as HTMLCollection ) . namedItem =
109- ( name : string ) : Element | null =>
110- list . find ( ( el ) => {
111- const htmlEl = el as HTMLElement ;
112- return (
113- htmlEl . getAttribute ( "name" ) ===
114- name || htmlEl . id === name
115- ) ;
116- } ) ?? null ;
117-
118- return list ;
103+ get ( this : Document ) : CachedCollection {
104+ const existing = (
105+ this as unknown as Record <
106+ symbol ,
107+ HTMLCollection
108+ >
109+ ) [ CACHE_SYM ] ;
110+ if ( existing ) {
111+ return existing ;
112+ }
113+
114+ const nodes = ( ) : NodeListOf < Element > =>
115+ this . querySelectorAll (
116+ "embed, ruffle-embed" ,
117+ ) ;
118+
119+ const base = Object . create (
120+ HTMLCollection . prototype ,
121+ ) as HTMLCollection ;
122+
123+ Object . defineProperty ( base , "length" , {
124+ enumerable : true ,
125+ configurable : true ,
126+ get ( ) {
127+ return nodes ( ) . length ;
128+ } ,
129+ } ) ;
130+
131+ base . item = function (
132+ index : number ,
133+ ) : Element | null {
134+ return nodes ( ) [ index ] ?? null ;
135+ } ;
136+
137+ base . namedItem = function (
138+ name : string ,
139+ ) : Element | null {
140+ const list = nodes ( ) ;
141+ for ( const el of list ) {
142+ const htmlEl = el as HTMLElement ;
143+ if (
144+ name &&
145+ ( htmlEl . getAttribute ( "name" ) ===
146+ name ||
147+ htmlEl . id === name )
148+ ) {
149+ return htmlEl ;
150+ }
151+ }
152+ return null ;
153+ } ;
154+
155+ ( base as Iterable < Element > ) [ Symbol . iterator ] =
156+ function * ( ) : Iterator < Element > {
157+ for ( const el of nodes ( ) ) {
158+ yield el ;
159+ }
160+ } ;
161+
162+ const proxy = new Proxy ( base , {
163+ get ( target , prop , receiver ) {
164+ if ( typeof prop === "string" ) {
165+ const index = Number ( prop ) ;
166+ if (
167+ ! Number . isNaN ( index ) &&
168+ index >= 0
169+ ) {
170+ return nodes ( ) [ index ] ;
171+ }
172+ }
173+ return Reflect . get (
174+ target ,
175+ prop ,
176+ receiver ,
177+ ) ;
178+ } ,
179+ has ( target , prop ) {
180+ if ( typeof prop === "string" ) {
181+ const index = Number ( prop ) ;
182+ if (
183+ ! Number . isNaN ( index ) &&
184+ index >= 0
185+ ) {
186+ return index < nodes ( ) . length ;
187+ }
188+ }
189+ return Reflect . has ( target , prop ) ;
190+ } ,
191+ } ) as CachedCollection ;
192+
193+ proxy [ CACHE_SYM ] = true ;
194+
195+ (
196+ this as unknown as Record <
197+ symbol ,
198+ CachedCollection
199+ >
200+ ) [ CACHE_SYM ] = proxy ;
201+
202+ return proxy ;
119203 } ,
204+ configurable : true ,
205+ enumerable : true ,
120206 } ) ;
121207 }
122208 }
0 commit comments