@cocreate/plugins 1.0.2 → 1.1.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/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/src/index.js +170 -300
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [1.1.0](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.0.3...v1.1.0) (2026-01-15)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add marker to indicate where css files will be inserted ([6b4b030](https://github.com/CoCreate-app/CoCreate-plugins/commit/6b4b0301acdabff19b8cca5968cc67619e417c92))
|
|
7
|
+
|
|
8
|
+
## [1.0.3](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.0.2...v1.0.3) (2025-12-26)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* update worklow ([8976d08](https://github.com/CoCreate-app/CoCreate-plugins/commit/8976d080f8972e840ff5530d5dfb15f839e9ac5b))
|
|
14
|
+
|
|
1
15
|
## [1.0.2](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.0.1...v1.0.2) (2025-12-25)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -6,17 +6,14 @@ const plugins = {
|
|
|
6
6
|
Toastify: {
|
|
7
7
|
js: [{ src: "https://cdn.jsdelivr.net/npm/toastify-js", crossOrigin: "anonymous" }],
|
|
8
8
|
css: ["https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css"]
|
|
9
|
-
// attribute prefix: toastify-
|
|
10
9
|
},
|
|
11
10
|
Choices: {
|
|
12
11
|
js: [{ src: "https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js", crossOrigin: "anonymous" }],
|
|
13
12
|
css: ["https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css"]
|
|
14
|
-
// attribute prefix: choices-
|
|
15
13
|
},
|
|
16
14
|
flatpickr: {
|
|
17
15
|
js: [{ src: "https://cdn.jsdelivr.net/npm/flatpickr", crossOrigin: "anonymous" }],
|
|
18
16
|
css: ["https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css"]
|
|
19
|
-
// attribute prefix: flatpickr-
|
|
20
17
|
},
|
|
21
18
|
Quill: {
|
|
22
19
|
js: [{ src: "https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.min.js", crossOrigin: "anonymous" }],
|
|
@@ -25,47 +22,37 @@ const plugins = {
|
|
|
25
22
|
"https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.snow.css",
|
|
26
23
|
"https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.bubble.css"
|
|
27
24
|
]
|
|
28
|
-
// attribute prefix: quill-
|
|
29
25
|
},
|
|
30
26
|
ClassicEditor: {
|
|
31
27
|
js: [{ src: "https://cdn.ckeditor.com/ckeditor5/41.2.0/classic/ckeditor.js", crossOrigin: "anonymous" }]
|
|
32
|
-
// attribute prefix: classiceditor-
|
|
33
28
|
},
|
|
34
29
|
Dropzone: {
|
|
35
30
|
js: [{ src: "https://unpkg.com/dropzone@5/dist/min/dropzone.min.js", crossOrigin: "anonymous" }],
|
|
36
31
|
css: ["https://unpkg.com/dropzone@5/dist/min/dropzone.min.css"]
|
|
37
|
-
// attribute prefix: dropzone-
|
|
38
32
|
},
|
|
39
33
|
SimpleBar: {
|
|
40
34
|
js: [{ src: "https://cdn.jsdelivr.net/npm/simplebar@latest/dist/simplebar.min.js", crossOrigin: "anonymous" }],
|
|
41
35
|
css: ["https://cdn.jsdelivr.net/npm/simplebar@latest/dist/simplebar.css"]
|
|
42
|
-
// attribute prefix: simplebar-
|
|
43
36
|
},
|
|
44
37
|
GLightbox: {
|
|
45
38
|
js: [{ src: "https://cdn.jsdelivr.net/npm/glightbox/dist/js/glightbox.min.js", crossOrigin: "anonymous" }],
|
|
46
39
|
css: ["https://cdn.jsdelivr.net/npm/glightbox/dist/css/glightbox.min.css"]
|
|
47
|
-
// attribute prefix: glightbox-
|
|
48
40
|
},
|
|
49
41
|
FgEmojiPicker: {
|
|
50
42
|
js: [{ src: "https://cdn.jsdelivr.net/npm/fg-emoji-picker/fgEmojiPicker.js", crossOrigin: "anonymous" }]
|
|
51
|
-
// attribute prefix: fgemojipicker-
|
|
52
43
|
},
|
|
53
44
|
bootstrap: {
|
|
54
45
|
js: [{ src: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js", crossOrigin: "anonymous" }]
|
|
55
|
-
// attribute prefix: bootstrap-
|
|
56
46
|
},
|
|
57
47
|
Waves: {
|
|
58
48
|
js: [{ src: "https://cdn.jsdelivr.net/npm/node-waves/dist/waves.min.js", crossOrigin: "anonymous" }],
|
|
59
49
|
css: ["https://cdn.jsdelivr.net/npm/node-waves/dist/waves.min.css"]
|
|
60
|
-
// attribute prefix: waves-
|
|
61
50
|
},
|
|
62
51
|
feather: {
|
|
63
52
|
js: [{ src: "https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js", crossOrigin: "anonymous" }]
|
|
64
|
-
// attribute prefix: feather-
|
|
65
53
|
},
|
|
66
54
|
ApexCharts: {
|
|
67
55
|
js: [{ src: "https://cdn.jsdelivr.net/npm/apexcharts", crossOrigin: "anonymous" }]
|
|
68
|
-
// attribute prefix: apexcharts-
|
|
69
56
|
},
|
|
70
57
|
jsVectorMap: {
|
|
71
58
|
js: [
|
|
@@ -73,41 +60,33 @@ const plugins = {
|
|
|
73
60
|
{ src: "https://cdn.jsdelivr.net/npm/jsvectormap/dist/maps/world.js", crossOrigin: "anonymous" }
|
|
74
61
|
],
|
|
75
62
|
css: ["https://cdn.jsdelivr.net/npm/jsvectormap/dist/css/jsvectormap.min.css"]
|
|
76
|
-
// attribute prefix: jsvectormap-
|
|
77
63
|
},
|
|
78
64
|
Swiper: {
|
|
79
65
|
js: [{ src: "https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js", crossOrigin: "anonymous" }],
|
|
80
66
|
css: ["https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css"]
|
|
81
|
-
// attribute prefix: swiper-
|
|
82
67
|
},
|
|
83
68
|
List: {
|
|
84
69
|
js: [
|
|
85
70
|
{ src: "https://cdnjs.cloudflare.com/ajax/libs/list.js/2.3.1/list.min.js", crossOrigin: "anonymous" },
|
|
86
71
|
{ src: "https://cdnjs.cloudflare.com/ajax/libs/list.pagination.js/0.1.1/list.pagination.min.js", crossOrigin: "anonymous" }
|
|
87
72
|
]
|
|
88
|
-
// attribute prefix: list-
|
|
89
73
|
},
|
|
90
74
|
Swal: {
|
|
91
75
|
js: [{ src: "https://cdn.jsdelivr.net/npm/sweetalert2@11", crossOrigin: "anonymous" }],
|
|
92
76
|
css: ["https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css"]
|
|
93
|
-
// attribute prefix: swal-
|
|
94
77
|
},
|
|
95
78
|
FullCalendar: {
|
|
96
79
|
js: [{ src: "https://cdn.jsdelivr.net/npm/fullcalendar@6.1.15/index.global.min.js", crossOrigin: "anonymous" }]
|
|
97
|
-
// attribute prefix: fullcalendar-
|
|
98
80
|
},
|
|
99
81
|
Cleave: {
|
|
100
82
|
js: [{ src: "https://cdn.jsdelivr.net/npm/cleave.js/dist/cleave.min.js", crossOrigin: "anonymous" }]
|
|
101
|
-
// attribute prefix: cleave-
|
|
102
83
|
},
|
|
103
84
|
noUiSlider: {
|
|
104
85
|
js: [{ src: "https://cdn.jsdelivr.net/npm/nouislider/dist/nouislider.min.js", crossOrigin: "anonymous" }],
|
|
105
86
|
css: ["https://cdn.jsdelivr.net/npm/nouislider/dist/nouislider.min.css"]
|
|
106
|
-
// attribute prefix: nouislider-
|
|
107
87
|
},
|
|
108
88
|
wNumb: {
|
|
109
89
|
js: [{ src: "https://cdn.jsdelivr.net/npm/wnumb/wNumb.min.js", crossOrigin: "anonymous" }]
|
|
110
|
-
// attribute prefix: wnumb-
|
|
111
90
|
},
|
|
112
91
|
Grid: {
|
|
113
92
|
js: [
|
|
@@ -115,7 +94,6 @@ const plugins = {
|
|
|
115
94
|
{ src: "https://unpkg.com/gridjs/plugins/selection/dist/selection.umd.js", crossOrigin: "anonymous" }
|
|
116
95
|
],
|
|
117
96
|
css: ["https://unpkg.com/gridjs/dist/theme/mermaid.min.css"]
|
|
118
|
-
// attribute prefix: grid-
|
|
119
97
|
},
|
|
120
98
|
FilePond: {
|
|
121
99
|
js: [
|
|
@@ -129,83 +107,74 @@ const plugins = {
|
|
|
129
107
|
"https://unpkg.com/filepond/dist/filepond.min.css",
|
|
130
108
|
"https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css"
|
|
131
109
|
]
|
|
132
|
-
// attribute prefix: filepond-
|
|
133
110
|
},
|
|
134
111
|
Prism: {
|
|
135
112
|
js: [{ src: "https://cdn.jsdelivr.net/npm/prismjs/prism.min.js", crossOrigin: "anonymous" }],
|
|
136
113
|
css: ["https://cdn.jsdelivr.net/npm/prismjs/themes/prism.min.css"]
|
|
137
|
-
// attribute prefix: prism-
|
|
138
114
|
},
|
|
139
115
|
Isotope: {
|
|
140
116
|
js: [{ src: "https://unpkg.com/isotope-layout@3/dist/isotope.pkgd.min.js", crossOrigin: "anonymous" }]
|
|
141
|
-
// attribute prefix: isotope-
|
|
142
117
|
},
|
|
143
118
|
particlesJS: {
|
|
144
119
|
js: [{ src: "https://cdn.jsdelivr.net/npm/particles.js@2.0.0/particles.min.js", crossOrigin: "anonymous" }]
|
|
145
|
-
// attribute prefix: particlesjs-
|
|
146
120
|
},
|
|
147
121
|
dragula: {
|
|
148
122
|
js: [{ src: "https://cdn.jsdelivr.net/npm/dragula/dist/dragula.min.js", crossOrigin: "anonymous" }],
|
|
149
123
|
css: ["https://cdn.jsdelivr.net/npm/dragula/dist/dragula.min.css"]
|
|
150
|
-
// attribute prefix: dragula-
|
|
151
124
|
},
|
|
152
125
|
DomAutoscroller: {
|
|
153
126
|
js: [{ src: "https://cdn.jsdelivr.net/npm/dom-autoscroller", crossOrigin: "anonymous" }]
|
|
154
|
-
// attribute prefix: domautoscroller-
|
|
155
127
|
},
|
|
156
128
|
Card: {
|
|
157
129
|
js: [{ src: "https://cdn.jsdelivr.net/npm/card/dist/card.js", crossOrigin: "anonymous" }]
|
|
158
|
-
// attribute prefix: card-
|
|
159
130
|
},
|
|
160
131
|
Chart: {
|
|
161
132
|
js: [{ src: "https://cdn.jsdelivr.net/npm/chart.js", crossOrigin: "anonymous" }]
|
|
162
|
-
// attribute prefix: chart-
|
|
163
133
|
},
|
|
164
134
|
echarts: {
|
|
165
135
|
js: [{ src: "https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js", crossOrigin: "anonymous" }]
|
|
166
|
-
// attribute prefix: echarts-
|
|
167
136
|
},
|
|
168
137
|
Multi: {
|
|
169
138
|
js: [{ src: "https://cdn.jsdelivr.net/npm/multi.js/dist/multi.min.js", crossOrigin: "anonymous" }],
|
|
170
139
|
css: ["https://cdn.jsdelivr.net/npm/multi.js/dist/multi.min.css"]
|
|
171
|
-
// attribute prefix: multi-
|
|
172
140
|
},
|
|
173
141
|
autoComplete: {
|
|
174
142
|
js: [{ src: "https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.7/dist/autoComplete.min.js", crossOrigin: "anonymous" }],
|
|
175
143
|
css: ["https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.7/dist/css/autoComplete.01.min.css"]
|
|
176
|
-
// attribute prefix: autocomplete-
|
|
177
144
|
},
|
|
178
145
|
Pickr: {
|
|
179
146
|
js: [{ src: "https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/pickr.min.js", crossOrigin: "anonymous" }],
|
|
180
147
|
css: ["https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/themes/classic.min.css"]
|
|
181
|
-
// attribute prefix: pickr-
|
|
182
148
|
},
|
|
183
149
|
Shepherd: {
|
|
184
150
|
js: [{ src: "https://cdn.jsdelivr.net/npm/shepherd.js/dist/js/shepherd.min.js", crossOrigin: "anonymous" }],
|
|
185
151
|
css: ["https://cdn.jsdelivr.net/npm/shepherd.js/dist/css/shepherd.css"]
|
|
186
|
-
// attribute prefix: shepherd-
|
|
187
152
|
},
|
|
188
153
|
GMaps: {
|
|
189
154
|
js: [{ src: "https://cdn.jsdelivr.net/npm/gmaps/gmaps.min.js", crossOrigin: "anonymous" }]
|
|
190
|
-
// attribute prefix: gmaps-
|
|
191
155
|
},
|
|
192
156
|
L: {
|
|
193
157
|
js: [{ src: "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js", crossOrigin: "anonymous" }],
|
|
194
158
|
css: ["https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"]
|
|
195
|
-
// attribute prefix: l-
|
|
196
159
|
},
|
|
197
160
|
Masonry: {
|
|
198
161
|
js: [{ src: "https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js", crossOrigin: "anonymous" }]
|
|
199
|
-
// attribute prefix: masonry-
|
|
200
162
|
},
|
|
201
163
|
Rater: {
|
|
202
164
|
js: [{ src: "https://cdn.jsdelivr.net/npm/rater-js/index.js", crossOrigin: "anonymous" }]
|
|
203
|
-
|
|
165
|
+
},
|
|
166
|
+
Anime: {
|
|
167
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/animejs@3.2.2/lib/anime.min.js", crossOrigin: "anonymous" }]
|
|
204
168
|
}
|
|
205
169
|
};
|
|
206
170
|
|
|
207
171
|
// --- CORE ENGINE ---
|
|
208
172
|
|
|
173
|
+
// Global Cache for script promises to prevent race conditions and duplicate loads
|
|
174
|
+
const scriptCache = new Map();
|
|
175
|
+
// Cache the CSS marker once on load
|
|
176
|
+
const cssMarker = typeof document !== 'undefined' ? document.querySelector('link[plugins]') : null;
|
|
177
|
+
|
|
209
178
|
/**
|
|
210
179
|
* Global Initialization Function
|
|
211
180
|
* Processes one or more elements to attach plugins.
|
|
@@ -214,323 +183,219 @@ const plugins = {
|
|
|
214
183
|
export function init(elements) {
|
|
215
184
|
if (!elements) return;
|
|
216
185
|
|
|
217
|
-
// 1. Detect Type & Normalize to Array
|
|
218
|
-
// Normalize string handling (removed), Element, NodeList, HTMLCollection, Array
|
|
219
186
|
let collection = [];
|
|
220
|
-
|
|
221
187
|
if (elements instanceof HTMLElement || elements instanceof Element) {
|
|
222
188
|
collection = [elements];
|
|
223
189
|
} else if (elements.length !== undefined && typeof elements !== 'function') {
|
|
224
|
-
// Handle Array-like objects (NodeList, HTMLCollection, Array)
|
|
225
190
|
collection = Array.from(elements);
|
|
226
191
|
} else {
|
|
227
|
-
// Fallback for single object that isn't element (unlikely given spec, but safe)
|
|
228
192
|
collection = [elements];
|
|
229
193
|
}
|
|
230
194
|
|
|
231
|
-
// 2. Iterate and Process
|
|
232
195
|
collection.forEach(el => {
|
|
233
|
-
// Ensure valid element before processing
|
|
234
196
|
if (el && typeof el.getAttribute === 'function') {
|
|
235
197
|
processPlugin(el);
|
|
236
198
|
}
|
|
237
199
|
});
|
|
238
200
|
}
|
|
239
201
|
|
|
240
|
-
// Auto-Init on load
|
|
241
|
-
if (typeof document !== 'undefined') {
|
|
242
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
243
|
-
init(document.querySelectorAll("[plugin]"));
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
|
|
247
202
|
async function processPlugin(el) {
|
|
248
203
|
const rawAttr = el.getAttribute("plugin");
|
|
249
204
|
if (!rawAttr) return;
|
|
250
205
|
|
|
251
|
-
const
|
|
206
|
+
const pluginNames = rawAttr.split(',').map(s => s.trim());
|
|
252
207
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if (pluginDef) {
|
|
258
|
-
console.log(`[Plugin System] Loading ${pluginName}...`);
|
|
208
|
+
for (const pluginName of pluginNames) {
|
|
209
|
+
const pluginDef = plugins[pluginName];
|
|
210
|
+
if (!pluginDef) continue;
|
|
259
211
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
212
|
+
// Load CSS
|
|
213
|
+
if (pluginDef.css) pluginDef.css.forEach(href => {
|
|
214
|
+
if (!document.querySelector(`link[href="${href}"]`)) {
|
|
263
215
|
const link = document.createElement("link");
|
|
264
|
-
link.rel="stylesheet";
|
|
216
|
+
link.rel = "stylesheet";
|
|
217
|
+
link.href = href;
|
|
218
|
+
|
|
219
|
+
// CSS INJECTION STRATEGY:
|
|
220
|
+
// 1. Priority: Check for a specific marker element <link plugin>
|
|
221
|
+
// (Cached globally in cssMarker)
|
|
222
|
+
|
|
223
|
+
if (cssMarker) {
|
|
224
|
+
// Insert before the marker
|
|
225
|
+
cssMarker.parentNode.insertBefore(link, cssMarker);
|
|
226
|
+
} else {
|
|
227
|
+
// 2. Fallback: Prepend before existing CSS
|
|
228
|
+
// To allow custom CSS to easily override plugin defaults, we must ensure
|
|
229
|
+
// plugin CSS loads BEFORE user CSS.
|
|
230
|
+
const firstStyle = document.head.querySelector('link[rel="stylesheet"], style');
|
|
231
|
+
|
|
232
|
+
if (firstStyle) {
|
|
233
|
+
document.head.insertBefore(link, firstStyle);
|
|
234
|
+
} else {
|
|
235
|
+
// If no CSS exists yet, appending is safe
|
|
236
|
+
document.head.appendChild(link);
|
|
237
|
+
}
|
|
265
238
|
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (
|
|
281
|
-
|
|
282
|
-
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Load JS with Promise Cache
|
|
243
|
+
if (pluginDef.js) {
|
|
244
|
+
for (const item of pluginDef.js) {
|
|
245
|
+
const src = typeof item === 'string' ? item : item.src;
|
|
246
|
+
const integrity = typeof item === 'object' ? item.integrity : null;
|
|
247
|
+
const crossOrigin = typeof item === 'object' ? item.crossOrigin : null;
|
|
248
|
+
|
|
249
|
+
if (!scriptCache.has(src)) {
|
|
250
|
+
const scriptPromise = new Promise((resolve, reject) => {
|
|
251
|
+
// Check if already in DOM (manual load)
|
|
252
|
+
const existing = document.querySelector(`script[src="${src}"]`);
|
|
253
|
+
if (existing) {
|
|
254
|
+
if (existing.dataset.loaded === "true") {
|
|
255
|
+
resolve();
|
|
256
|
+
} else {
|
|
257
|
+
const prevOnload = existing.onload;
|
|
258
|
+
existing.onload = () => {
|
|
259
|
+
if (prevOnload) prevOnload();
|
|
260
|
+
existing.dataset.loaded = "true";
|
|
261
|
+
resolve();
|
|
262
|
+
};
|
|
263
|
+
existing.onerror = reject;
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
const s = document.createElement("script");
|
|
267
|
+
s.src = src;
|
|
268
|
+
if (integrity) {
|
|
269
|
+
s.integrity = integrity;
|
|
270
|
+
s.crossOrigin = crossOrigin || "anonymous";
|
|
271
|
+
}
|
|
272
|
+
s.onload = () => {
|
|
273
|
+
s.dataset.loaded = "true";
|
|
274
|
+
resolve();
|
|
275
|
+
};
|
|
276
|
+
s.onerror = reject;
|
|
277
|
+
document.head.appendChild(s);
|
|
283
278
|
}
|
|
284
|
-
s.onload = resolve;
|
|
285
|
-
s.onerror = reject;
|
|
286
|
-
document.head.appendChild(s);
|
|
287
279
|
});
|
|
280
|
+
scriptCache.set(src, scriptPromise);
|
|
288
281
|
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Initialize after load
|
|
292
|
-
executeGenericPlugin(el, pluginName);
|
|
293
282
|
|
|
294
|
-
|
|
295
|
-
|
|
283
|
+
try {
|
|
284
|
+
await scriptCache.get(src);
|
|
285
|
+
} catch (e) {
|
|
286
|
+
console.error(`Failed to load script: ${src}`, e);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
executeGenericPlugin(el, pluginName);
|
|
296
292
|
}
|
|
297
293
|
}
|
|
298
294
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
// Now looks for attributes like `quill=""` and `quill-theme=""`.
|
|
302
|
-
const prefix = pluginName.toLowerCase();
|
|
295
|
+
function executeGenericPlugin(el, name) {
|
|
296
|
+
const prefix = name.toLowerCase();
|
|
303
297
|
const mainAttr = el.getAttribute(prefix);
|
|
298
|
+
let rawData;
|
|
304
299
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if (!attr.name.startsWith(prefix + '-')) return;
|
|
311
|
-
const key = attr.name.substring(prefix.length + 1);
|
|
312
|
-
|
|
313
|
-
if (key.startsWith('$')) return;
|
|
300
|
+
try {
|
|
301
|
+
rawData = JSON.parse(mainAttr);
|
|
302
|
+
} catch(e) {
|
|
303
|
+
rawData = {};
|
|
304
|
+
}
|
|
314
305
|
|
|
315
|
-
|
|
316
|
-
|
|
306
|
+
// 1. Gather configuration from attributes
|
|
307
|
+
if (rawData && typeof rawData === 'object' && !Array.isArray(rawData)) {
|
|
308
|
+
Array.from(el.attributes).forEach(attr => {
|
|
309
|
+
if (!attr.name.startsWith(prefix + '-')) return;
|
|
310
|
+
let key = attr.name.substring(prefix.length + 1);
|
|
311
|
+
if (key.startsWith('$')) key = key.substring(1);
|
|
312
|
+
let val = attr.value;
|
|
313
|
+
try { val = JSON.parse(val); } catch (e) { }
|
|
314
|
+
rawData[key] = val;
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
317
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
let target = config;
|
|
321
|
-
for(let i=0; i<parts.length-1; i++) {
|
|
322
|
-
if (!target[parts[i]]) target[parts[i]] = {};
|
|
323
|
-
target = target[parts[i]];
|
|
324
|
-
}
|
|
325
|
-
target[parts[parts.length-1]] = val;
|
|
326
|
-
} else {
|
|
327
|
-
config[key] = val;
|
|
328
|
-
}
|
|
329
|
-
});
|
|
318
|
+
// 2. Resolve parameters (Token Resolver)
|
|
319
|
+
const resolved = processParams(el, rawData);
|
|
330
320
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
if (!Constructor) {
|
|
335
|
-
console.error(`Constructor for ${pluginName} not found on window.`);
|
|
321
|
+
const Target = window[name] || window[prefix];
|
|
322
|
+
if (!Target) {
|
|
323
|
+
console.error(`Constructor for ${name} not found on window.`);
|
|
336
324
|
return;
|
|
337
325
|
}
|
|
338
326
|
|
|
339
|
-
let instance
|
|
327
|
+
let instance;
|
|
340
328
|
try {
|
|
341
|
-
|
|
342
|
-
if (
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
329
|
+
// 3. Determine if we call as a function (Anime) or as a Constructor (Swiper)
|
|
330
|
+
if (typeof Target === 'function' && (!Target.prototype || name === 'Anime')) {
|
|
331
|
+
instance = Target(resolved);
|
|
332
|
+
} else {
|
|
333
|
+
if (Array.isArray(resolved)) {
|
|
334
|
+
instance = new Target(...resolved);
|
|
335
|
+
} else {
|
|
336
|
+
instance = new Target(resolved);
|
|
337
|
+
}
|
|
349
338
|
}
|
|
350
|
-
|
|
351
|
-
} catch(e) {
|
|
352
|
-
console.error(`
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
if (instance) el[pluginName] = instance;
|
|
357
|
-
else return;
|
|
358
|
-
|
|
359
|
-
// 3. OPERATOR PIPELINE
|
|
360
|
-
const opPipeline = [];
|
|
361
|
-
attributes.forEach(attr => {
|
|
362
|
-
if (!attr.name.startsWith(prefix + '-')) return;
|
|
363
|
-
let key = attr.name.substring(prefix.length + 1);
|
|
364
|
-
if (!key.startsWith('$')) return;
|
|
365
|
-
let val = attr.value;
|
|
366
|
-
try { val = JSON.parse(val); } catch(e) {}
|
|
367
|
-
let opName = key;
|
|
368
|
-
opPipeline.push(opName, [val]);
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
if (opPipeline.length > 0) {
|
|
372
|
-
await executePipeline(el, opPipeline, pluginName, instance, config);
|
|
339
|
+
console.log(`Initialized ${name}`);
|
|
340
|
+
} catch (e) {
|
|
341
|
+
console.error(`Error initializing ${name}:`, e);
|
|
373
342
|
}
|
|
343
|
+
if (instance) el[name] = instance;
|
|
374
344
|
}
|
|
375
345
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
if (opName.startsWith('$')) {
|
|
403
|
-
if (!currentCtx) {
|
|
404
|
-
if (element[pluginName]) currentCtx = element[pluginName];
|
|
405
|
-
if (!currentCtx) {
|
|
406
|
-
console.warn(`Context undefined for operator ${opName}. Skipping.`);
|
|
407
|
-
continue;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
let cleanName = opName.substring(1);
|
|
412
|
-
if (!cleanName.includes('.')) {
|
|
413
|
-
cleanName = cleanName.replace(/-/g, '');
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
let realKey = cleanName;
|
|
417
|
-
if (!cleanName.includes('.') && currentCtx && (typeof currentCtx === 'object' || typeof currentCtx === 'function')) {
|
|
418
|
-
try {
|
|
419
|
-
const keys = Object.keys(currentCtx.constructor?.prototype || currentCtx);
|
|
420
|
-
const found = keys.find(k => k.toLowerCase() === cleanName.toLowerCase());
|
|
421
|
-
if (found) realKey = found;
|
|
422
|
-
else {
|
|
423
|
-
const directFound = Object.keys(currentCtx).find(k => k.toLowerCase() === cleanName.toLowerCase());
|
|
424
|
-
if(directFound) realKey = directFound;
|
|
425
|
-
}
|
|
426
|
-
} catch (e) {}
|
|
346
|
+
/**
|
|
347
|
+
* Generic Parameter Processor
|
|
348
|
+
* Handles:
|
|
349
|
+
* - $this / $this.children
|
|
350
|
+
* - $window.path.to.function(arg)
|
|
351
|
+
* - $anime.stagger(100)
|
|
352
|
+
*/
|
|
353
|
+
function processParams(el, params) {
|
|
354
|
+
if (typeof params === 'string' && params.startsWith('$')) {
|
|
355
|
+
try {
|
|
356
|
+
// 1. Check for Method Call: $root.path.to.func(arg)
|
|
357
|
+
const callMatch = params.match(/^\$([^.]+)\.(.+)\((.*)\)$/);
|
|
358
|
+
if (callMatch) {
|
|
359
|
+
const [_, root, path, arg] = callMatch;
|
|
360
|
+
const obj = (root === 'this') ? el : window[root];
|
|
361
|
+
|
|
362
|
+
// If root object exists, drill down
|
|
363
|
+
if (obj) {
|
|
364
|
+
const func = path.split('.').reduce((o, k) => (o || {})[k], obj);
|
|
365
|
+
if (typeof func === 'function') {
|
|
366
|
+
// Parse argument if JSON-like, else string
|
|
367
|
+
const parsedArg = arg ? (function() { try { return JSON.parse(arg); } catch(e) { return arg; } })() : undefined;
|
|
368
|
+
return func(parsedArg);
|
|
369
|
+
}
|
|
427
370
|
}
|
|
428
|
-
opName = realKey;
|
|
429
371
|
}
|
|
430
|
-
|
|
431
|
-
const [parent, val] = resolvePath(i === 0 ? window : currentCtx, opName);
|
|
432
372
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
373
|
+
// 2. Check for Property Access: $root.path.to.prop or just $root
|
|
374
|
+
const propMatch = params.match(/^\$([^.]+)(?:\.(.+))?$/);
|
|
375
|
+
if (propMatch) {
|
|
376
|
+
const [_, root, path] = propMatch;
|
|
377
|
+
const obj = (root === 'this') ? el : window[root];
|
|
378
|
+
if (!obj) return params; // Return raw string if root not found
|
|
379
|
+
if (!path) return obj;
|
|
380
|
+
|
|
381
|
+
const val = path.split('.').reduce((o, k) => (o || {})[k], obj);
|
|
382
|
+
// Convert HTMLCollections to Arrays
|
|
383
|
+
return (val instanceof HTMLCollection) ? Array.from(val) : val;
|
|
436
384
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
currentCtx = val;
|
|
440
|
-
|
|
441
|
-
if (typeof currentCtx === 'function') {
|
|
442
|
-
let args = [];
|
|
443
|
-
if (i + 1 < pipelineArray.length && Array.isArray(pipelineArray[i + 1])) {
|
|
444
|
-
args = processParams(element, pipelineArray[i + 1], config, instance, pluginName);
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
try {
|
|
448
|
-
const finalArgs = (args.length === 1) ? [args[0]] : args;
|
|
449
|
-
try {
|
|
450
|
-
currentCtx = await currentCtx.apply(parentCtx, finalArgs);
|
|
451
|
-
} catch (err) {
|
|
452
|
-
currentCtx = new currentCtx(...finalArgs);
|
|
453
|
-
parentCtx = currentCtx;
|
|
454
|
-
}
|
|
455
|
-
} catch(e) {
|
|
456
|
-
console.error(`Exec error ${opName}`, e);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
return currentCtx;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
// --- HELPERS ---
|
|
465
|
-
function applyMapping(element, instance, def) {
|
|
466
|
-
if (def.includes('=')) {
|
|
467
|
-
const [lhs, rhs] = def.split('=').map(s => s.trim());
|
|
468
|
-
const pathRaw = rhs;
|
|
469
|
-
const match = lhs.match(/^[$]?([\w\.]+)(\(\))?$/);
|
|
470
|
-
if (!match) return;
|
|
471
|
-
const alias = match[1];
|
|
472
|
-
const isFunction = match[2] === '()';
|
|
473
|
-
|
|
474
|
-
if (isFunction) {
|
|
475
|
-
element[alias] = function(val) {
|
|
476
|
-
if (arguments.length > 0) return assignDeep(instance, pathRaw, val);
|
|
477
|
-
else return resolveDeep(instance, pathRaw);
|
|
478
|
-
};
|
|
479
|
-
} else {
|
|
480
|
-
Object.defineProperty(element, alias, {
|
|
481
|
-
get: () => resolveDeep(instance, pathRaw),
|
|
482
|
-
set: (v) => assignDeep(instance, pathRaw, v),
|
|
483
|
-
configurable: true
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
|
-
} else {
|
|
487
|
-
if (typeof instance[def] === 'function') {
|
|
488
|
-
element[def] = instance[def].bind(instance);
|
|
385
|
+
} catch (e) {
|
|
386
|
+
console.warn("Failed to resolve dynamic token:", params);
|
|
489
387
|
}
|
|
490
388
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
function processParams(element, params, config, instance, pluginName) {
|
|
494
|
-
if (params === "$this") return element;
|
|
495
|
-
if (params === "$config") return config;
|
|
496
|
-
if (params === "$root" || params === "$instance") return instance;
|
|
497
|
-
if (typeof params === 'string' && params === `$${pluginName}`) return instance;
|
|
498
|
-
if (typeof params === 'string' && params.startsWith('$this.')) return resolveDeep(element, params.substring(6));
|
|
499
|
-
if (Array.isArray(params)) return params.map(p => processParams(element, p, config, instance, pluginName));
|
|
389
|
+
|
|
390
|
+
if (Array.isArray(params)) return params.map(p => processParams(el, p));
|
|
500
391
|
if (typeof params === 'object' && params !== null) {
|
|
501
392
|
const res = {};
|
|
502
|
-
for(let k in params) res[k] = processParams(
|
|
393
|
+
for (let k in params) res[k] = processParams(el, params[k]);
|
|
503
394
|
return res;
|
|
504
395
|
}
|
|
505
396
|
return params;
|
|
506
397
|
}
|
|
507
398
|
|
|
508
|
-
function resolveDeep(obj, path) {
|
|
509
|
-
if (!obj || !path) return undefined;
|
|
510
|
-
return path.split('.').reduce((o, k) => (o || {})[k], obj);
|
|
511
|
-
}
|
|
512
|
-
function assignDeep(obj, path, val) {
|
|
513
|
-
if (!obj || !path) return;
|
|
514
|
-
const parts = path.split('.');
|
|
515
|
-
let target = obj;
|
|
516
|
-
for(let i=0; i<parts.length-1; i++) target = target[parts[i]];
|
|
517
|
-
const last = parts[parts.length-1];
|
|
518
|
-
if(typeof target[last] === 'function') target[last](val);
|
|
519
|
-
else target[last] = val;
|
|
520
|
-
}
|
|
521
|
-
function resolvePath(obj, path) {
|
|
522
|
-
if (!obj || !path) return [null, undefined];
|
|
523
|
-
const parts = path.split('.');
|
|
524
|
-
let current = obj;
|
|
525
|
-
let parent = null;
|
|
526
|
-
for (const part of parts) {
|
|
527
|
-
if (!current) return [null, undefined];
|
|
528
|
-
parent = current;
|
|
529
|
-
current = current[part];
|
|
530
|
-
}
|
|
531
|
-
return [parent, current];
|
|
532
|
-
}
|
|
533
|
-
|
|
534
399
|
// --- OBSERVER INTEGRATION ---
|
|
535
400
|
Observer.init({
|
|
536
401
|
name: "plugin",
|
|
@@ -540,4 +405,9 @@ Observer.init({
|
|
|
540
405
|
callback: (mutation) => {
|
|
541
406
|
init(mutation.target);
|
|
542
407
|
}
|
|
543
|
-
});
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// Auto-init for existing elements
|
|
411
|
+
if (typeof document !== 'undefined') {
|
|
412
|
+
init(document.querySelectorAll("[plugin]"));
|
|
413
|
+
}
|