@astronox/core 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/ads/adsense/AdsenseHead.astro +55 -0
- package/src/ads/adsense/AdsenseUnit.astro +45 -0
- package/src/ads/adsense/index.ts +3 -1
- package/src/ads/adsense/provider.ts +41 -0
- package/src/ads/adsense/runtime.ts +284 -0
- package/src/ads/contracts.ts +67 -5
- package/src/components/content/CardHeader/CardHeader.astro +20 -0
- package/src/components/content/CardHeader/CardHeader.css +21 -0
- package/src/components/feedback/Tooltip/tooltipController.ts +37 -5
- package/src/components/monetization/AdSlot/AdSlot.astro +0 -3
- package/src/components/monetization/AdSlot/AdSlot.css +5 -28
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.astro +2 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.css +12 -6
- package/src/components/navigation/Footer/Footer.astro +8 -0
- package/src/components/navigation/Footer/Footer.css +20 -6
- package/src/components/navigation/Footer/contracts.ts +1 -0
- package/src/composition/Grid/Grid.css +1 -1
- package/src/composition/SurfaceGroup/SurfaceGroup.astro +2 -1
- package/src/composition/SurfaceGroup/SurfaceGroup.css +18 -18
- package/src/composition/SurfaceGroup/stickySurfaceGroupController.ts +9 -0
- package/src/config/environment.ts +66 -2
- package/src/layouts/BaseLayout.astro +6 -1
- package/src/patterns/Combobox/comboboxController.ts +51 -12
- package/src/patterns/Dropdown/dropdownController.ts +17 -6
- package/src/patterns/Fullscreen/FullscreenControl.astro +3 -30
- package/src/patterns/Fullscreen/fullscreenController.ts +18 -22
- package/src/patterns/Table/Table.css +8 -2
- package/src/patterns/Table/TableFilterPanel.astro +84 -0
- package/src/patterns/Table/TableFilterPanel.css +139 -0
- package/src/patterns/Table/TableSkeleton.css +0 -3
- package/src/patterns/Table/tableFilterPanelController.ts +91 -0
- package/src/patterns/Table/tableProgressiveController.ts +78 -16
- package/src/patterns/Table/tableScrollPosition.ts +41 -0
- package/src/patterns/Table/tableSortController.ts +6 -3
- package/src/patterns/index.ts +2 -0
- package/src/styles/style.css +1 -0
- package/src/ads/adsense.ts +0 -112
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
const initializedAttribute = 'data-table-filter-panel-initialized';
|
|
2
|
+
const compactAttribute = 'data-table-filter-panel-compact';
|
|
3
|
+
const openAttribute = 'data-table-filter-panel-open';
|
|
4
|
+
const noop = (): void => undefined;
|
|
5
|
+
|
|
6
|
+
const collapseWidths = {
|
|
7
|
+
sm: 24,
|
|
8
|
+
md: 36,
|
|
9
|
+
lg: 44,
|
|
10
|
+
xl: 54,
|
|
11
|
+
} as const;
|
|
12
|
+
|
|
13
|
+
export function initTableFilterPanel(root: HTMLElement): () => void {
|
|
14
|
+
if (root.hasAttribute(initializedAttribute)) return noop;
|
|
15
|
+
|
|
16
|
+
const trigger = root.querySelector<HTMLButtonElement>(
|
|
17
|
+
':scope > [data-widget="card-header"] [data-card-header-trigger]',
|
|
18
|
+
);
|
|
19
|
+
const content = root.querySelector<HTMLElement>(
|
|
20
|
+
':scope > [data-table-filter-panel-content]',
|
|
21
|
+
);
|
|
22
|
+
if (!trigger || !content) return noop;
|
|
23
|
+
|
|
24
|
+
const collapseAt = root.dataset.tableFilterPanelCollapseAt;
|
|
25
|
+
const collapseWidth =
|
|
26
|
+
collapseAt && collapseAt in collapseWidths
|
|
27
|
+
? collapseWidths[collapseAt as keyof typeof collapseWidths]
|
|
28
|
+
: collapseWidths.lg;
|
|
29
|
+
const boundary =
|
|
30
|
+
root.closest<HTMLElement>('.grid-container') ?? root.parentElement ?? root;
|
|
31
|
+
const grid = boundary.querySelector<HTMLElement>(
|
|
32
|
+
':scope > [data-composition="grid"]',
|
|
33
|
+
);
|
|
34
|
+
let compact = false;
|
|
35
|
+
let open = true;
|
|
36
|
+
|
|
37
|
+
const sync = (): void => {
|
|
38
|
+
root.toggleAttribute(compactAttribute, compact);
|
|
39
|
+
root.toggleAttribute(openAttribute, open);
|
|
40
|
+
trigger.hidden = !compact;
|
|
41
|
+
trigger.tabIndex = compact ? 0 : -1;
|
|
42
|
+
trigger.setAttribute('aria-expanded', String(open));
|
|
43
|
+
content.hidden = !open;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const updateMode = (): void => {
|
|
47
|
+
const rootFontSize = Number.parseFloat(
|
|
48
|
+
window.getComputedStyle(document.documentElement).fontSize,
|
|
49
|
+
);
|
|
50
|
+
const renderedColumns = grid
|
|
51
|
+
? window
|
|
52
|
+
.getComputedStyle(grid)
|
|
53
|
+
.gridTemplateColumns.split(/\s+/)
|
|
54
|
+
.filter(Boolean).length
|
|
55
|
+
: 0;
|
|
56
|
+
const nextCompact = grid
|
|
57
|
+
? renderedColumns === 1
|
|
58
|
+
: boundary.getBoundingClientRect().width < collapseWidth * rootFontSize;
|
|
59
|
+
if (nextCompact === compact) return;
|
|
60
|
+
|
|
61
|
+
compact = nextCompact;
|
|
62
|
+
open = !compact;
|
|
63
|
+
sync();
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const handleClick = (): void => {
|
|
67
|
+
if (!compact) return;
|
|
68
|
+
open = !open;
|
|
69
|
+
sync();
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
root.setAttribute(initializedAttribute, '');
|
|
73
|
+
trigger.addEventListener('click', handleClick);
|
|
74
|
+
const resizeObserver =
|
|
75
|
+
'ResizeObserver' in window ? new ResizeObserver(updateMode) : null;
|
|
76
|
+
resizeObserver?.observe(boundary);
|
|
77
|
+
updateMode();
|
|
78
|
+
sync();
|
|
79
|
+
|
|
80
|
+
return () => {
|
|
81
|
+
resizeObserver?.disconnect();
|
|
82
|
+
trigger.removeEventListener('click', handleClick);
|
|
83
|
+
trigger.hidden = true;
|
|
84
|
+
trigger.tabIndex = -1;
|
|
85
|
+
trigger.setAttribute('aria-expanded', 'true');
|
|
86
|
+
content.hidden = false;
|
|
87
|
+
root.removeAttribute(initializedAttribute);
|
|
88
|
+
root.removeAttribute(compactAttribute);
|
|
89
|
+
root.removeAttribute(openAttribute);
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
TableProgressiveModel,
|
|
4
4
|
} from '../../display/contracts';
|
|
5
5
|
import { tableProgressiveBatchSize } from '../../display/tableProgressive';
|
|
6
|
+
import { mutateTablePreservingScroll } from './tableScrollPosition';
|
|
6
7
|
|
|
7
8
|
const initializedAttribute = 'data-table-progressive-initialized';
|
|
8
9
|
|
|
@@ -151,6 +152,8 @@ export function initTableProgressive(root: HTMLElement): () => void {
|
|
|
151
152
|
let disposed = false;
|
|
152
153
|
let generation = 0;
|
|
153
154
|
let prepared = false;
|
|
155
|
+
let retainedWindowSize = Math.max(batchSize, getRows(activeRowsRoot).length);
|
|
156
|
+
let scheduledRenderFrame: number | null = null;
|
|
154
157
|
|
|
155
158
|
registerRows(getRows(activeRowsRoot));
|
|
156
159
|
|
|
@@ -181,8 +184,11 @@ export function initTableProgressive(root: HTMLElement): () => void {
|
|
|
181
184
|
activeSkeletonTemplate.content.querySelectorAll<HTMLElement>(
|
|
182
185
|
'[data-table-skeleton-row]',
|
|
183
186
|
),
|
|
184
|
-
)
|
|
185
|
-
|
|
187
|
+
);
|
|
188
|
+
if (candidates.length === 0) return;
|
|
189
|
+
skeletonRows = Array.from({ length: count }, (_, index) =>
|
|
190
|
+
document.importNode(candidates[index % candidates.length]!, true),
|
|
191
|
+
);
|
|
186
192
|
insertBeforeEmpty(activeRowsRoot, empty, skeletonRows);
|
|
187
193
|
}
|
|
188
194
|
|
|
@@ -204,6 +210,21 @@ export function initTableProgressive(root: HTMLElement): () => void {
|
|
|
204
210
|
);
|
|
205
211
|
}
|
|
206
212
|
|
|
213
|
+
function replaceRows(rows: readonly HTMLElement[]): void {
|
|
214
|
+
for (const row of rows) row.hidden = false;
|
|
215
|
+
mutateTablePreservingScroll(root, () => {
|
|
216
|
+
removeSkeleton();
|
|
217
|
+
detachRenderedRows();
|
|
218
|
+
insertBeforeEmpty(activeRowsRoot, empty, rows);
|
|
219
|
+
});
|
|
220
|
+
root.dispatchEvent(
|
|
221
|
+
new CustomEvent<TableRowsAppendedDetail>('table:rows-appended', {
|
|
222
|
+
bubbles: true,
|
|
223
|
+
detail: { rows },
|
|
224
|
+
}),
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
207
228
|
function updateEmptyState(): void {
|
|
208
229
|
if (empty) empty.hidden = results.length > 0;
|
|
209
230
|
}
|
|
@@ -286,10 +307,12 @@ export function initTableProgressive(root: HTMLElement): () => void {
|
|
|
286
307
|
TableLoadingState,
|
|
287
308
|
'pending' | 'refreshing' | 'appending'
|
|
288
309
|
> = 'appending',
|
|
310
|
+
requestedCount = batchSize,
|
|
311
|
+
replace = false,
|
|
289
312
|
): Promise<void> {
|
|
290
313
|
if (request || disposed || cursor >= results.length) return;
|
|
291
314
|
|
|
292
|
-
const target = results.slice(cursor, cursor +
|
|
315
|
+
const target = results.slice(cursor, cursor + requestedCount);
|
|
293
316
|
if (target.length === 0) {
|
|
294
317
|
removeSkeleton();
|
|
295
318
|
updatePagination();
|
|
@@ -305,7 +328,9 @@ export function initTableProgressive(root: HTMLElement): () => void {
|
|
|
305
328
|
request = activeRequest;
|
|
306
329
|
setPaginationState(activePagination, activeStatus, 'loading');
|
|
307
330
|
updateTableState(loadingState);
|
|
308
|
-
|
|
331
|
+
if (loadingState === 'appending' || skeletonRows.length === 0) {
|
|
332
|
+
addSkeleton(target.length);
|
|
333
|
+
}
|
|
309
334
|
|
|
310
335
|
try {
|
|
311
336
|
await loadSources(missing, activeRequest.signal);
|
|
@@ -336,9 +361,16 @@ export function initTableProgressive(root: HTMLElement): () => void {
|
|
|
336
361
|
return;
|
|
337
362
|
}
|
|
338
363
|
|
|
339
|
-
|
|
340
|
-
|
|
364
|
+
if (replace) replaceRows(rows as HTMLElement[]);
|
|
365
|
+
else {
|
|
366
|
+
removeSkeleton();
|
|
367
|
+
appendRows(rows as HTMLElement[]);
|
|
368
|
+
}
|
|
341
369
|
cursor += target.length;
|
|
370
|
+
retainedWindowSize = Math.max(
|
|
371
|
+
retainedWindowSize,
|
|
372
|
+
getRows(activeRowsRoot).length,
|
|
373
|
+
);
|
|
342
374
|
updatePagination();
|
|
343
375
|
updateTableState('ready');
|
|
344
376
|
}
|
|
@@ -403,22 +435,48 @@ export function initTableProgressive(root: HTMLElement): () => void {
|
|
|
403
435
|
offset: number,
|
|
404
436
|
state: Extract<TableLoadingState, 'pending' | 'refreshing'>,
|
|
405
437
|
): void {
|
|
438
|
+
retainedWindowSize = Math.max(
|
|
439
|
+
retainedWindowSize,
|
|
440
|
+
getRows(activeRowsRoot).length,
|
|
441
|
+
);
|
|
406
442
|
generation += 1;
|
|
407
443
|
request?.abort();
|
|
408
444
|
request = null;
|
|
445
|
+
if (scheduledRenderFrame !== null) {
|
|
446
|
+
window.cancelAnimationFrame(scheduledRenderFrame);
|
|
447
|
+
scheduledRenderFrame = null;
|
|
448
|
+
}
|
|
449
|
+
observer.disconnect();
|
|
409
450
|
updateTableState(state);
|
|
410
|
-
|
|
411
|
-
|
|
451
|
+
setPaginationState(activePagination, activeStatus, 'loading');
|
|
452
|
+
if (empty) empty.hidden = true;
|
|
453
|
+
mutateTablePreservingScroll(root, () => {
|
|
454
|
+
detachRenderedRows();
|
|
455
|
+
addSkeleton(retainedWindowSize);
|
|
456
|
+
});
|
|
457
|
+
|
|
412
458
|
applyQuery();
|
|
413
459
|
cursor = Math.min(offset, results.length);
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
460
|
+
const activeGeneration = generation;
|
|
461
|
+
scheduledRenderFrame = window.requestAnimationFrame(() => {
|
|
462
|
+
scheduledRenderFrame = window.requestAnimationFrame(() => {
|
|
463
|
+
scheduledRenderFrame = null;
|
|
464
|
+
if (disposed || generation !== activeGeneration) return;
|
|
465
|
+
|
|
466
|
+
updateEmptyState();
|
|
467
|
+
if (results.length === 0) {
|
|
468
|
+
replaceRows([]);
|
|
469
|
+
updatePagination();
|
|
470
|
+
updateTableState('empty');
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
void renderNextBatch(
|
|
474
|
+
state,
|
|
475
|
+
Math.min(retainedWindowSize, results.length - cursor),
|
|
476
|
+
true,
|
|
477
|
+
);
|
|
478
|
+
});
|
|
479
|
+
});
|
|
422
480
|
}
|
|
423
481
|
|
|
424
482
|
function refresh(): void {
|
|
@@ -434,6 +492,10 @@ export function initTableProgressive(root: HTMLElement): () => void {
|
|
|
434
492
|
destroy() {
|
|
435
493
|
disposed = true;
|
|
436
494
|
request?.abort();
|
|
495
|
+
if (scheduledRenderFrame !== null) {
|
|
496
|
+
window.cancelAnimationFrame(scheduledRenderFrame);
|
|
497
|
+
scheduledRenderFrame = null;
|
|
498
|
+
}
|
|
437
499
|
observer.disconnect();
|
|
438
500
|
removeSkeleton();
|
|
439
501
|
activePagination.hidden = !fallbackLink;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
type ScrollRestoreState = {
|
|
2
|
+
frame: number;
|
|
3
|
+
originalOverflowAnchor: string;
|
|
4
|
+
scrollRoot: HTMLElement;
|
|
5
|
+
scrollTop: number;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const restoreStates = new WeakMap<HTMLElement, ScrollRestoreState>();
|
|
9
|
+
|
|
10
|
+
export function mutateTablePreservingScroll(
|
|
11
|
+
root: HTMLElement,
|
|
12
|
+
mutation: () => void,
|
|
13
|
+
): void {
|
|
14
|
+
const scrollRoot = root.closest<HTMLElement>('[data-site-viewport]');
|
|
15
|
+
if (!scrollRoot) {
|
|
16
|
+
mutation();
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const previous = restoreStates.get(root);
|
|
21
|
+
if (previous) window.cancelAnimationFrame(previous.frame);
|
|
22
|
+
const originalOverflowAnchor =
|
|
23
|
+
previous?.originalOverflowAnchor ?? root.style.overflowAnchor;
|
|
24
|
+
const scrollTop = scrollRoot.scrollTop;
|
|
25
|
+
|
|
26
|
+
root.style.overflowAnchor = 'none';
|
|
27
|
+
mutation();
|
|
28
|
+
scrollRoot.scrollTop = scrollTop;
|
|
29
|
+
|
|
30
|
+
const state: ScrollRestoreState = {
|
|
31
|
+
frame: window.requestAnimationFrame(() => {
|
|
32
|
+
scrollRoot.scrollTop = scrollTop;
|
|
33
|
+
root.style.overflowAnchor = originalOverflowAnchor;
|
|
34
|
+
restoreStates.delete(root);
|
|
35
|
+
}),
|
|
36
|
+
originalOverflowAnchor,
|
|
37
|
+
scrollRoot,
|
|
38
|
+
scrollTop,
|
|
39
|
+
};
|
|
40
|
+
restoreStates.set(root, state);
|
|
41
|
+
}
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
setTableProgressiveSort,
|
|
5
5
|
updateTableProgressiveEntry,
|
|
6
6
|
} from './tableProgressiveController';
|
|
7
|
+
import { mutateTablePreservingScroll } from './tableScrollPosition';
|
|
7
8
|
|
|
8
9
|
type SortDirection = 'ascending' | 'descending';
|
|
9
10
|
type SortType = 'number' | 'text';
|
|
@@ -96,9 +97,11 @@ export function initTableSort(root: HTMLElement): () => void {
|
|
|
96
97
|
orderDirty = activeState !== null;
|
|
97
98
|
if (rows.every((row, index) => row === sortedRows[index])) return;
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
mutateTablePreservingScroll(root, () => {
|
|
101
|
+
const fragment = document.createDocumentFragment();
|
|
102
|
+
for (const row of sortedRows) fragment.append(row);
|
|
103
|
+
rowsRoot.prepend(fragment);
|
|
104
|
+
});
|
|
102
105
|
};
|
|
103
106
|
|
|
104
107
|
const scheduleSort = (): void => {
|
package/src/patterns/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ export { default as StageFlow } from './StageFlow/StageFlow.astro';
|
|
|
15
15
|
export { default as Table } from './Table/Table.astro';
|
|
16
16
|
export { default as TableBody } from './Table/TableBody.astro';
|
|
17
17
|
export { default as TableFooter } from './Table/TableFooter.astro';
|
|
18
|
+
export { default as TableFilterPanel } from './Table/TableFilterPanel.astro';
|
|
18
19
|
export { default as TableHeader } from './Table/TableHeader.astro';
|
|
19
20
|
export { default as TablePagination } from './Table/TablePagination.astro';
|
|
20
21
|
export { default as TableSkeleton } from './Table/TableSkeleton.astro';
|
|
@@ -41,6 +42,7 @@ export * from './SpriteProjectilePreview/contracts';
|
|
|
41
42
|
export * from './SpriteProjectilePreview/spriteProjectilePreviewController';
|
|
42
43
|
export * from './Table/tableCellController';
|
|
43
44
|
export * from './Table/tableFilterController';
|
|
45
|
+
export * from './Table/tableFilterPanelController';
|
|
44
46
|
export * from './Table/tableLayout';
|
|
45
47
|
export * from './Table/tableProgressiveController';
|
|
46
48
|
export * from './Table/tableSortController';
|
package/src/styles/style.css
CHANGED
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
@import '../patterns/Table/TableBody.css' layer(patterns);
|
|
84
84
|
@import '../patterns/Table/TableCell.css' layer(patterns);
|
|
85
85
|
@import '../patterns/Table/TableFooter.css' layer(patterns);
|
|
86
|
+
@import '../patterns/Table/TableFilterPanel.css' layer(patterns);
|
|
86
87
|
@import '../patterns/Table/TableHeader.css' layer(patterns);
|
|
87
88
|
@import '../patterns/Table/TablePagination.css' layer(patterns);
|
|
88
89
|
@import '../patterns/Table/TableSkeleton.css' layer(patterns);
|
package/src/ads/adsense.ts
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import type { AdConsentProvider, AdProvider } from './contracts';
|
|
2
|
-
import { setAdSlotState } from './slot';
|
|
3
|
-
|
|
4
|
-
declare global {
|
|
5
|
-
interface Window {
|
|
6
|
-
adsbygoogle?: Record<string, unknown>[];
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function createAdsenseProvider({
|
|
11
|
-
clientId,
|
|
12
|
-
consent,
|
|
13
|
-
loadTimeoutMs,
|
|
14
|
-
}: {
|
|
15
|
-
clientId: string;
|
|
16
|
-
consent: AdConsentProvider;
|
|
17
|
-
loadTimeoutMs: number;
|
|
18
|
-
}): AdProvider {
|
|
19
|
-
let loading: Promise<void> | null = null;
|
|
20
|
-
|
|
21
|
-
const load = async (): Promise<void> => {
|
|
22
|
-
if (!(await consent.canLoadAds())) {
|
|
23
|
-
throw new Error('Advertising consent was not granted');
|
|
24
|
-
}
|
|
25
|
-
loading ??= loadAdsenseScript(clientId, loadTimeoutMs).catch((reason) => {
|
|
26
|
-
loading = null;
|
|
27
|
-
throw reason;
|
|
28
|
-
});
|
|
29
|
-
return loading;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
load,
|
|
34
|
-
async request(slot) {
|
|
35
|
-
setAdSlotState(slot, 'loading');
|
|
36
|
-
try {
|
|
37
|
-
await load();
|
|
38
|
-
window.adsbygoogle ??= [];
|
|
39
|
-
window.adsbygoogle.push({});
|
|
40
|
-
observeAdsenseSlot(slot);
|
|
41
|
-
} catch (reason) {
|
|
42
|
-
setAdSlotState(slot, 'blocked');
|
|
43
|
-
throw reason;
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function loadAdsenseScript(
|
|
50
|
-
clientId: string,
|
|
51
|
-
loadTimeoutMs: number,
|
|
52
|
-
): Promise<void> {
|
|
53
|
-
const existing = document.querySelector<HTMLScriptElement>(
|
|
54
|
-
'script[data-astronox-adsense]',
|
|
55
|
-
);
|
|
56
|
-
if (existing?.dataset.loaded === 'true') return Promise.resolve();
|
|
57
|
-
|
|
58
|
-
return new Promise((resolve, reject) => {
|
|
59
|
-
const script = existing ?? document.createElement('script');
|
|
60
|
-
script.async = true;
|
|
61
|
-
script.crossOrigin = 'anonymous';
|
|
62
|
-
script.dataset.astronoxAdsense = '';
|
|
63
|
-
script.src = `https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${encodeURIComponent(clientId)}`;
|
|
64
|
-
const timeout = window.setTimeout(
|
|
65
|
-
() => reject(new Error('AdSense loading timed out')),
|
|
66
|
-
loadTimeoutMs,
|
|
67
|
-
);
|
|
68
|
-
script.addEventListener(
|
|
69
|
-
'load',
|
|
70
|
-
() => {
|
|
71
|
-
window.clearTimeout(timeout);
|
|
72
|
-
script.dataset.loaded = 'true';
|
|
73
|
-
resolve();
|
|
74
|
-
},
|
|
75
|
-
{ once: true },
|
|
76
|
-
);
|
|
77
|
-
script.addEventListener(
|
|
78
|
-
'error',
|
|
79
|
-
() => {
|
|
80
|
-
window.clearTimeout(timeout);
|
|
81
|
-
reject(new Error('AdSense failed to load'));
|
|
82
|
-
},
|
|
83
|
-
{ once: true },
|
|
84
|
-
);
|
|
85
|
-
if (!existing) document.head.append(script);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function observeAdsenseSlot(slot: HTMLElement): void {
|
|
90
|
-
const advertisement = slot.querySelector<HTMLElement>('.adsbygoogle');
|
|
91
|
-
if (!advertisement) {
|
|
92
|
-
setAdSlotState(slot, 'unavailable');
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const sync = (): void => {
|
|
97
|
-
const status = advertisement.dataset.adStatus;
|
|
98
|
-
if (status === 'filled') setAdSlotState(slot, 'filled');
|
|
99
|
-
if (status === 'unfilled') setAdSlotState(slot, 'unavailable');
|
|
100
|
-
};
|
|
101
|
-
sync();
|
|
102
|
-
if (advertisement.dataset.adStatus) return;
|
|
103
|
-
|
|
104
|
-
const observer = new MutationObserver(() => {
|
|
105
|
-
sync();
|
|
106
|
-
if (advertisement.dataset.adStatus) observer.disconnect();
|
|
107
|
-
});
|
|
108
|
-
observer.observe(advertisement, {
|
|
109
|
-
attributeFilter: ['data-ad-status'],
|
|
110
|
-
attributes: true,
|
|
111
|
-
});
|
|
112
|
-
}
|