Skip to content

Commit c3221c2

Browse files
authored
Merge pull request #8170 from QwikDev/v2-qwikloader-document-event-fix
fix(qwikloader): don't trigger document and window events for normal events
2 parents b91ce76 + 23b3dcd commit c3221c2

File tree

8 files changed

+86
-46
lines changed

8 files changed

+86
-46
lines changed

.changeset/dirty-meals-attack.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@qwik.dev/core': minor
3+
---
4+
5+
fix: don't trigger document and window events for normal events

packages/qwik/src/core/client/vnode-diff.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ import { _OWNER, _PROPS_HANDLER } from '../shared/utils/constants';
3737
import {
3838
fromCamelToKebabCase,
3939
getEventDataFromHtmlAttribute,
40+
getLoaderScopedEventName,
41+
getScopedEventName,
4042
isHtmlAttributeAnEventName,
4143
} from '../shared/utils/event-names';
4244
import { getFileLocationFromJsx } from '../shared/utils/jsx-filename';
@@ -660,16 +662,18 @@ export const vnode_diff = (
660662
if (isHtmlAttributeAnEventName(key)) {
661663
const data = getEventDataFromHtmlAttribute(key);
662664
if (data) {
663-
const scope = data[0];
664-
const eventName = data[1];
665+
const [scope, eventName] = data;
666+
const scopedEvent = getScopedEventName(scope, eventName);
667+
const loaderScopedEvent = getLoaderScopedEventName(scope, scopedEvent);
665668

666669
if (eventName) {
667-
vNewNode!.setProp(HANDLER_PREFIX + ':' + scope + ':' + eventName, value);
670+
vNewNode!.setProp(HANDLER_PREFIX + ':' + scopedEvent, value);
668671
if (scope) {
669672
// window and document need attrs so qwik loader can find them
670673
vNewNode!.setAttr(key, '', journal);
671674
}
672-
registerQwikLoaderEvent(eventName);
675+
// register an event for qwik loader (window/document prefixed with '-')
676+
registerQwikLoaderEvent(loaderScopedEvent);
673677
}
674678
}
675679

@@ -932,9 +936,11 @@ export const vnode_diff = (
932936
const data = getEventDataFromHtmlAttribute(key);
933937
if (data) {
934938
const [scope, eventName] = data;
935-
record(':' + scope + ':' + eventName, value);
936-
// register an event for qwik loader
937-
registerQwikLoaderEvent(eventName);
939+
const scopedEvent = getScopedEventName(scope, eventName);
940+
const loaderScopedEvent = getLoaderScopedEventName(scope, scopedEvent);
941+
record(':' + scopedEvent, value);
942+
// register an event for qwik loader (window/document prefixed with '-')
943+
registerQwikLoaderEvent(loaderScopedEvent);
938944
patchEventDispatch = true;
939945
}
940946
};

packages/qwik/src/core/shared/utils/event-names.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,12 @@ export const getEventDataFromHtmlAttribute = (htmlKey: string): [string, string]
109109
}
110110
return ['document', htmlKey.substring(12)];
111111
};
112+
113+
export const getScopedEventName = (scope: string, eventName: string): string => {
114+
const suffix = ':' + eventName;
115+
return scope ? scope + suffix : suffix;
116+
};
117+
118+
export const getLoaderScopedEventName = (scope: string, scopedEvent: string): string => {
119+
return scope ? '-' + scopedEvent : scopedEvent;
120+
};

