juxscript 1.1.69 → 1.1.71
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/dom-structure-map.json +1 -1
- package/lib/components/blueprint.d.ts.map +1 -1
- package/lib/components/blueprint.ts +368 -368
- package/lib/components/include.d.ts +53 -87
- package/lib/components/include.d.ts.map +1 -1
- package/lib/components/include.js +136 -248
- package/lib/components/include.ts +149 -278
- package/lib/components/modal.d.ts +3 -2
- package/lib/components/modal.d.ts.map +1 -1
- package/lib/components/modal.js +20 -1
- package/lib/components/modal.ts +27 -3
- package/package.json +1 -1
|
@@ -1,120 +1,86 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Include -
|
|
3
|
-
*
|
|
2
|
+
* Include - Simplified resource injection for bundled and external resources
|
|
3
|
+
* Supports page-specific scoping and cleanup
|
|
4
4
|
*/
|
|
5
|
-
type
|
|
5
|
+
type ResourceType = 'css' | 'js' | 'module';
|
|
6
6
|
interface IncludeOptions {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
integrity?: string;
|
|
7
|
+
type?: ResourceType;
|
|
8
|
+
target?: string;
|
|
10
9
|
async?: boolean;
|
|
11
10
|
defer?: boolean;
|
|
12
|
-
|
|
11
|
+
crossOrigin?: 'anonymous' | 'use-credentials';
|
|
12
|
+
integrity?: string;
|
|
13
|
+
pageScoped?: boolean;
|
|
13
14
|
}
|
|
14
15
|
export declare class Include {
|
|
15
16
|
private url;
|
|
16
|
-
private type;
|
|
17
17
|
private options;
|
|
18
18
|
private element;
|
|
19
|
-
private
|
|
20
|
-
constructor(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
withCss(): this;
|
|
19
|
+
private pageId;
|
|
20
|
+
constructor(url: string, options?: IncludeOptions);
|
|
21
|
+
css(): this;
|
|
22
|
+
js(): this;
|
|
23
|
+
module(): this;
|
|
24
|
+
async(): this;
|
|
25
|
+
defer(): this;
|
|
27
26
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
* Inject into specific container instead of <head>
|
|
28
|
+
* Useful for page-specific styles
|
|
30
29
|
*
|
|
31
30
|
* @example
|
|
32
|
-
* jux.include('
|
|
33
|
-
* jux.include('https://unpkg.com/alpine').withJs({ defer: true }).render();
|
|
31
|
+
* jux.include('/css/page1.css').into('#page1-container');
|
|
34
32
|
*/
|
|
35
|
-
|
|
36
|
-
async?: boolean;
|
|
37
|
-
defer?: boolean;
|
|
38
|
-
}): this;
|
|
33
|
+
into(selector: string): this;
|
|
39
34
|
/**
|
|
40
|
-
*
|
|
41
|
-
* Use for module scripts
|
|
42
|
-
*/
|
|
43
|
-
withModule(): this;
|
|
44
|
-
withImage(): this;
|
|
45
|
-
withFont(): this;
|
|
46
|
-
withPreload(as?: string): this;
|
|
47
|
-
withPrefetch(): this;
|
|
48
|
-
withJson(): this;
|
|
49
|
-
/**
|
|
50
|
-
* Shorthand for .withJs()
|
|
51
|
-
* @example jux.include(url).asScript()
|
|
52
|
-
*/
|
|
53
|
-
asScript(options?: {
|
|
54
|
-
async?: boolean;
|
|
55
|
-
defer?: boolean;
|
|
56
|
-
}): this;
|
|
57
|
-
/**
|
|
58
|
-
* Shorthand for .withCss()
|
|
59
|
-
* @example jux.include(url).asStylesheet()
|
|
60
|
-
*/
|
|
61
|
-
asStylesheet(): this;
|
|
62
|
-
/**
|
|
63
|
-
* Fetch and parse JSON file
|
|
64
|
-
* Returns a Promise that resolves to the parsed JSON data
|
|
35
|
+
* Mark as page-scoped for automatic cleanup
|
|
65
36
|
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*/
|
|
70
|
-
asJson<T = any>(): Promise<T>;
|
|
71
|
-
/**
|
|
72
|
-
* Fetch JSON and execute callback with data
|
|
73
|
-
*
|
|
74
|
-
* Usage:
|
|
75
|
-
* jux.include('config.json').onJson(data => {
|
|
76
|
-
* console.log('Config:', data);
|
|
77
|
-
* });
|
|
37
|
+
* @example
|
|
38
|
+
* jux.include('/css/dashboard.css').forPage('dashboard');
|
|
39
|
+
* // Later: Include.cleanupPage('dashboard');
|
|
78
40
|
*/
|
|
79
|
-
|
|
80
|
-
with(options: IncludeOptions): this;
|
|
81
|
-
inHead(): this;
|
|
82
|
-
inBody(): this;
|
|
83
|
-
async(): this;
|
|
84
|
-
defer(): this;
|
|
85
|
-
crossOrigin(value?: 'anonymous' | 'use-credentials'): this;
|
|
86
|
-
integrity(hash: string): this;
|
|
41
|
+
forPage(pageId: string): this;
|
|
87
42
|
render(): this;
|
|
88
43
|
private createStylesheet;
|
|
89
44
|
private createScript;
|
|
90
|
-
private createLink;
|
|
91
45
|
private getContainer;
|
|
92
|
-
private
|
|
46
|
+
private isAlreadyLoaded;
|
|
93
47
|
remove(): this;
|
|
48
|
+
/**
|
|
49
|
+
* Remove all resources for a specific page
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* Include.cleanupPage('dashboard');
|
|
53
|
+
*/
|
|
54
|
+
static cleanupPage(pageId: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* Remove all page-scoped resources
|
|
57
|
+
*/
|
|
58
|
+
static cleanupAll(): void;
|
|
94
59
|
}
|
|
95
60
|
/**
|
|
96
|
-
* Factory function -
|
|
61
|
+
* Factory function - simplified usage
|
|
97
62
|
*
|
|
98
63
|
* Usage:
|
|
99
|
-
* //
|
|
100
|
-
* jux.include('styles.css');
|
|
101
|
-
* jux.include('
|
|
64
|
+
* // Basic (auto-detects from extension)
|
|
65
|
+
* jux.include('/css/styles.css');
|
|
66
|
+
* jux.include('/js/app.js');
|
|
102
67
|
*
|
|
103
|
-
* //
|
|
104
|
-
* jux.include('
|
|
105
|
-
* jux.include('
|
|
106
|
-
* jux.include('https://unpkg.com/htmx.org').withJs({ defer: true });
|
|
68
|
+
* // Page-specific with cleanup
|
|
69
|
+
* jux.include('/css/dashboard.css').forPage('dashboard');
|
|
70
|
+
* jux.include('/js/dashboard.js').forPage('dashboard');
|
|
107
71
|
*
|
|
108
|
-
* //
|
|
109
|
-
*
|
|
72
|
+
* // Later cleanup:
|
|
73
|
+
* Include.cleanupPage('dashboard');
|
|
110
74
|
*
|
|
111
|
-
* //
|
|
112
|
-
* jux.include('
|
|
75
|
+
* // Inject into specific container
|
|
76
|
+
* jux.include('/css/widget.css').into('#widget-container');
|
|
77
|
+
*
|
|
78
|
+
* // External CDN
|
|
79
|
+
* jux.include('https://cdn.tailwindcss.com').js();
|
|
113
80
|
*
|
|
114
|
-
* //
|
|
115
|
-
*
|
|
116
|
-
* jux.include('data.json').onJson(data => console.log(data));
|
|
81
|
+
* // Module
|
|
82
|
+
* jux.include('/js/app.mjs').module();
|
|
117
83
|
*/
|
|
118
|
-
export declare function include(
|
|
84
|
+
export declare function include(url: string, options?: IncludeOptions): Include;
|
|
119
85
|
export {};
|
|
120
86
|
//# sourceMappingURL=include.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"include.d.ts","sourceRoot":"","sources":["include.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"include.d.ts","sourceRoot":"","sources":["include.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,KAAK,YAAY,GAAG,KAAK,GAAG,IAAI,GAAG,QAAQ,CAAC;AAE5C,UAAU,cAAc;IACtB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,WAAW,GAAG,iBAAiB,CAAC;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAKD,qBAAa,OAAO;IAClB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,MAAM,CAAuB;gBAEzB,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB;IAoBrD,GAAG,IAAI,IAAI;IAKX,EAAE,IAAI,IAAI;IAKV,MAAM,IAAI,IAAI;IAKd,KAAK,IAAI,IAAI;IAKb,KAAK,IAAI,IAAI;IAKb;;;;;;OAMG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK5B;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAU7B,MAAM,IAAI,IAAI;IAoDd,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,YAAY;IAuBpB,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,eAAe;IAKvB,MAAM,IAAI,IAAI;IAYd;;;;;OAKG;IACH,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAYxC;;OAEG;IACH,MAAM,CAAC,UAAU,IAAI,IAAI;CAS1B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAItE"}
|
|
@@ -1,175 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Include -
|
|
3
|
-
*
|
|
2
|
+
* Include - Simplified resource injection for bundled and external resources
|
|
3
|
+
* Supports page-specific scoping and cleanup
|
|
4
4
|
*/
|
|
5
|
+
// Global registry for page-scoped resources
|
|
6
|
+
const scopedResources = new Map();
|
|
5
7
|
export class Include {
|
|
6
|
-
constructor(
|
|
7
|
-
this.options = {};
|
|
8
|
+
constructor(url, options = {}) {
|
|
8
9
|
this.element = null;
|
|
9
|
-
this.
|
|
10
|
-
this.url =
|
|
11
|
-
this.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
url.match(/\.(js|mjs)($|\?)/)) {
|
|
23
|
-
return 'script';
|
|
24
|
-
}
|
|
25
|
-
if (url.endsWith('.css'))
|
|
26
|
-
return 'stylesheet';
|
|
27
|
-
if (url.endsWith('.json'))
|
|
28
|
-
return 'json';
|
|
29
|
-
if (url.match(/\.(png|jpg|jpeg|gif|svg|webp)$/i))
|
|
30
|
-
return 'image';
|
|
31
|
-
if (url.match(/\.(woff|woff2|ttf|otf|eot)$/i))
|
|
32
|
-
return 'font';
|
|
33
|
-
// Default to script for extensionless URLs from CDN domains
|
|
34
|
-
if (!url.includes('.') || url.match(/^https?:\/\/cdn/)) {
|
|
35
|
-
return 'script';
|
|
36
|
-
}
|
|
37
|
-
return 'preload';
|
|
38
|
-
}
|
|
39
|
-
/* -------------------------
|
|
40
|
-
* Fluent Type Setters
|
|
41
|
-
* ------------------------- */
|
|
42
|
-
/**
|
|
43
|
-
* Force treat as CSS stylesheet
|
|
44
|
-
* Use when URL doesn't have .css extension
|
|
45
|
-
*/
|
|
46
|
-
withCss() {
|
|
47
|
-
this.type = 'stylesheet';
|
|
48
|
-
this.explicitType = true; // Mark as explicit
|
|
49
|
-
return this;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Force treat as JavaScript
|
|
53
|
-
* Use when URL doesn't have .js extension (CDN scripts, etc.)
|
|
54
|
-
*
|
|
55
|
-
* @example
|
|
56
|
-
* jux.include('https://cdn.tailwindcss.com').withJs().render();
|
|
57
|
-
* jux.include('https://unpkg.com/alpine').withJs({ defer: true }).render();
|
|
58
|
-
*/
|
|
59
|
-
withJs(options) {
|
|
60
|
-
this.type = 'script';
|
|
61
|
-
this.explicitType = true; // Mark as explicit
|
|
62
|
-
if (options?.async)
|
|
63
|
-
this.options.async = true;
|
|
64
|
-
if (options?.defer)
|
|
65
|
-
this.options.defer = true;
|
|
66
|
-
return this;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Force treat as ES module
|
|
70
|
-
* Use for module scripts
|
|
71
|
-
*/
|
|
72
|
-
withModule() {
|
|
73
|
-
this.type = 'module';
|
|
74
|
-
this.explicitType = true; // Mark as explicit
|
|
75
|
-
return this;
|
|
76
|
-
}
|
|
77
|
-
withImage() {
|
|
78
|
-
this.type = 'image';
|
|
79
|
-
this.explicitType = true;
|
|
80
|
-
return this;
|
|
81
|
-
}
|
|
82
|
-
withFont() {
|
|
83
|
-
this.type = 'font';
|
|
84
|
-
this.explicitType = true;
|
|
85
|
-
return this;
|
|
86
|
-
}
|
|
87
|
-
withPreload(as) {
|
|
88
|
-
this.type = 'preload';
|
|
89
|
-
this.explicitType = true;
|
|
90
|
-
if (as)
|
|
91
|
-
this.options.as = as;
|
|
92
|
-
return this;
|
|
93
|
-
}
|
|
94
|
-
withPrefetch() {
|
|
95
|
-
this.type = 'prefetch';
|
|
96
|
-
this.explicitType = true;
|
|
97
|
-
return this;
|
|
98
|
-
}
|
|
99
|
-
withJson() {
|
|
100
|
-
this.type = 'json';
|
|
101
|
-
this.explicitType = true;
|
|
102
|
-
return this;
|
|
103
|
-
}
|
|
104
|
-
/* -------------------------
|
|
105
|
-
* Convenience aliases for common patterns
|
|
106
|
-
* ------------------------- */
|
|
107
|
-
/**
|
|
108
|
-
* Shorthand for .withJs()
|
|
109
|
-
* @example jux.include(url).asScript()
|
|
110
|
-
*/
|
|
111
|
-
asScript(options) {
|
|
112
|
-
return this.withJs(options);
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Shorthand for .withCss()
|
|
116
|
-
* @example jux.include(url).asStylesheet()
|
|
117
|
-
*/
|
|
118
|
-
asStylesheet() {
|
|
119
|
-
return this.withCss();
|
|
120
|
-
}
|
|
121
|
-
/* -------------------------
|
|
122
|
-
* JSON Fetching
|
|
123
|
-
* ------------------------- */
|
|
124
|
-
/**
|
|
125
|
-
* Fetch and parse JSON file
|
|
126
|
-
* Returns a Promise that resolves to the parsed JSON data
|
|
127
|
-
*
|
|
128
|
-
* Usage:
|
|
129
|
-
* const config = await jux.include('config.json').asJson();
|
|
130
|
-
* const data = await jux.include('/api/data').asJson();
|
|
131
|
-
*/
|
|
132
|
-
async asJson() {
|
|
133
|
-
try {
|
|
134
|
-
const response = await fetch(this.url);
|
|
135
|
-
if (!response.ok) {
|
|
136
|
-
throw new Error(`HTTP error! status: ${response.status}`);
|
|
10
|
+
this.pageId = null;
|
|
11
|
+
this.url = url;
|
|
12
|
+
this.options = options;
|
|
13
|
+
// Auto-detect type from extension if not provided
|
|
14
|
+
if (!options.type) {
|
|
15
|
+
if (url.endsWith('.css')) {
|
|
16
|
+
this.options.type = 'css';
|
|
17
|
+
}
|
|
18
|
+
else if (url.endsWith('.mjs') || url.endsWith('.module.js')) {
|
|
19
|
+
this.options.type = 'module';
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
this.options.type = 'js';
|
|
137
23
|
}
|
|
138
|
-
const data = await response.json();
|
|
139
|
-
console.log(`✓ JSON loaded: ${this.url}`);
|
|
140
|
-
return data;
|
|
141
|
-
}
|
|
142
|
-
catch (error) {
|
|
143
|
-
throw error;
|
|
144
24
|
}
|
|
145
25
|
}
|
|
146
|
-
/**
|
|
147
|
-
* Fetch JSON and execute callback with data
|
|
148
|
-
*
|
|
149
|
-
* Usage:
|
|
150
|
-
* jux.include('config.json').onJson(data => {
|
|
151
|
-
* console.log('Config:', data);
|
|
152
|
-
* });
|
|
153
|
-
*/
|
|
154
|
-
onJson(callback) {
|
|
155
|
-
this.asJson().then(callback).catch(error => {
|
|
156
|
-
console.error('Failed to load JSON:', error);
|
|
157
|
-
});
|
|
158
|
-
return this;
|
|
159
|
-
}
|
|
160
26
|
/* -------------------------
|
|
161
|
-
*
|
|
27
|
+
* Fluent API
|
|
162
28
|
* ------------------------- */
|
|
163
|
-
|
|
164
|
-
this.options =
|
|
29
|
+
css() {
|
|
30
|
+
this.options.type = 'css';
|
|
165
31
|
return this;
|
|
166
32
|
}
|
|
167
|
-
|
|
168
|
-
this.options.
|
|
33
|
+
js() {
|
|
34
|
+
this.options.type = 'js';
|
|
169
35
|
return this;
|
|
170
36
|
}
|
|
171
|
-
|
|
172
|
-
this.options.
|
|
37
|
+
module() {
|
|
38
|
+
this.options.type = 'module';
|
|
173
39
|
return this;
|
|
174
40
|
}
|
|
175
41
|
async() {
|
|
@@ -180,12 +46,27 @@ export class Include {
|
|
|
180
46
|
this.options.defer = true;
|
|
181
47
|
return this;
|
|
182
48
|
}
|
|
183
|
-
|
|
184
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Inject into specific container instead of <head>
|
|
51
|
+
* Useful for page-specific styles
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* jux.include('/css/page1.css').into('#page1-container');
|
|
55
|
+
*/
|
|
56
|
+
into(selector) {
|
|
57
|
+
this.options.target = selector;
|
|
185
58
|
return this;
|
|
186
59
|
}
|
|
187
|
-
|
|
188
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Mark as page-scoped for automatic cleanup
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* jux.include('/css/dashboard.css').forPage('dashboard');
|
|
65
|
+
* // Later: Include.cleanupPage('dashboard');
|
|
66
|
+
*/
|
|
67
|
+
forPage(pageId) {
|
|
68
|
+
this.pageId = pageId;
|
|
69
|
+
this.options.pageScoped = true;
|
|
189
70
|
return this;
|
|
190
71
|
}
|
|
191
72
|
/* -------------------------
|
|
@@ -194,45 +75,40 @@ export class Include {
|
|
|
194
75
|
render() {
|
|
195
76
|
if (typeof document === 'undefined')
|
|
196
77
|
return this;
|
|
197
|
-
// Don't render JSON type (it's fetched via asJson() instead)
|
|
198
|
-
if (this.type === 'json') {
|
|
199
|
-
console.warn('Include: JSON files should be loaded with .asJson() instead of .render()');
|
|
200
|
-
return this;
|
|
201
|
-
}
|
|
202
78
|
try {
|
|
203
|
-
|
|
79
|
+
// Check if already loaded
|
|
80
|
+
if (this.isAlreadyLoaded()) {
|
|
81
|
+
console.log(`⚠️ Resource already loaded: ${this.url}`);
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
// Create element based on type
|
|
204
85
|
let element;
|
|
205
|
-
switch (this.type) {
|
|
206
|
-
case '
|
|
86
|
+
switch (this.options.type) {
|
|
87
|
+
case 'css':
|
|
207
88
|
element = this.createStylesheet();
|
|
208
89
|
break;
|
|
209
|
-
case '
|
|
90
|
+
case 'js':
|
|
210
91
|
case 'module':
|
|
211
92
|
element = this.createScript();
|
|
212
93
|
break;
|
|
213
|
-
case 'image':
|
|
214
|
-
case 'font':
|
|
215
|
-
case 'preload':
|
|
216
|
-
case 'prefetch':
|
|
217
|
-
element = this.createLink();
|
|
218
|
-
break;
|
|
219
94
|
default:
|
|
220
|
-
throw new Error(`Unknown
|
|
95
|
+
throw new Error(`Unknown resource type: ${this.options.type}`);
|
|
221
96
|
}
|
|
97
|
+
// Get target container
|
|
222
98
|
const container = this.getContainer();
|
|
223
|
-
|
|
224
|
-
if (location === 'body-end') {
|
|
225
|
-
container.appendChild(element);
|
|
226
|
-
}
|
|
227
|
-
else {
|
|
228
|
-
container.insertBefore(element, container.firstChild);
|
|
229
|
-
}
|
|
99
|
+
container.appendChild(element);
|
|
230
100
|
this.element = element;
|
|
231
|
-
//
|
|
232
|
-
|
|
233
|
-
|
|
101
|
+
// Register for page cleanup if needed
|
|
102
|
+
if (this.options.pageScoped && this.pageId) {
|
|
103
|
+
if (!scopedResources.has(this.pageId)) {
|
|
104
|
+
scopedResources.set(this.pageId, new Set());
|
|
105
|
+
}
|
|
106
|
+
scopedResources.get(this.pageId).add(element);
|
|
107
|
+
}
|
|
108
|
+
console.log(`✓ Loaded ${this.options.type}: ${this.url}`);
|
|
234
109
|
}
|
|
235
110
|
catch (error) {
|
|
111
|
+
console.error(`✗ Failed to load ${this.options.type}: ${this.url}`, error);
|
|
236
112
|
throw error;
|
|
237
113
|
}
|
|
238
114
|
return this;
|
|
@@ -244,18 +120,22 @@ export class Include {
|
|
|
244
120
|
const link = document.createElement('link');
|
|
245
121
|
link.rel = 'stylesheet';
|
|
246
122
|
link.href = this.url;
|
|
123
|
+
link.dataset.juxInclude = this.url;
|
|
247
124
|
if (this.options.crossOrigin)
|
|
248
125
|
link.crossOrigin = this.options.crossOrigin;
|
|
249
126
|
if (this.options.integrity)
|
|
250
127
|
link.integrity = this.options.integrity;
|
|
251
128
|
link.onload = () => console.log(`✓ Stylesheet loaded: ${this.url}`);
|
|
252
|
-
link.onerror = () =>
|
|
129
|
+
link.onerror = () => {
|
|
130
|
+
throw new Error(`Failed to load stylesheet: ${this.url}`);
|
|
131
|
+
};
|
|
253
132
|
return link;
|
|
254
133
|
}
|
|
255
134
|
createScript() {
|
|
256
135
|
const script = document.createElement('script');
|
|
257
136
|
script.src = this.url;
|
|
258
|
-
|
|
137
|
+
script.dataset.juxInclude = this.url;
|
|
138
|
+
if (this.options.type === 'module')
|
|
259
139
|
script.type = 'module';
|
|
260
140
|
if (this.options.async)
|
|
261
141
|
script.async = true;
|
|
@@ -266,48 +146,27 @@ export class Include {
|
|
|
266
146
|
if (this.options.integrity)
|
|
267
147
|
script.integrity = this.options.integrity;
|
|
268
148
|
script.onload = () => console.log(`✓ Script loaded: ${this.url}`);
|
|
269
|
-
script.onerror = () =>
|
|
149
|
+
script.onerror = () => {
|
|
150
|
+
throw new Error(`Failed to load script: ${this.url}`);
|
|
151
|
+
};
|
|
270
152
|
return script;
|
|
271
153
|
}
|
|
272
|
-
createLink() {
|
|
273
|
-
const link = document.createElement('link');
|
|
274
|
-
// Set rel based on type
|
|
275
|
-
if (this.type === 'prefetch') {
|
|
276
|
-
link.rel = 'prefetch';
|
|
277
|
-
}
|
|
278
|
-
else {
|
|
279
|
-
link.rel = 'preload';
|
|
280
|
-
}
|
|
281
|
-
link.href = this.url;
|
|
282
|
-
// Set 'as' attribute
|
|
283
|
-
if (this.type === 'image') {
|
|
284
|
-
link.as = 'image';
|
|
285
|
-
}
|
|
286
|
-
else if (this.type === 'font') {
|
|
287
|
-
link.as = 'font';
|
|
288
|
-
link.crossOrigin = this.options.crossOrigin || 'anonymous';
|
|
289
|
-
}
|
|
290
|
-
else if (this.options.as) {
|
|
291
|
-
link.as = this.options.as;
|
|
292
|
-
}
|
|
293
|
-
if (this.options.crossOrigin && this.type !== 'font') {
|
|
294
|
-
link.crossOrigin = this.options.crossOrigin;
|
|
295
|
-
}
|
|
296
|
-
if (this.options.integrity) {
|
|
297
|
-
link.integrity = this.options.integrity;
|
|
298
|
-
}
|
|
299
|
-
return link;
|
|
300
|
-
}
|
|
301
154
|
/* -------------------------
|
|
302
155
|
* Helpers
|
|
303
156
|
* ------------------------- */
|
|
304
157
|
getContainer() {
|
|
305
|
-
|
|
306
|
-
|
|
158
|
+
if (this.options.target) {
|
|
159
|
+
const container = document.querySelector(this.options.target);
|
|
160
|
+
if (!container) {
|
|
161
|
+
throw new Error(`Target container not found: ${this.options.target}`);
|
|
162
|
+
}
|
|
163
|
+
return container;
|
|
164
|
+
}
|
|
165
|
+
return document.head;
|
|
307
166
|
}
|
|
308
|
-
|
|
309
|
-
const
|
|
310
|
-
|
|
167
|
+
isAlreadyLoaded() {
|
|
168
|
+
const selector = `[data-jux-include="${this.url}"]`;
|
|
169
|
+
return document.querySelector(selector) !== null;
|
|
311
170
|
}
|
|
312
171
|
remove() {
|
|
313
172
|
if (this.element?.parentNode) {
|
|
@@ -316,35 +175,64 @@ export class Include {
|
|
|
316
175
|
}
|
|
317
176
|
return this;
|
|
318
177
|
}
|
|
178
|
+
/* -------------------------
|
|
179
|
+
* Static Page Cleanup
|
|
180
|
+
* ------------------------- */
|
|
181
|
+
/**
|
|
182
|
+
* Remove all resources for a specific page
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* Include.cleanupPage('dashboard');
|
|
186
|
+
*/
|
|
187
|
+
static cleanupPage(pageId) {
|
|
188
|
+
const resources = scopedResources.get(pageId);
|
|
189
|
+
if (!resources)
|
|
190
|
+
return;
|
|
191
|
+
resources.forEach(element => {
|
|
192
|
+
element.parentNode?.removeChild(element);
|
|
193
|
+
});
|
|
194
|
+
scopedResources.delete(pageId);
|
|
195
|
+
console.log(`✓ Cleaned up page resources: ${pageId}`);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Remove all page-scoped resources
|
|
199
|
+
*/
|
|
200
|
+
static cleanupAll() {
|
|
201
|
+
scopedResources.forEach((resources, pageId) => {
|
|
202
|
+
resources.forEach(element => {
|
|
203
|
+
element.parentNode?.removeChild(element);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
scopedResources.clear();
|
|
207
|
+
console.log('✓ Cleaned up all page-scoped resources');
|
|
208
|
+
}
|
|
319
209
|
}
|
|
320
210
|
/**
|
|
321
|
-
* Factory function -
|
|
211
|
+
* Factory function - simplified usage
|
|
322
212
|
*
|
|
323
213
|
* Usage:
|
|
324
|
-
* //
|
|
325
|
-
* jux.include('styles.css');
|
|
326
|
-
* jux.include('
|
|
214
|
+
* // Basic (auto-detects from extension)
|
|
215
|
+
* jux.include('/css/styles.css');
|
|
216
|
+
* jux.include('/js/app.js');
|
|
327
217
|
*
|
|
328
|
-
* //
|
|
329
|
-
* jux.include('
|
|
330
|
-
* jux.include('
|
|
331
|
-
* jux.include('https://unpkg.com/htmx.org').withJs({ defer: true });
|
|
218
|
+
* // Page-specific with cleanup
|
|
219
|
+
* jux.include('/css/dashboard.css').forPage('dashboard');
|
|
220
|
+
* jux.include('/js/dashboard.js').forPage('dashboard');
|
|
332
221
|
*
|
|
333
|
-
* //
|
|
334
|
-
*
|
|
222
|
+
* // Later cleanup:
|
|
223
|
+
* Include.cleanupPage('dashboard');
|
|
335
224
|
*
|
|
336
|
-
* //
|
|
337
|
-
* jux.include('
|
|
225
|
+
* // Inject into specific container
|
|
226
|
+
* jux.include('/css/widget.css').into('#widget-container');
|
|
338
227
|
*
|
|
339
|
-
* //
|
|
340
|
-
*
|
|
341
|
-
*
|
|
228
|
+
* // External CDN
|
|
229
|
+
* jux.include('https://cdn.tailwindcss.com').js();
|
|
230
|
+
*
|
|
231
|
+
* // Module
|
|
232
|
+
* jux.include('/js/app.mjs').module();
|
|
342
233
|
*/
|
|
343
|
-
export function include(
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
imp.render();
|
|
348
|
-
}
|
|
349
|
-
return imp;
|
|
234
|
+
export function include(url, options) {
|
|
235
|
+
const inc = new Include(url, options);
|
|
236
|
+
inc.render();
|
|
237
|
+
return inc;
|
|
350
238
|
}
|