@sc4rfurryx/proteusjs 1.0.0
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/API.md +438 -0
- package/FEATURES.md +286 -0
- package/LICENSE +21 -0
- package/README.md +645 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/proteus.cjs.js +16014 -0
- package/dist/proteus.cjs.js.map +1 -0
- package/dist/proteus.d.ts +3018 -0
- package/dist/proteus.esm.js +16005 -0
- package/dist/proteus.esm.js.map +1 -0
- package/dist/proteus.esm.min.js +8 -0
- package/dist/proteus.esm.min.js.map +1 -0
- package/dist/proteus.js +16020 -0
- package/dist/proteus.js.map +1 -0
- package/dist/proteus.min.js +8 -0
- package/dist/proteus.min.js.map +1 -0
- package/package.json +98 -0
- package/src/__tests__/mvp-integration.test.ts +518 -0
- package/src/accessibility/AccessibilityEngine.ts +2106 -0
- package/src/accessibility/ScreenReaderSupport.ts +444 -0
- package/src/accessibility/__tests__/ScreenReaderSupport.test.ts +435 -0
- package/src/animations/FLIPAnimationSystem.ts +491 -0
- package/src/compatibility/BrowserCompatibility.ts +1076 -0
- package/src/containers/BreakpointSystem.ts +347 -0
- package/src/containers/ContainerBreakpoints.ts +726 -0
- package/src/containers/ContainerManager.ts +370 -0
- package/src/containers/ContainerUnits.ts +336 -0
- package/src/containers/ContextIsolation.ts +394 -0
- package/src/containers/ElementQueries.ts +411 -0
- package/src/containers/SmartContainer.ts +536 -0
- package/src/containers/SmartContainers.ts +376 -0
- package/src/containers/__tests__/ContainerBreakpoints.test.ts +411 -0
- package/src/containers/__tests__/SmartContainers.test.ts +281 -0
- package/src/content/ResponsiveImages.ts +570 -0
- package/src/core/EventSystem.ts +147 -0
- package/src/core/MemoryManager.ts +321 -0
- package/src/core/PerformanceMonitor.ts +238 -0
- package/src/core/PluginSystem.ts +275 -0
- package/src/core/ProteusJS.test.ts +164 -0
- package/src/core/ProteusJS.ts +962 -0
- package/src/developer/PerformanceProfiler.ts +567 -0
- package/src/developer/VisualDebuggingTools.ts +656 -0
- package/src/developer/ZeroConfigSystem.ts +593 -0
- package/src/index.ts +35 -0
- package/src/integration.test.ts +227 -0
- package/src/layout/AdaptiveGrid.ts +429 -0
- package/src/layout/ContentReordering.ts +532 -0
- package/src/layout/FlexboxEnhancer.ts +406 -0
- package/src/layout/FlowLayout.ts +545 -0
- package/src/layout/SpacingSystem.ts +512 -0
- package/src/observers/IntersectionObserverPolyfill.ts +289 -0
- package/src/observers/ObserverManager.ts +299 -0
- package/src/observers/ResizeObserverPolyfill.ts +179 -0
- package/src/performance/BatchDOMOperations.ts +519 -0
- package/src/performance/CSSOptimizationEngine.ts +646 -0
- package/src/performance/CacheOptimizationSystem.ts +601 -0
- package/src/performance/EfficientEventHandler.ts +740 -0
- package/src/performance/LazyEvaluationSystem.ts +532 -0
- package/src/performance/MemoryManagementSystem.ts +497 -0
- package/src/performance/PerformanceMonitor.ts +931 -0
- package/src/performance/__tests__/BatchDOMOperations.test.ts +309 -0
- package/src/performance/__tests__/EfficientEventHandler.test.ts +268 -0
- package/src/performance/__tests__/PerformanceMonitor.test.ts +422 -0
- package/src/polyfills/BrowserPolyfills.ts +586 -0
- package/src/polyfills/__tests__/BrowserPolyfills.test.ts +328 -0
- package/src/test/setup.ts +115 -0
- package/src/theming/SmartThemeSystem.ts +591 -0
- package/src/types/index.ts +134 -0
- package/src/typography/ClampScaling.ts +356 -0
- package/src/typography/FluidTypography.ts +759 -0
- package/src/typography/LineHeightOptimization.ts +430 -0
- package/src/typography/LineHeightOptimizer.ts +326 -0
- package/src/typography/TextFitting.ts +355 -0
- package/src/typography/TypographicScale.ts +428 -0
- package/src/typography/VerticalRhythm.ts +369 -0
- package/src/typography/__tests__/FluidTypography.test.ts +432 -0
- package/src/typography/__tests__/LineHeightOptimization.test.ts +436 -0
- package/src/utils/Logger.ts +173 -0
- package/src/utils/debounce.ts +259 -0
- package/src/utils/performance.ts +371 -0
- package/src/utils/support.ts +106 -0
- package/src/utils/version.ts +24 -0
@@ -0,0 +1,1076 @@
|
|
1
|
+
/**
|
2
|
+
* Comprehensive Browser Compatibility System for ProteusJS
|
3
|
+
* Feature detection, polyfills, and graceful degradation
|
4
|
+
*/
|
5
|
+
|
6
|
+
import { logger } from '../utils/Logger';
|
7
|
+
|
8
|
+
export interface BrowserInfo {
|
9
|
+
name: string;
|
10
|
+
version: string;
|
11
|
+
engine: string;
|
12
|
+
platform: string;
|
13
|
+
mobile: boolean;
|
14
|
+
supported: boolean;
|
15
|
+
}
|
16
|
+
|
17
|
+
export interface FeatureSupport {
|
18
|
+
containerQueries: boolean;
|
19
|
+
clampFunction: boolean;
|
20
|
+
customProperties: boolean;
|
21
|
+
flexbox: boolean;
|
22
|
+
grid: boolean;
|
23
|
+
intersectionObserver: boolean;
|
24
|
+
resizeObserver: boolean;
|
25
|
+
mutationObserver: boolean;
|
26
|
+
requestAnimationFrame: boolean;
|
27
|
+
webAnimations: boolean;
|
28
|
+
prefersReducedMotion: boolean;
|
29
|
+
prefersColorScheme: boolean;
|
30
|
+
viewportUnits: boolean;
|
31
|
+
calc: boolean;
|
32
|
+
transforms: boolean;
|
33
|
+
transitions: boolean;
|
34
|
+
animations: boolean;
|
35
|
+
webFonts: boolean;
|
36
|
+
fontDisplay: boolean;
|
37
|
+
fontVariationSettings: boolean;
|
38
|
+
}
|
39
|
+
|
40
|
+
export interface CompatibilityConfig {
|
41
|
+
enablePolyfills: boolean;
|
42
|
+
gracefulDegradation: boolean;
|
43
|
+
fallbackStrategies: boolean;
|
44
|
+
performanceOptimizations: boolean;
|
45
|
+
legacySupport: boolean;
|
46
|
+
modernFeatures: boolean;
|
47
|
+
autoDetection: boolean;
|
48
|
+
}
|
49
|
+
|
50
|
+
export interface PolyfillInfo {
|
51
|
+
name: string;
|
52
|
+
required: boolean;
|
53
|
+
loaded: boolean;
|
54
|
+
size: number;
|
55
|
+
url?: string;
|
56
|
+
fallback?: () => void;
|
57
|
+
}
|
58
|
+
|
59
|
+
export class BrowserCompatibility {
|
60
|
+
private browserInfo: BrowserInfo;
|
61
|
+
private featureSupport: FeatureSupport;
|
62
|
+
private config: Required<CompatibilityConfig>;
|
63
|
+
private polyfills: Map<string, PolyfillInfo> = new Map();
|
64
|
+
private fallbacks: Map<string, () => void> = new Map();
|
65
|
+
private modernFeatures: Set<string> = new Set();
|
66
|
+
private legacyFeatures: Set<string> = new Set();
|
67
|
+
|
68
|
+
constructor(config: Partial<CompatibilityConfig> = {}) {
|
69
|
+
this.config = {
|
70
|
+
enablePolyfills: true,
|
71
|
+
gracefulDegradation: true,
|
72
|
+
fallbackStrategies: true,
|
73
|
+
performanceOptimizations: true,
|
74
|
+
legacySupport: true,
|
75
|
+
modernFeatures: true,
|
76
|
+
autoDetection: true,
|
77
|
+
...config
|
78
|
+
};
|
79
|
+
|
80
|
+
this.browserInfo = this.detectBrowser();
|
81
|
+
this.featureSupport = this.detectFeatures();
|
82
|
+
|
83
|
+
if (this.config.autoDetection) {
|
84
|
+
this.initializeCompatibility();
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
/**
|
89
|
+
* Initialize compatibility system
|
90
|
+
*/
|
91
|
+
public async initializeCompatibility(): Promise<void> {
|
92
|
+
logger.info('Initializing browser compatibility system');
|
93
|
+
|
94
|
+
// Apply browser-specific fixes
|
95
|
+
this.applyBrowserFixes();
|
96
|
+
|
97
|
+
// Load required polyfills
|
98
|
+
if (this.config.enablePolyfills) {
|
99
|
+
await this.loadPolyfills();
|
100
|
+
}
|
101
|
+
|
102
|
+
// Setup fallback strategies
|
103
|
+
if (this.config.fallbackStrategies) {
|
104
|
+
this.setupFallbacks();
|
105
|
+
}
|
106
|
+
|
107
|
+
// Apply performance optimizations
|
108
|
+
if (this.config.performanceOptimizations) {
|
109
|
+
this.applyPerformanceOptimizations();
|
110
|
+
}
|
111
|
+
|
112
|
+
// Setup modern feature detection
|
113
|
+
if (this.config.modernFeatures) {
|
114
|
+
this.setupModernFeatures();
|
115
|
+
}
|
116
|
+
|
117
|
+
logger.info('Browser compatibility system initialized', {
|
118
|
+
browser: this.browserInfo,
|
119
|
+
features: this.featureSupport
|
120
|
+
});
|
121
|
+
}
|
122
|
+
|
123
|
+
/**
|
124
|
+
* Detect browser information
|
125
|
+
*/
|
126
|
+
private detectBrowser(): BrowserInfo {
|
127
|
+
const userAgent = navigator.userAgent;
|
128
|
+
const platform = navigator.platform;
|
129
|
+
|
130
|
+
let name = 'Unknown';
|
131
|
+
let version = '0';
|
132
|
+
let engine = 'Unknown';
|
133
|
+
|
134
|
+
// Chrome
|
135
|
+
if (userAgent.includes('Chrome') && !userAgent.includes('Edg')) {
|
136
|
+
name = 'Chrome';
|
137
|
+
const match = userAgent.match(/Chrome\/(\d+)/);
|
138
|
+
version = match?.[1] || '0';
|
139
|
+
engine = 'Blink';
|
140
|
+
}
|
141
|
+
// Firefox
|
142
|
+
else if (userAgent.includes('Firefox')) {
|
143
|
+
name = 'Firefox';
|
144
|
+
const match = userAgent.match(/Firefox\/(\d+)/);
|
145
|
+
version = match?.[1] || '0';
|
146
|
+
engine = 'Gecko';
|
147
|
+
}
|
148
|
+
// Safari
|
149
|
+
else if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {
|
150
|
+
name = 'Safari';
|
151
|
+
const match = userAgent.match(/Version\/(\d+)/);
|
152
|
+
version = match?.[1] || '0';
|
153
|
+
engine = 'WebKit';
|
154
|
+
}
|
155
|
+
// Edge
|
156
|
+
else if (userAgent.includes('Edg')) {
|
157
|
+
name = 'Edge';
|
158
|
+
const match = userAgent.match(/Edg\/(\d+)/);
|
159
|
+
version = match?.[1] || '0';
|
160
|
+
engine = 'Blink';
|
161
|
+
}
|
162
|
+
// Internet Explorer
|
163
|
+
else if (userAgent.includes('MSIE') || userAgent.includes('Trident')) {
|
164
|
+
name = 'Internet Explorer';
|
165
|
+
const match = userAgent.match(/(?:MSIE |rv:)(\d+)/);
|
166
|
+
version = match?.[1] || '0';
|
167
|
+
engine = 'Trident';
|
168
|
+
}
|
169
|
+
|
170
|
+
const mobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
|
171
|
+
const supported = this.isBrowserSupported(name, parseInt(version));
|
172
|
+
|
173
|
+
return {
|
174
|
+
name,
|
175
|
+
version,
|
176
|
+
engine,
|
177
|
+
platform,
|
178
|
+
mobile,
|
179
|
+
supported
|
180
|
+
};
|
181
|
+
}
|
182
|
+
|
183
|
+
/**
|
184
|
+
* Check if browser is supported
|
185
|
+
*/
|
186
|
+
private isBrowserSupported(name: string, version: number): boolean {
|
187
|
+
const minimumVersions: Record<string, number> = {
|
188
|
+
'Chrome': 60,
|
189
|
+
'Firefox': 55,
|
190
|
+
'Safari': 12,
|
191
|
+
'Edge': 79,
|
192
|
+
'Internet Explorer': 11
|
193
|
+
};
|
194
|
+
|
195
|
+
return version >= (minimumVersions[name] || 0);
|
196
|
+
}
|
197
|
+
|
198
|
+
/**
|
199
|
+
* Detect feature support
|
200
|
+
*/
|
201
|
+
private detectFeatures(): FeatureSupport {
|
202
|
+
return {
|
203
|
+
containerQueries: this.supportsContainerQueries(),
|
204
|
+
clampFunction: this.supportsClamp(),
|
205
|
+
customProperties: this.supportsCustomProperties(),
|
206
|
+
flexbox: this.supportsFlexbox(),
|
207
|
+
grid: this.supportsGrid(),
|
208
|
+
intersectionObserver: 'IntersectionObserver' in window,
|
209
|
+
resizeObserver: 'ResizeObserver' in window,
|
210
|
+
mutationObserver: 'MutationObserver' in window,
|
211
|
+
requestAnimationFrame: 'requestAnimationFrame' in window,
|
212
|
+
webAnimations: 'animate' in document.createElement('div'),
|
213
|
+
prefersReducedMotion: this.supportsMediaQuery('(prefers-reduced-motion: reduce)'),
|
214
|
+
prefersColorScheme: this.supportsMediaQuery('(prefers-color-scheme: dark)'),
|
215
|
+
viewportUnits: this.supportsViewportUnits(),
|
216
|
+
calc: this.supportsCalc(),
|
217
|
+
transforms: this.supportsTransforms(),
|
218
|
+
transitions: this.supportsTransitions(),
|
219
|
+
animations: this.supportsAnimations(),
|
220
|
+
webFonts: this.supportsWebFonts(),
|
221
|
+
fontDisplay: this.supportsFontDisplay(),
|
222
|
+
fontVariationSettings: this.supportsFontVariationSettings()
|
223
|
+
};
|
224
|
+
}
|
225
|
+
|
226
|
+
/**
|
227
|
+
* Check container queries support
|
228
|
+
*/
|
229
|
+
private supportsContainerQueries(): boolean {
|
230
|
+
try {
|
231
|
+
return CSS.supports('container-type', 'inline-size');
|
232
|
+
} catch {
|
233
|
+
return false;
|
234
|
+
}
|
235
|
+
}
|
236
|
+
|
237
|
+
/**
|
238
|
+
* Check clamp() function support
|
239
|
+
*/
|
240
|
+
private supportsClamp(): boolean {
|
241
|
+
try {
|
242
|
+
return CSS.supports('width', 'clamp(1px, 2px, 3px)');
|
243
|
+
} catch {
|
244
|
+
return false;
|
245
|
+
}
|
246
|
+
}
|
247
|
+
|
248
|
+
/**
|
249
|
+
* Check CSS custom properties support
|
250
|
+
*/
|
251
|
+
private supportsCustomProperties(): boolean {
|
252
|
+
try {
|
253
|
+
return CSS.supports('--test', '0');
|
254
|
+
} catch {
|
255
|
+
return false;
|
256
|
+
}
|
257
|
+
}
|
258
|
+
|
259
|
+
/**
|
260
|
+
* Check flexbox support
|
261
|
+
*/
|
262
|
+
private supportsFlexbox(): boolean {
|
263
|
+
try {
|
264
|
+
return CSS.supports('display', 'flex');
|
265
|
+
} catch {
|
266
|
+
return false;
|
267
|
+
}
|
268
|
+
}
|
269
|
+
|
270
|
+
/**
|
271
|
+
* Check CSS Grid support
|
272
|
+
*/
|
273
|
+
private supportsGrid(): boolean {
|
274
|
+
try {
|
275
|
+
return CSS.supports('display', 'grid');
|
276
|
+
} catch {
|
277
|
+
return false;
|
278
|
+
}
|
279
|
+
}
|
280
|
+
|
281
|
+
/**
|
282
|
+
* Check media query support
|
283
|
+
*/
|
284
|
+
private supportsMediaQuery(query: string): boolean {
|
285
|
+
try {
|
286
|
+
return window.matchMedia(query).media === query;
|
287
|
+
} catch {
|
288
|
+
return false;
|
289
|
+
}
|
290
|
+
}
|
291
|
+
|
292
|
+
/**
|
293
|
+
* Check viewport units support
|
294
|
+
*/
|
295
|
+
private supportsViewportUnits(): boolean {
|
296
|
+
try {
|
297
|
+
return CSS.supports('width', '1vw');
|
298
|
+
} catch {
|
299
|
+
return false;
|
300
|
+
}
|
301
|
+
}
|
302
|
+
|
303
|
+
/**
|
304
|
+
* Check calc() function support
|
305
|
+
*/
|
306
|
+
private supportsCalc(): boolean {
|
307
|
+
try {
|
308
|
+
return CSS.supports('width', 'calc(1px + 1px)');
|
309
|
+
} catch {
|
310
|
+
return false;
|
311
|
+
}
|
312
|
+
}
|
313
|
+
|
314
|
+
/**
|
315
|
+
* Check CSS transforms support
|
316
|
+
*/
|
317
|
+
private supportsTransforms(): boolean {
|
318
|
+
try {
|
319
|
+
return CSS.supports('transform', 'translateX(1px)');
|
320
|
+
} catch {
|
321
|
+
return false;
|
322
|
+
}
|
323
|
+
}
|
324
|
+
|
325
|
+
/**
|
326
|
+
* Check CSS transitions support
|
327
|
+
*/
|
328
|
+
private supportsTransitions(): boolean {
|
329
|
+
try {
|
330
|
+
return CSS.supports('transition', 'all 1s');
|
331
|
+
} catch {
|
332
|
+
return false;
|
333
|
+
}
|
334
|
+
}
|
335
|
+
|
336
|
+
/**
|
337
|
+
* Check CSS animations support
|
338
|
+
*/
|
339
|
+
private supportsAnimations(): boolean {
|
340
|
+
try {
|
341
|
+
return CSS.supports('animation', 'test 1s');
|
342
|
+
} catch {
|
343
|
+
return false;
|
344
|
+
}
|
345
|
+
}
|
346
|
+
|
347
|
+
/**
|
348
|
+
* Check web fonts support
|
349
|
+
*/
|
350
|
+
private supportsWebFonts(): boolean {
|
351
|
+
return 'fonts' in document;
|
352
|
+
}
|
353
|
+
|
354
|
+
/**
|
355
|
+
* Check font-display support
|
356
|
+
*/
|
357
|
+
private supportsFontDisplay(): boolean {
|
358
|
+
try {
|
359
|
+
return CSS.supports('font-display', 'swap');
|
360
|
+
} catch {
|
361
|
+
return false;
|
362
|
+
}
|
363
|
+
}
|
364
|
+
|
365
|
+
/**
|
366
|
+
* Check font-variation-settings support
|
367
|
+
*/
|
368
|
+
private supportsFontVariationSettings(): boolean {
|
369
|
+
try {
|
370
|
+
return CSS.supports('font-variation-settings', '"wght" 400');
|
371
|
+
} catch {
|
372
|
+
return false;
|
373
|
+
}
|
374
|
+
}
|
375
|
+
|
376
|
+
/**
|
377
|
+
* Apply browser-specific fixes
|
378
|
+
*/
|
379
|
+
private applyBrowserFixes(): void {
|
380
|
+
// Internet Explorer fixes
|
381
|
+
if (this.browserInfo.name === 'Internet Explorer') {
|
382
|
+
this.applyIEFixes();
|
383
|
+
}
|
384
|
+
|
385
|
+
// Safari fixes
|
386
|
+
if (this.browserInfo.name === 'Safari') {
|
387
|
+
this.applySafariFixes();
|
388
|
+
}
|
389
|
+
|
390
|
+
// Firefox fixes
|
391
|
+
if (this.browserInfo.name === 'Firefox') {
|
392
|
+
this.applyFirefoxFixes();
|
393
|
+
}
|
394
|
+
|
395
|
+
// Mobile browser fixes
|
396
|
+
if (this.browserInfo.mobile) {
|
397
|
+
this.applyMobileFixes();
|
398
|
+
}
|
399
|
+
}
|
400
|
+
|
401
|
+
/**
|
402
|
+
* Apply Internet Explorer specific fixes
|
403
|
+
*/
|
404
|
+
private applyIEFixes(): void {
|
405
|
+
// Add IE-specific CSS class
|
406
|
+
document.documentElement.classList.add('ie');
|
407
|
+
|
408
|
+
// Fix viewport units in IE
|
409
|
+
if (!this.featureSupport.viewportUnits) {
|
410
|
+
this.addPolyfill('viewport-units', {
|
411
|
+
name: 'Viewport Units Polyfill',
|
412
|
+
required: true,
|
413
|
+
loaded: false,
|
414
|
+
size: 15000,
|
415
|
+
fallback: this.viewportUnitsFallback.bind(this)
|
416
|
+
});
|
417
|
+
}
|
418
|
+
|
419
|
+
// Fix flexbox in IE
|
420
|
+
if (!this.featureSupport.flexbox) {
|
421
|
+
this.addPolyfill('flexbox', {
|
422
|
+
name: 'Flexbox Polyfill',
|
423
|
+
required: true,
|
424
|
+
loaded: false,
|
425
|
+
size: 25000,
|
426
|
+
fallback: this.flexboxFallback.bind(this)
|
427
|
+
});
|
428
|
+
}
|
429
|
+
}
|
430
|
+
|
431
|
+
/**
|
432
|
+
* Apply Safari specific fixes
|
433
|
+
*/
|
434
|
+
private applySafariFixes(): void {
|
435
|
+
// Add Safari-specific CSS class
|
436
|
+
document.documentElement.classList.add('safari');
|
437
|
+
|
438
|
+
// Fix backdrop-filter in older Safari
|
439
|
+
const safariVersion = parseInt(this.browserInfo.version);
|
440
|
+
if (safariVersion < 14) {
|
441
|
+
document.documentElement.classList.add('safari-old');
|
442
|
+
}
|
443
|
+
}
|
444
|
+
|
445
|
+
/**
|
446
|
+
* Apply Firefox specific fixes
|
447
|
+
*/
|
448
|
+
private applyFirefoxFixes(): void {
|
449
|
+
// Add Firefox-specific CSS class
|
450
|
+
document.documentElement.classList.add('firefox');
|
451
|
+
|
452
|
+
// Fix scrollbar styling in Firefox
|
453
|
+
const style = document.createElement('style');
|
454
|
+
style.textContent = `
|
455
|
+
/* Firefox scrollbar styling */
|
456
|
+
* {
|
457
|
+
scrollbar-width: thin;
|
458
|
+
scrollbar-color: rgba(0,0,0,0.3) transparent;
|
459
|
+
}
|
460
|
+
`;
|
461
|
+
document.head.appendChild(style);
|
462
|
+
}
|
463
|
+
|
464
|
+
/**
|
465
|
+
* Apply mobile browser fixes
|
466
|
+
*/
|
467
|
+
private applyMobileFixes(): void {
|
468
|
+
// Add mobile-specific CSS class
|
469
|
+
document.documentElement.classList.add('mobile');
|
470
|
+
|
471
|
+
// Fix viewport height on mobile
|
472
|
+
const setVH = () => {
|
473
|
+
const vh = window.innerHeight * 0.01;
|
474
|
+
document.documentElement.style.setProperty('--vh', `${vh}px`);
|
475
|
+
};
|
476
|
+
|
477
|
+
setVH();
|
478
|
+
window.addEventListener('resize', setVH);
|
479
|
+
window.addEventListener('orientationchange', setVH);
|
480
|
+
|
481
|
+
// Prevent zoom on input focus
|
482
|
+
const viewport = document.querySelector('meta[name="viewport"]');
|
483
|
+
if (viewport) {
|
484
|
+
viewport.setAttribute('content',
|
485
|
+
'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'
|
486
|
+
);
|
487
|
+
}
|
488
|
+
}
|
489
|
+
|
490
|
+
/**
|
491
|
+
* Load required polyfills
|
492
|
+
*/
|
493
|
+
private async loadPolyfills(): Promise<void> {
|
494
|
+
const requiredPolyfills = Array.from(this.polyfills.values())
|
495
|
+
.filter(polyfill => polyfill.required && !polyfill.loaded);
|
496
|
+
|
497
|
+
if (requiredPolyfills.length === 0) {
|
498
|
+
return;
|
499
|
+
}
|
500
|
+
|
501
|
+
logger.info(`Loading ${requiredPolyfills.length} polyfills`);
|
502
|
+
|
503
|
+
for (const polyfill of requiredPolyfills) {
|
504
|
+
try {
|
505
|
+
if (polyfill.url) {
|
506
|
+
await this.loadPolyfillScript(polyfill);
|
507
|
+
} else if (polyfill.fallback) {
|
508
|
+
polyfill.fallback();
|
509
|
+
}
|
510
|
+
|
511
|
+
polyfill.loaded = true;
|
512
|
+
logger.info(`Loaded polyfill: ${polyfill.name}`);
|
513
|
+
} catch (error) {
|
514
|
+
logger.error(`Failed to load polyfill ${polyfill.name}:`, error);
|
515
|
+
|
516
|
+
// Try fallback if available
|
517
|
+
if (polyfill.fallback) {
|
518
|
+
polyfill.fallback();
|
519
|
+
polyfill.loaded = true;
|
520
|
+
}
|
521
|
+
}
|
522
|
+
}
|
523
|
+
}
|
524
|
+
|
525
|
+
/**
|
526
|
+
* Load polyfill script
|
527
|
+
*/
|
528
|
+
private loadPolyfillScript(polyfill: PolyfillInfo): Promise<void> {
|
529
|
+
return new Promise((resolve, reject) => {
|
530
|
+
const script = document.createElement('script');
|
531
|
+
script.src = polyfill.url!;
|
532
|
+
script.async = true;
|
533
|
+
|
534
|
+
script.onload = () => resolve();
|
535
|
+
script.onerror = () => reject(new Error(`Failed to load ${polyfill.url}`));
|
536
|
+
|
537
|
+
document.head.appendChild(script);
|
538
|
+
});
|
539
|
+
}
|
540
|
+
|
541
|
+
/**
|
542
|
+
* Add polyfill to the system
|
543
|
+
*/
|
544
|
+
private addPolyfill(name: string, info: PolyfillInfo): void {
|
545
|
+
this.polyfills.set(name, info);
|
546
|
+
}
|
547
|
+
|
548
|
+
/**
|
549
|
+
* Setup fallback strategies
|
550
|
+
*/
|
551
|
+
private setupFallbacks(): void {
|
552
|
+
// Container queries fallback
|
553
|
+
if (!this.featureSupport.containerQueries) {
|
554
|
+
this.fallbacks.set('container-queries', this.containerQueriesFallback.bind(this));
|
555
|
+
}
|
556
|
+
|
557
|
+
// Clamp function fallback
|
558
|
+
if (!this.featureSupport.clampFunction) {
|
559
|
+
this.fallbacks.set('clamp', this.clampFallback.bind(this));
|
560
|
+
}
|
561
|
+
|
562
|
+
// Custom properties fallback
|
563
|
+
if (!this.featureSupport.customProperties) {
|
564
|
+
this.fallbacks.set('custom-properties', this.customPropertiesFallback.bind(this));
|
565
|
+
}
|
566
|
+
|
567
|
+
// Intersection Observer fallback
|
568
|
+
if (!this.featureSupport.intersectionObserver) {
|
569
|
+
this.fallbacks.set('intersection-observer', this.intersectionObserverFallback.bind(this));
|
570
|
+
}
|
571
|
+
}
|
572
|
+
|
573
|
+
/**
|
574
|
+
* Container queries fallback
|
575
|
+
*/
|
576
|
+
private containerQueriesFallback(): void {
|
577
|
+
// Implement ResizeObserver-based container queries
|
578
|
+
if (this.featureSupport.resizeObserver) {
|
579
|
+
logger.info('Using ResizeObserver fallback for container queries');
|
580
|
+
|
581
|
+
// Create a polyfill using ResizeObserver
|
582
|
+
const style = document.createElement('style');
|
583
|
+
style.id = 'container-queries-fallback';
|
584
|
+
style.textContent = `
|
585
|
+
/* Container queries fallback using ResizeObserver */
|
586
|
+
[data-container-fallback] {
|
587
|
+
position: relative;
|
588
|
+
}
|
589
|
+
|
590
|
+
[data-container="small"] {
|
591
|
+
--container-size: small;
|
592
|
+
}
|
593
|
+
|
594
|
+
[data-container="medium"] {
|
595
|
+
--container-size: medium;
|
596
|
+
}
|
597
|
+
|
598
|
+
[data-container="large"] {
|
599
|
+
--container-size: large;
|
600
|
+
}
|
601
|
+
`;
|
602
|
+
|
603
|
+
if (!document.getElementById('container-queries-fallback')) {
|
604
|
+
document.head.appendChild(style);
|
605
|
+
}
|
606
|
+
|
607
|
+
// Setup ResizeObserver to simulate container queries
|
608
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
609
|
+
entries.forEach((entry) => {
|
610
|
+
const element = entry.target;
|
611
|
+
const width = entry.contentRect.width;
|
612
|
+
|
613
|
+
// Apply container size classes based on width
|
614
|
+
element.removeAttribute('data-container');
|
615
|
+
|
616
|
+
if (width <= 400) {
|
617
|
+
element.setAttribute('data-container', 'small');
|
618
|
+
} else if (width <= 800) {
|
619
|
+
element.setAttribute('data-container', 'medium');
|
620
|
+
} else {
|
621
|
+
element.setAttribute('data-container', 'large');
|
622
|
+
}
|
623
|
+
});
|
624
|
+
});
|
625
|
+
|
626
|
+
// Observe all elements with container query attributes
|
627
|
+
document.querySelectorAll('[data-container-fallback]').forEach((element) => {
|
628
|
+
resizeObserver.observe(element);
|
629
|
+
});
|
630
|
+
|
631
|
+
} else {
|
632
|
+
logger.warn('No fallback available for container queries');
|
633
|
+
|
634
|
+
// Use media queries as last resort
|
635
|
+
const style = document.createElement('style');
|
636
|
+
style.textContent = `
|
637
|
+
/* Media query fallback for container queries */
|
638
|
+
@media (max-width: 400px) {
|
639
|
+
[data-container-fallback] {
|
640
|
+
--container-size: small;
|
641
|
+
}
|
642
|
+
}
|
643
|
+
|
644
|
+
@media (min-width: 401px) and (max-width: 800px) {
|
645
|
+
[data-container-fallback] {
|
646
|
+
--container-size: medium;
|
647
|
+
}
|
648
|
+
}
|
649
|
+
|
650
|
+
@media (min-width: 801px) {
|
651
|
+
[data-container-fallback] {
|
652
|
+
--container-size: large;
|
653
|
+
}
|
654
|
+
}
|
655
|
+
`;
|
656
|
+
|
657
|
+
document.head.appendChild(style);
|
658
|
+
}
|
659
|
+
}
|
660
|
+
|
661
|
+
/**
|
662
|
+
* Clamp function fallback
|
663
|
+
*/
|
664
|
+
private clampFallback(): void {
|
665
|
+
// Replace clamp() with calc() and media queries
|
666
|
+
logger.info('Using calc() and media queries fallback for clamp()');
|
667
|
+
|
668
|
+
const style = document.createElement('style');
|
669
|
+
style.id = 'clamp-fallback';
|
670
|
+
style.textContent = `
|
671
|
+
/* Clamp fallback styles */
|
672
|
+
.proteus-clamp {
|
673
|
+
font-size: var(--min-size, 16px);
|
674
|
+
}
|
675
|
+
|
676
|
+
@media (min-width: 320px) {
|
677
|
+
.proteus-clamp {
|
678
|
+
font-size: calc(var(--min-size, 16px) + (var(--max-size, 24px) - var(--min-size, 16px)) * ((100vw - 320px) / (1200px - 320px)));
|
679
|
+
}
|
680
|
+
}
|
681
|
+
|
682
|
+
@media (min-width: 1200px) {
|
683
|
+
.proteus-clamp {
|
684
|
+
font-size: var(--max-size, 24px);
|
685
|
+
}
|
686
|
+
}
|
687
|
+
`;
|
688
|
+
|
689
|
+
document.head.appendChild(style);
|
690
|
+
}
|
691
|
+
|
692
|
+
/**
|
693
|
+
* Custom properties fallback
|
694
|
+
*/
|
695
|
+
private customPropertiesFallback(): void {
|
696
|
+
logger.info('Using JavaScript fallback for CSS custom properties');
|
697
|
+
|
698
|
+
// Create a simple CSS custom properties polyfill
|
699
|
+
const customPropertiesMap = new Map<string, string>();
|
700
|
+
|
701
|
+
// Parse existing custom properties
|
702
|
+
const parseCustomProperties = (element: Element) => {
|
703
|
+
const computedStyle = window.getComputedStyle(element);
|
704
|
+
const cssText = (element as HTMLElement).style.cssText;
|
705
|
+
|
706
|
+
// Extract custom properties from inline styles
|
707
|
+
const customPropRegex = /--([\w-]+):\s*([^;]+)/g;
|
708
|
+
let match;
|
709
|
+
|
710
|
+
while ((match = customPropRegex.exec(cssText)) !== null) {
|
711
|
+
if (match[1] && match[2]) {
|
712
|
+
customPropertiesMap.set(`--${match[1]}`, match[2].trim());
|
713
|
+
}
|
714
|
+
}
|
715
|
+
};
|
716
|
+
|
717
|
+
// Apply custom properties polyfill
|
718
|
+
const applyCustomProperties = (element: Element) => {
|
719
|
+
const htmlElement = element as HTMLElement;
|
720
|
+
const style = htmlElement.style;
|
721
|
+
|
722
|
+
// Replace var() functions with actual values
|
723
|
+
const cssText = style.cssText;
|
724
|
+
const varRegex = /var\((--[\w-]+)(?:,\s*([^)]+))?\)/g;
|
725
|
+
|
726
|
+
let updatedCssText = cssText;
|
727
|
+
let varMatch;
|
728
|
+
|
729
|
+
while ((varMatch = varRegex.exec(cssText)) !== null) {
|
730
|
+
const varName = varMatch[1];
|
731
|
+
const fallbackValue = varMatch[2] || '';
|
732
|
+
const value = customPropertiesMap.get(varName || '') || fallbackValue;
|
733
|
+
|
734
|
+
if (value) {
|
735
|
+
updatedCssText = updatedCssText.replace(varMatch[0], value);
|
736
|
+
}
|
737
|
+
}
|
738
|
+
|
739
|
+
if (updatedCssText !== cssText) {
|
740
|
+
htmlElement.style.cssText = updatedCssText;
|
741
|
+
}
|
742
|
+
};
|
743
|
+
|
744
|
+
// Setup mutation observer to handle dynamic changes
|
745
|
+
const observer = new MutationObserver((mutations) => {
|
746
|
+
mutations.forEach((mutation) => {
|
747
|
+
if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
|
748
|
+
const element = mutation.target as Element;
|
749
|
+
parseCustomProperties(element);
|
750
|
+
applyCustomProperties(element);
|
751
|
+
}
|
752
|
+
});
|
753
|
+
});
|
754
|
+
|
755
|
+
// Start observing
|
756
|
+
observer.observe(document.body, {
|
757
|
+
attributes: true,
|
758
|
+
subtree: true,
|
759
|
+
attributeFilter: ['style']
|
760
|
+
});
|
761
|
+
|
762
|
+
// Initial processing
|
763
|
+
document.querySelectorAll('*').forEach((element) => {
|
764
|
+
parseCustomProperties(element);
|
765
|
+
applyCustomProperties(element);
|
766
|
+
});
|
767
|
+
}
|
768
|
+
|
769
|
+
/**
|
770
|
+
* Intersection Observer fallback
|
771
|
+
*/
|
772
|
+
private intersectionObserverFallback(): void {
|
773
|
+
logger.info('Using scroll event fallback for Intersection Observer');
|
774
|
+
|
775
|
+
// Create a polyfill using scroll events
|
776
|
+
class IntersectionObserverPolyfill {
|
777
|
+
private callback: IntersectionObserverCallback;
|
778
|
+
private elements: Set<Element> = new Set();
|
779
|
+
private rootMargin: string;
|
780
|
+
private threshold: number | number[];
|
781
|
+
|
782
|
+
constructor(callback: IntersectionObserverCallback, options: IntersectionObserverInit = {}) {
|
783
|
+
this.callback = callback;
|
784
|
+
this.rootMargin = options.rootMargin || '0px';
|
785
|
+
this.threshold = options.threshold || 0;
|
786
|
+
|
787
|
+
this.setupScrollListener();
|
788
|
+
}
|
789
|
+
|
790
|
+
observe(element: Element): void {
|
791
|
+
this.elements.add(element);
|
792
|
+
this.checkIntersection();
|
793
|
+
}
|
794
|
+
|
795
|
+
unobserve(element: Element): void {
|
796
|
+
this.elements.delete(element);
|
797
|
+
}
|
798
|
+
|
799
|
+
disconnect(): void {
|
800
|
+
this.elements.clear();
|
801
|
+
window.removeEventListener('scroll', this.handleScroll);
|
802
|
+
window.removeEventListener('resize', this.handleScroll);
|
803
|
+
}
|
804
|
+
|
805
|
+
private setupScrollListener(): void {
|
806
|
+
this.handleScroll = this.handleScroll.bind(this);
|
807
|
+
window.addEventListener('scroll', this.handleScroll, { passive: true });
|
808
|
+
window.addEventListener('resize', this.handleScroll, { passive: true });
|
809
|
+
}
|
810
|
+
|
811
|
+
private handleScroll = (): void => {
|
812
|
+
this.checkIntersection();
|
813
|
+
};
|
814
|
+
|
815
|
+
private checkIntersection(): void {
|
816
|
+
const entries: IntersectionObserverEntry[] = [];
|
817
|
+
|
818
|
+
this.elements.forEach((element) => {
|
819
|
+
const rect = element.getBoundingClientRect();
|
820
|
+
const windowHeight = window.innerHeight;
|
821
|
+
const windowWidth = window.innerWidth;
|
822
|
+
|
823
|
+
// Calculate intersection
|
824
|
+
const isIntersecting = rect.top < windowHeight &&
|
825
|
+
rect.bottom > 0 &&
|
826
|
+
rect.left < windowWidth &&
|
827
|
+
rect.right > 0;
|
828
|
+
|
829
|
+
// Calculate intersection ratio (simplified)
|
830
|
+
let intersectionRatio = 0;
|
831
|
+
if (isIntersecting) {
|
832
|
+
const visibleHeight = Math.min(rect.bottom, windowHeight) - Math.max(rect.top, 0);
|
833
|
+
const visibleWidth = Math.min(rect.right, windowWidth) - Math.max(rect.left, 0);
|
834
|
+
const visibleArea = visibleHeight * visibleWidth;
|
835
|
+
const totalArea = rect.height * rect.width;
|
836
|
+
intersectionRatio = totalArea > 0 ? visibleArea / totalArea : 0;
|
837
|
+
}
|
838
|
+
|
839
|
+
entries.push({
|
840
|
+
target: element,
|
841
|
+
isIntersecting,
|
842
|
+
intersectionRatio,
|
843
|
+
boundingClientRect: rect,
|
844
|
+
intersectionRect: rect, // Simplified
|
845
|
+
rootBounds: { top: 0, left: 0, bottom: windowHeight, right: windowWidth, width: windowWidth, height: windowHeight },
|
846
|
+
time: Date.now()
|
847
|
+
} as IntersectionObserverEntry);
|
848
|
+
});
|
849
|
+
|
850
|
+
if (entries.length > 0) {
|
851
|
+
this.callback(entries, this as any);
|
852
|
+
}
|
853
|
+
}
|
854
|
+
}
|
855
|
+
|
856
|
+
// Replace global IntersectionObserver if not available
|
857
|
+
if (!(window as any).IntersectionObserver) {
|
858
|
+
(window as any).IntersectionObserver = IntersectionObserverPolyfill;
|
859
|
+
}
|
860
|
+
}
|
861
|
+
|
862
|
+
/**
|
863
|
+
* Viewport units fallback
|
864
|
+
*/
|
865
|
+
private viewportUnitsFallback(): void {
|
866
|
+
logger.info('Using JavaScript fallback for viewport units');
|
867
|
+
|
868
|
+
const updateViewportUnits = () => {
|
869
|
+
const vw = window.innerWidth / 100;
|
870
|
+
const vh = window.innerHeight / 100;
|
871
|
+
|
872
|
+
document.documentElement.style.setProperty('--vw', `${vw}px`);
|
873
|
+
document.documentElement.style.setProperty('--vh', `${vh}px`);
|
874
|
+
};
|
875
|
+
|
876
|
+
updateViewportUnits();
|
877
|
+
window.addEventListener('resize', updateViewportUnits);
|
878
|
+
}
|
879
|
+
|
880
|
+
/**
|
881
|
+
* Flexbox fallback
|
882
|
+
*/
|
883
|
+
private flexboxFallback(): void {
|
884
|
+
logger.info('Using table/inline-block fallback for flexbox');
|
885
|
+
|
886
|
+
const style = document.createElement('style');
|
887
|
+
style.textContent = `
|
888
|
+
/* Flexbox fallback */
|
889
|
+
.proteus-flex {
|
890
|
+
display: table;
|
891
|
+
width: 100%;
|
892
|
+
}
|
893
|
+
|
894
|
+
.proteus-flex-item {
|
895
|
+
display: table-cell;
|
896
|
+
vertical-align: middle;
|
897
|
+
}
|
898
|
+
`;
|
899
|
+
|
900
|
+
document.head.appendChild(style);
|
901
|
+
}
|
902
|
+
|
903
|
+
/**
|
904
|
+
* Apply performance optimizations
|
905
|
+
*/
|
906
|
+
private applyPerformanceOptimizations(): void {
|
907
|
+
// Optimize for older browsers
|
908
|
+
if (!this.browserInfo.supported) {
|
909
|
+
this.applyLegacyOptimizations();
|
910
|
+
}
|
911
|
+
|
912
|
+
// Optimize for mobile
|
913
|
+
if (this.browserInfo.mobile) {
|
914
|
+
this.applyMobileOptimizations();
|
915
|
+
}
|
916
|
+
|
917
|
+
// Optimize based on feature support
|
918
|
+
this.applyFeatureBasedOptimizations();
|
919
|
+
}
|
920
|
+
|
921
|
+
/**
|
922
|
+
* Apply legacy browser optimizations
|
923
|
+
*/
|
924
|
+
private applyLegacyOptimizations(): void {
|
925
|
+
// Reduce animation complexity
|
926
|
+
document.documentElement.classList.add('legacy-browser');
|
927
|
+
|
928
|
+
const style = document.createElement('style');
|
929
|
+
style.textContent = `
|
930
|
+
.legacy-browser * {
|
931
|
+
animation-duration: 0.1s !important;
|
932
|
+
transition-duration: 0.1s !important;
|
933
|
+
}
|
934
|
+
`;
|
935
|
+
|
936
|
+
document.head.appendChild(style);
|
937
|
+
}
|
938
|
+
|
939
|
+
/**
|
940
|
+
* Apply mobile optimizations
|
941
|
+
*/
|
942
|
+
private applyMobileOptimizations(): void {
|
943
|
+
// Optimize touch interactions
|
944
|
+
const style = document.createElement('style');
|
945
|
+
style.textContent = `
|
946
|
+
/* Mobile optimizations */
|
947
|
+
* {
|
948
|
+
-webkit-tap-highlight-color: transparent;
|
949
|
+
touch-action: manipulation;
|
950
|
+
}
|
951
|
+
|
952
|
+
button, a, [role="button"] {
|
953
|
+
min-height: 44px;
|
954
|
+
min-width: 44px;
|
955
|
+
}
|
956
|
+
`;
|
957
|
+
|
958
|
+
document.head.appendChild(style);
|
959
|
+
}
|
960
|
+
|
961
|
+
/**
|
962
|
+
* Apply feature-based optimizations
|
963
|
+
*/
|
964
|
+
private applyFeatureBasedOptimizations(): void {
|
965
|
+
// Use hardware acceleration when available
|
966
|
+
if (this.featureSupport.transforms) {
|
967
|
+
document.documentElement.classList.add('transforms-supported');
|
968
|
+
}
|
969
|
+
|
970
|
+
// Optimize animations
|
971
|
+
if (!this.featureSupport.webAnimations) {
|
972
|
+
document.documentElement.classList.add('no-web-animations');
|
973
|
+
}
|
974
|
+
}
|
975
|
+
|
976
|
+
/**
|
977
|
+
* Setup modern features
|
978
|
+
*/
|
979
|
+
private setupModernFeatures(): void {
|
980
|
+
// Enable modern features based on support
|
981
|
+
Object.entries(this.featureSupport).forEach(([feature, supported]) => {
|
982
|
+
if (supported) {
|
983
|
+
this.modernFeatures.add(feature);
|
984
|
+
document.documentElement.classList.add(`supports-${feature.replace(/([A-Z])/g, '-$1').toLowerCase()}`);
|
985
|
+
} else {
|
986
|
+
this.legacyFeatures.add(feature);
|
987
|
+
document.documentElement.classList.add(`no-${feature.replace(/([A-Z])/g, '-$1').toLowerCase()}`);
|
988
|
+
}
|
989
|
+
});
|
990
|
+
}
|
991
|
+
|
992
|
+
/**
|
993
|
+
* Get browser information
|
994
|
+
*/
|
995
|
+
public getBrowserInfo(): BrowserInfo {
|
996
|
+
return { ...this.browserInfo };
|
997
|
+
}
|
998
|
+
|
999
|
+
/**
|
1000
|
+
* Get feature support information
|
1001
|
+
*/
|
1002
|
+
public getFeatureSupport(): FeatureSupport {
|
1003
|
+
return { ...this.featureSupport };
|
1004
|
+
}
|
1005
|
+
|
1006
|
+
/**
|
1007
|
+
* Check if specific feature is supported
|
1008
|
+
*/
|
1009
|
+
public isFeatureSupported(feature: keyof FeatureSupport): boolean {
|
1010
|
+
return this.featureSupport[feature];
|
1011
|
+
}
|
1012
|
+
|
1013
|
+
/**
|
1014
|
+
* Get compatibility report
|
1015
|
+
*/
|
1016
|
+
public getCompatibilityReport(): {
|
1017
|
+
browser: BrowserInfo;
|
1018
|
+
features: FeatureSupport;
|
1019
|
+
polyfills: PolyfillInfo[];
|
1020
|
+
modernFeatures: string[];
|
1021
|
+
legacyFeatures: string[];
|
1022
|
+
recommendations: string[];
|
1023
|
+
} {
|
1024
|
+
const recommendations: string[] = [];
|
1025
|
+
|
1026
|
+
if (!this.browserInfo.supported) {
|
1027
|
+
recommendations.push('Consider upgrading to a modern browser for better performance');
|
1028
|
+
}
|
1029
|
+
|
1030
|
+
if (this.legacyFeatures.size > 5) {
|
1031
|
+
recommendations.push('Many modern features are not supported - consider using polyfills');
|
1032
|
+
}
|
1033
|
+
|
1034
|
+
if (this.browserInfo.name === 'Internet Explorer') {
|
1035
|
+
recommendations.push('Internet Explorer support is limited - consider migrating to Edge');
|
1036
|
+
}
|
1037
|
+
|
1038
|
+
return {
|
1039
|
+
browser: this.getBrowserInfo(),
|
1040
|
+
features: this.getFeatureSupport(),
|
1041
|
+
polyfills: Array.from(this.polyfills.values()),
|
1042
|
+
modernFeatures: Array.from(this.modernFeatures),
|
1043
|
+
legacyFeatures: Array.from(this.legacyFeatures),
|
1044
|
+
recommendations
|
1045
|
+
};
|
1046
|
+
}
|
1047
|
+
|
1048
|
+
/**
|
1049
|
+
* Enable graceful degradation for specific feature
|
1050
|
+
*/
|
1051
|
+
public enableGracefulDegradation(feature: string, fallback: () => void): void {
|
1052
|
+
this.fallbacks.set(feature, fallback);
|
1053
|
+
|
1054
|
+
// Apply fallback if feature is not supported
|
1055
|
+
if (!this.modernFeatures.has(feature)) {
|
1056
|
+
fallback();
|
1057
|
+
}
|
1058
|
+
}
|
1059
|
+
|
1060
|
+
/**
|
1061
|
+
* Cleanup compatibility system
|
1062
|
+
*/
|
1063
|
+
public destroy(): void {
|
1064
|
+
// Remove added styles
|
1065
|
+
const styles = document.querySelectorAll('style[id*="proteus"], style[id*="clamp-fallback"]');
|
1066
|
+
styles.forEach(style => style.remove());
|
1067
|
+
|
1068
|
+
// Clear maps
|
1069
|
+
this.polyfills.clear();
|
1070
|
+
this.fallbacks.clear();
|
1071
|
+
this.modernFeatures.clear();
|
1072
|
+
this.legacyFeatures.clear();
|
1073
|
+
|
1074
|
+
logger.info('Browser compatibility system destroyed');
|
1075
|
+
}
|
1076
|
+
}
|