packages/qwik/src/core/ssr/ssr-render-jsx.ts

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import { DEBUG_TYPE, VirtualType } from '../shared/types';
1818
import { isAsyncGenerator } from '../shared/utils/async-generator';
1919
import {
2020
getEventDataFromHtmlAttribute,
21+
getLoaderScopedEventName,
22+
getScopedEventName,
2123
isHtmlAttributeAnEventName,
2224
isPreventDefault,
2325
} from '../shared/utils/event-names';
@@ -167,13 +169,13 @@ function processJSXNode(
167169

168170
const innerHTML = ssr.openElement(
169171
type,
170-
varPropsToSsrAttrs(jsx.varProps, jsx.constProps, {
172+
toSsrAttrs(jsx.varProps, {
171173
serializationCtx: ssr.serializationCtx,
172174
styleScopedId: options.styleScoped,
173175
key: jsx.key,
174176
toSort: jsx.toSort,
175177
}),
176-
constPropsToSsrAttrs(jsx.constProps, jsx.varProps, {
178+
toSsrAttrs(jsx.constProps, {
177179
serializationCtx: ssr.serializationCtx,
178180
styleScopedId: options.styleScoped,
179181
}),
@@ -325,22 +327,6 @@ interface SsrAttrsOptions {
325327
toSort?: boolean;
326328
}
327329

328-
export function varPropsToSsrAttrs(
329-
varProps: Record<string, unknown>,
330-
constProps: Record<string, unknown> | null,
331-
options: SsrAttrsOptions
332-
): SsrAttrs | null {
333-
return toSsrAttrs(varProps, options);
334-
}
335-
336-
export function constPropsToSsrAttrs(
337-
constProps: Record<string, unknown> | null,
338-
varProps: Record<string, unknown>,
339-
options: SsrAttrsOptions
340-
): SsrAttrs | null {
341-
return toSsrAttrs(constProps, options);
342-
}
343-
344330
export function toSsrAttrs(
345331
record: Record<string, unknown> | null | undefined,
346332
options: SsrAttrsOptions
@@ -451,8 +437,10 @@ function addQwikEventToSerializationContext(
451437
// TODO extract window/document too so qwikloader can precisely listen
452438
const data = getEventDataFromHtmlAttribute(key);
453439
if (data) {
454-
const eventName = data[1];
455-
serializationCtx.$eventNames$.add(eventName);
440+
const [scope, eventName] = data;
441+
const scopedEvent = getScopedEventName(scope, eventName);
442+
const loaderScopedEvent = getLoaderScopedEventName(scope, scopedEvent);
443+
serializationCtx.$eventNames$.add(loaderScopedEvent);
456444
serializationCtx.$eventQrls$.add(qrl);
457445
}
458446
}
@@ -461,8 +449,8 @@ function addPreventDefaultEventToSerializationContext(
461449
serializationCtx: SerializationContext,
462450
key: string
463451
) {
464-
// skip first 15 chars, this is length of the `preventdefault:`
465-
const eventName = key.substring(15);
452+
// skip first 15 chars, this is length of the `preventdefault`, leave the ":"
453+
const eventName = key.substring(14);
466454
if (eventName) {
467455
serializationCtx.$eventNames$.add(eventName);
468456
}

packages/qwik/src/core/tests/container.spec.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import type { QRLInternal } from '../shared/qrl/qrl-class';
2121
import { _qrlSync } from '../shared/qrl/qrl.public';
2222
import { TypeIds } from '../shared/serdes/constants';
2323
import { hasClassAttr } from '../shared/utils/scoped-styles';
24-
import { constPropsToSsrAttrs, varPropsToSsrAttrs } from '../ssr/ssr-render-jsx';
2524
import { type SSRContainer } from '../ssr/ssr-types';
25+
import { toSsrAttrs } from '../ssr/ssr-render-jsx';
2626

2727
vi.hoisted(() => {
2828
vi.stubGlobal('QWIK_LOADER_DEFAULT_MINIFIED', 'min');
@@ -605,12 +605,12 @@ async function toHTML(jsx: JSXOutput): Promise<string> {
605605
}
606606
ssrContainer.openElement(
607607
jsx.type,
608-
varPropsToSsrAttrs(jsx.varProps as any, jsx.constProps, {
608+
toSsrAttrs(jsx.varProps, {
609609
serializationCtx: ssrContainer.serializationCtx,
610610
styleScopedId: null,
611611
key: jsx.key,
612612
}),
613-
constPropsToSsrAttrs(jsx.constProps as any, jsx.varProps, {
613+
toSsrAttrs(jsx.constProps, {
614614
serializationCtx: ssrContainer.serializationCtx,
615615
styleScopedId: null,
616616
})

packages/qwik/src/core/tests/render-api.spec.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ const ManyEventsComponent = component$(() => {
9999
onClick$={inlinedQrl(() => {}, 's_click2')}
100100
onBlur$={inlinedQrl(() => {}, 's_blur1')}
101101
on-anotherCustom$={inlinedQrl(() => {}, 's_anotherCustom1')}
102+
document:onFocus$={inlinedQrl(() => {}, 's_documentFocus1')}
103+
window:onClick$={inlinedQrl(() => {}, 's_windowClick1')}
102104
>
103105
click
104106
</button>
@@ -521,7 +523,8 @@ describe('render api', () => {
521523
qwikLoader: 'module',
522524
});
523525
expect(result.html).toContain(
524-
'(window.qwikevents||(window.qwikevents=[])).push("focus", "-my---custom", "click", "dblclick", "another-custom", "blur")'
526+
'(window.qwikevents||(window.qwikevents=[])).push(' +
527+
'":focus", ":-my---custom", ":click", ":dblclick", "-document:focus", ":another-custom", ":blur", "-window:click")'
525528
);
526529
});
527530
});

packages/qwik/src/qwikloader.ts

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ const win = window as unknown as qWindow;
2424
const events = new Set<string>();
2525
const roots = new Set<EventTarget & ParentNode>([doc]);
2626
const symbols: Record<string, unknown> = {};
27+
const windowPrefix = '-window';
28+
const documentPrefix = '-document';
2729

2830
let hasInitialized: number;
2931

@@ -104,12 +106,12 @@ const dispatch = async (
104106
}
105107
return;
106108
}
107-
const attrValue = element.getAttribute(attrName);
108109
// </DELETE ME LATER>
109110
const qDispatchEvent = (element as QElement).qDispatchEvent;
110111
if (qDispatchEvent) {
111112
return qDispatchEvent(ev, scope);
112113
}
114+
const attrValue = element.getAttribute(attrName);
113115
if (attrValue) {
114116
const container = element.closest(
115117
'[q\\:container]:not([q\\:container=html]):not([q\\:container=text])'
@@ -207,11 +209,14 @@ const camelToKebab = (str: string) => str.replace(/([A-Z-])/g, (a) => '-' + a.to
207209
*
208210
* @param ev - Browser event.
209211
*/
210-
const processDocumentEvent = async (ev: Event) => {
212+
const processDocumentEvent = async (ev: Event, scope: string) => {
211213
// eslint-disable-next-line prefer-const
212214
let type = camelToKebab(ev.type);
213215
let element = ev.target as Element | null;
214-
broadcast('-document', ev, type);
216+
if (scope === documentPrefix) {
217+
broadcast(documentPrefix, ev, type);
218+
return;
219+
}
215220

216221
while (element && element.getAttribute) {
217222
const results = dispatch(element, '', ev, type);
@@ -227,7 +232,7 @@ const processDocumentEvent = async (ev: Event) => {
227232
};
228233

229234
const processWindowEvent = (ev: Event) => {
230-
broadcast('-window', ev, camelToKebab(ev.type));
235+
broadcast(windowPrefix, ev, camelToKebab(ev.type));
231236
};
232237

233238
const processReadyStateChange = () => {
@@ -241,7 +246,7 @@ const processReadyStateChange = () => {
241246
const riC = win.requestIdleCallback ?? win.setTimeout;
242247
riC.bind(win)(() => emitEvent('qidle'));
243248

244-
if (events.has('qvisible')) {
249+
if (events.has(':qvisible')) {
245250
const results = querySelectorAll('[on\\:qvisible]');
246251
const observer = new IntersectionObserver((entries) => {
247252
for (const entry of entries) {
@@ -268,23 +273,47 @@ const addEventListener = (
268273
// Keep in sync with ./qwikloader.unit.ts
269274
const kebabToCamel = (eventName: string) => eventName.replace(/-./g, (a) => a[1].toUpperCase());
270275

276+
const processEventName = (event: string) => {
277+
const i = event.indexOf(':');
278+
let scope = '';
279+
let eventName = event;
280+
if (i >= 0) {
281+
const s = event.substring(0, i);
282+
if (s === '' || s === windowPrefix || s === documentPrefix) {
283+
scope = s;
284+
eventName = event.substring(i + 1);
285+
}
286+
}
287+
return { scope, eventName: kebabToCamel(eventName) };
288+
};
289+
271290
const processEventOrNode = (...eventNames: (string | (EventTarget & ParentNode))[]) => {
272291
for (const eventNameOrNode of eventNames) {
273292
if (typeof eventNameOrNode === 'string') {
274293
// If it is string we just add the event to window and each of our roots.
275294
if (!events.has(eventNameOrNode)) {
276295
events.add(eventNameOrNode);
277-
const eventName = kebabToCamel(eventNameOrNode);
278-
roots.forEach((root) => addEventListener(root, eventName, processDocumentEvent, true));
296+
const { scope, eventName } = processEventName(eventNameOrNode);
279297

280-
addEventListener(win, eventName, processWindowEvent, true);
298+
if (scope === windowPrefix) {
299+
addEventListener(win, eventName, processWindowEvent, true);
300+
} else {
301+
roots.forEach((root) =>
302+
addEventListener(root, eventName, (ev) => processDocumentEvent(ev, scope), true)
303+
);
304+
}
281305
}
282306
} else {
283307
// If it is a new root, we also need this root to catch up to all of the document events so far.
284308
if (!roots.has(eventNameOrNode)) {
285309
events.forEach((kebabEventName) => {
286-
const eventName = kebabToCamel(kebabEventName);
287-
addEventListener(eventNameOrNode, eventName, processDocumentEvent, true);
310+
const { scope, eventName } = processEventName(kebabEventName);
311+
addEventListener(
312+
eventNameOrNode,
313+
eventName,
314+
(ev) => processDocumentEvent(ev, scope),
315+
true
316+
);
288317
});
289318

290319
roots.add(eventNameOrNode);

packages/qwik/src/qwikloader.unit.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ test('qwikloader script', () => {
2323
const compressed = compress(Buffer.from(qwikLoader), { mode: 1, quality: 11 });
2424
expect([compressed.length, qwikLoader.length]).toMatchInlineSnapshot(`
2525
[
26-
1515,
27-
3308,
26+
1615,
27+
3555,
2828
]
2929
`);
3030

3131
expect(qwikLoader).toMatchInlineSnapshot(
32-
`"const t=document,e=window,n=new Set,o=new Set([t]),r={};let s;const i=(t,e)=>Array.from(t.querySelectorAll(e)),a=t=>{const e=[];return o.forEach(n=>e.push(...i(n,t))),e},c=t=>{w(t),i(t,"[q\\\\:shadowroot]").forEach(t=>{const e=t.shadowRoot;e&&c(e)})},l=t=>t&&"function"==typeof t.then,f=(t,e,n=e.type)=>{a("[on"+t+"\\\\:"+n+"]").forEach(o=>{b(o,t,e,n)})},p=e=>{if(void 0===e._qwikjson_){let n=(e===t.documentElement?t.body:e).lastElementChild;for(;n;){if("SCRIPT"===n.tagName&&"qwik/json"===n.getAttribute("type")){e._qwikjson_=JSON.parse(n.textContent.replace(/\\\\x3C(\\/?script)/gi,"<$1"));break}n=n.previousElementSibling}}},u=(t,e)=>new CustomEvent(t,{detail:e}),b=async(e,n,o,s=o.type)=>{const i="on"+n+":"+s;e.hasAttribute("preventdefault:"+s)&&o.preventDefault(),e.hasAttribute("stoppropagation:"+s)&&o.stopPropagation();const a=e._qc_,c=a&&a.li.filter(t=>t[0]===i);if(c&&c.length>0){for(const t of c){const n=t[1].getFn([e,o],()=>e.isConnected)(o,e),r=o.cancelBubble;l(n)&&await n,r&&o.stopPropagation()}return}const f=e.getAttribute(i),u=e.qDispatchEvent;if(u)return u(o,n);if(f){const n=e.closest("[q\\\\:container]:not([q\\\\:container=html]):not([q\\\\:container=text])"),s=n.getAttribute("q:base"),i=n.getAttribute("q:version")||"unknown",a=n.getAttribute("q:manifest-hash")||"dev",c=new URL(s,t.baseURI);for(const u of f.split("\\n")){const f=new URL(u,c),b=f.href,h=f.hash.replace(/^#?([^?[|]*).*$/,"$1")||"default",_=performance.now();let d,y,g;const m=u.startsWith("#"),w={qBase:s,qManifest:a,qVersion:i,href:b,symbol:h,element:e,reqTime:_};if(m){const e=n.getAttribute("q:instance");d=(t["qFuncs_"+e]||[])[Number.parseInt(h)],d||(y="sync",g=Error("sym:"+h))}else if(h in r)d=r[h];else{q("qsymbol",w);const t=f.href.split("#")[0];try{const e=import(t);p(n),d=(await e)[h],d?r[h]=d:(y="no-symbol",g=Error(\`\${h} not in \${t}\`))}catch(t){y||(y="async"),g=t}}if(!d){q("qerror",{importError:y,error:g,...w}),console.error(g);break}const v=t.__q_context__;if(e.isConnected)try{t.__q_context__=[e,o,f];const n=d(o,e);l(n)&&await n}catch(t){q("qerror",{error:t,...w})}finally{t.__q_context__=v}}}},q=(e,n)=>{t.dispatchEvent(u(e,n))},h=t=>t.replace(/([A-Z-])/g,t=>"-"+t.toLowerCase()),_=async t=>{let e=h(t.type),n=t.target;for(f("-document",t,e);n&&n.getAttribute;){const o=b(n,"",t,e);let r=t.cancelBubble;l(o)&&await o,r||(r=r||t.cancelBubble||n.hasAttribute("stoppropagation:"+t.type)),n=t.bubbles&&!0!==r?n.parentElement:null}},d=t=>{f("-window",t,h(t.type))},y=()=>{const r=t.readyState;if(!s&&("interactive"==r||"complete"==r)&&(o.forEach(c),s=1,q("qinit"),(e.requestIdleCallback??e.setTimeout).bind(e)(()=>q("qidle")),n.has("qvisible"))){const t=a("[on\\\\:qvisible]"),e=new IntersectionObserver(t=>{for(const n of t)n.isIntersecting&&(e.unobserve(n.target),b(n.target,"",u("qvisible",n)))});t.forEach(t=>e.observe(t))}},g=(t,e,n,o=!1)=>{t.addEventListener(e,n,{capture:o,passive:!1})},m=t=>t.replace(/-./g,t=>t[1].toUpperCase()),w=(...t)=>{for(const r of t)if("string"==typeof r){if(!n.has(r)){n.add(r);const t=m(r);o.forEach(e=>g(e,t,_,!0)),g(e,t,d,!0)}}else o.has(r)||(n.forEach(t=>{const e=m(t);g(r,e,_,!0)}),o.add(r))};if(!("__q_context__"in t)){t.__q_context__=0;const r=e.qwikevents;r&&(Array.isArray(r)?w(...r):w("click","input")),e.qwikevents={events:n,roots:o,push:w},g(t,"readystatechange",y),y()}"`
32+
`"const t=document,e=window,n=new Set,o=new Set([t]),r={},s="-window",i="-document";let a;const c=(t,e)=>Array.from(t.querySelectorAll(e)),l=t=>{const e=[];return o.forEach(n=>e.push(...c(n,t))),e},f=t=>{A(t),c(t,"[q\\\\:shadowroot]").forEach(t=>{const e=t.shadowRoot;e&&f(e)})},p=t=>t&&"function"==typeof t.then,u=(t,e,n=e.type)=>{l("[on"+t+"\\\\:"+n+"]").forEach(o=>{h(o,t,e,n)})},b=e=>{if(void 0===e._qwikjson_){let n=(e===t.documentElement?t.body:e).lastElementChild;for(;n;){if("SCRIPT"===n.tagName&&"qwik/json"===n.getAttribute("type")){e._qwikjson_=JSON.parse(n.textContent.replace(/\\\\x3C(\\/?script)/gi,"<$1"));break}n=n.previousElementSibling}}},q=(t,e)=>new CustomEvent(t,{detail:e}),h=async(e,n,o,s=o.type)=>{const i="on"+n+":"+s;e.hasAttribute("preventdefault:"+s)&&o.preventDefault(),e.hasAttribute("stoppropagation:"+s)&&o.stopPropagation();const a=e._qc_,c=a&&a.li.filter(t=>t[0]===i);if(c&&c.length>0){for(const t of c){const n=t[1].getFn([e,o],()=>e.isConnected)(o,e),r=o.cancelBubble;p(n)&&await n,r&&o.stopPropagation()}return}const l=e.qDispatchEvent;if(l)return l(o,n);const f=e.getAttribute(i);if(f){const n=e.closest("[q\\\\:container]:not([q\\\\:container=html]):not([q\\\\:container=text])"),s=n.getAttribute("q:base"),i=n.getAttribute("q:version")||"unknown",a=n.getAttribute("q:manifest-hash")||"dev",c=new URL(s,t.baseURI);for(const l of f.split("\\n")){const f=new URL(l,c),u=f.href,q=f.hash.replace(/^#?([^?[|]*).*$/,"$1")||"default",h=performance.now();let d,m,g;const y=l.startsWith("#"),v={qBase:s,qManifest:a,qVersion:i,href:u,symbol:q,element:e,reqTime:h};if(y){const e=n.getAttribute("q:instance");d=(t["qFuncs_"+e]||[])[Number.parseInt(q)],d||(m="sync",g=Error("sym:"+q))}else if(q in r)d=r[q];else{_("qsymbol",v);const t=f.href.split("#")[0];try{const e=import(t);b(n),d=(await e)[q],d?r[q]=d:(m="no-symbol",g=Error(\`\${q} not in \${t}\`))}catch(t){m||(m="async"),g=t}}if(!d){_("qerror",{importError:m,error:g,...v}),console.error(g);break}const w=t.__q_context__;if(e.isConnected)try{t.__q_context__=[e,o,f];const n=d(o,e);p(n)&&await n}catch(t){_("qerror",{error:t,...v})}finally{t.__q_context__=w}}}},_=(e,n)=>{t.dispatchEvent(q(e,n))},d=t=>t.replace(/([A-Z-])/g,t=>"-"+t.toLowerCase()),m=async(t,e)=>{let n=d(t.type),o=t.target;if(e!==i)for(;o&&o.getAttribute;){const e=h(o,"",t,n);let r=t.cancelBubble;p(e)&&await e,r||(r=r||t.cancelBubble||o.hasAttribute("stoppropagation:"+t.type)),o=t.bubbles&&!0!==r?o.parentElement:null}else u(i,t,n)},g=t=>{u(s,t,d(t.type))},y=()=>{const r=t.readyState;if(!a&&("interactive"==r||"complete"==r)&&(o.forEach(f),a=1,_("qinit"),(e.requestIdleCallback??e.setTimeout).bind(e)(()=>_("qidle")),n.has(":qvisible"))){const t=l("[on\\\\:qvisible]"),e=new IntersectionObserver(t=>{for(const n of t)n.isIntersecting&&(e.unobserve(n.target),h(n.target,"",q("qvisible",n)))});t.forEach(t=>e.observe(t))}},v=(t,e,n,o=!1)=>{t.addEventListener(e,n,{capture:o,passive:!1})},w=t=>t.replace(/-./g,t=>t[1].toUpperCase()),E=t=>{const e=t.indexOf(":");let n="",o=t;if(e>=0){const r=t.substring(0,e);""!==r&&r!==s&&r!==i||(n=r,o=t.substring(e+1))}return{scope:n,eventName:w(o)}},A=(...t)=>{for(const r of t)if("string"==typeof r){if(!n.has(r)){n.add(r);const{scope:t,eventName:i}=E(r);t===s?v(e,i,g,!0):o.forEach(e=>v(e,i,e=>m(e,t),!0))}}else o.has(r)||(n.forEach(t=>{const{scope:e,eventName:n}=E(t);v(r,n,t=>m(t,e),!0)}),o.add(r))};if(!("__q_context__"in t)){t.__q_context__=0;const r=e.qwikevents;r&&(Array.isArray(r)?A(...r):A("click","input")),e.qwikevents={events:n,roots:o,push:A},v(t,"readystatechange",y),y()}"`
3333
);
3434
});
3535

0 commit comments

Comments
 (0)