@cocreate/plugins 1.0.2
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/.github/FUNDING.yml +3 -0
- package/.github/workflows/automated.yml +44 -0
- package/.github/workflows/manual.yml +44 -0
- package/CHANGELOG.md +27 -0
- package/CONTRIBUTING.md +96 -0
- package/CoCreate.config.js +23 -0
- package/LICENSE +683 -0
- package/README.md +92 -0
- package/demo/index.html +12 -0
- package/docs/index.html +252 -0
- package/package.json +41 -0
- package/prettier.config.js +16 -0
- package/release.config.js +22 -0
- package/src/index.js +543 -0
- package/webpack.config.js +65 -0
package/src/index.js
ADDED
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
import Observer from "@cocreate/observer";
|
|
2
|
+
|
|
3
|
+
// --- CONFIGURATION ---
|
|
4
|
+
// SECURITY: STRICT ALLOWLIST
|
|
5
|
+
const plugins = {
|
|
6
|
+
Toastify: {
|
|
7
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/toastify-js", crossOrigin: "anonymous" }],
|
|
8
|
+
css: ["https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css"]
|
|
9
|
+
// attribute prefix: toastify-
|
|
10
|
+
},
|
|
11
|
+
Choices: {
|
|
12
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js", crossOrigin: "anonymous" }],
|
|
13
|
+
css: ["https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css"]
|
|
14
|
+
// attribute prefix: choices-
|
|
15
|
+
},
|
|
16
|
+
flatpickr: {
|
|
17
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/flatpickr", crossOrigin: "anonymous" }],
|
|
18
|
+
css: ["https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css"]
|
|
19
|
+
// attribute prefix: flatpickr-
|
|
20
|
+
},
|
|
21
|
+
Quill: {
|
|
22
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.min.js", crossOrigin: "anonymous" }],
|
|
23
|
+
css: [
|
|
24
|
+
"https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.core.css",
|
|
25
|
+
"https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.snow.css",
|
|
26
|
+
"https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.bubble.css"
|
|
27
|
+
]
|
|
28
|
+
// attribute prefix: quill-
|
|
29
|
+
},
|
|
30
|
+
ClassicEditor: {
|
|
31
|
+
js: [{ src: "https://cdn.ckeditor.com/ckeditor5/41.2.0/classic/ckeditor.js", crossOrigin: "anonymous" }]
|
|
32
|
+
// attribute prefix: classiceditor-
|
|
33
|
+
},
|
|
34
|
+
Dropzone: {
|
|
35
|
+
js: [{ src: "https://unpkg.com/dropzone@5/dist/min/dropzone.min.js", crossOrigin: "anonymous" }],
|
|
36
|
+
css: ["https://unpkg.com/dropzone@5/dist/min/dropzone.min.css"]
|
|
37
|
+
// attribute prefix: dropzone-
|
|
38
|
+
},
|
|
39
|
+
SimpleBar: {
|
|
40
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/simplebar@latest/dist/simplebar.min.js", crossOrigin: "anonymous" }],
|
|
41
|
+
css: ["https://cdn.jsdelivr.net/npm/simplebar@latest/dist/simplebar.css"]
|
|
42
|
+
// attribute prefix: simplebar-
|
|
43
|
+
},
|
|
44
|
+
GLightbox: {
|
|
45
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/glightbox/dist/js/glightbox.min.js", crossOrigin: "anonymous" }],
|
|
46
|
+
css: ["https://cdn.jsdelivr.net/npm/glightbox/dist/css/glightbox.min.css"]
|
|
47
|
+
// attribute prefix: glightbox-
|
|
48
|
+
},
|
|
49
|
+
FgEmojiPicker: {
|
|
50
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/fg-emoji-picker/fgEmojiPicker.js", crossOrigin: "anonymous" }]
|
|
51
|
+
// attribute prefix: fgemojipicker-
|
|
52
|
+
},
|
|
53
|
+
bootstrap: {
|
|
54
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js", crossOrigin: "anonymous" }]
|
|
55
|
+
// attribute prefix: bootstrap-
|
|
56
|
+
},
|
|
57
|
+
Waves: {
|
|
58
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/node-waves/dist/waves.min.js", crossOrigin: "anonymous" }],
|
|
59
|
+
css: ["https://cdn.jsdelivr.net/npm/node-waves/dist/waves.min.css"]
|
|
60
|
+
// attribute prefix: waves-
|
|
61
|
+
},
|
|
62
|
+
feather: {
|
|
63
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js", crossOrigin: "anonymous" }]
|
|
64
|
+
// attribute prefix: feather-
|
|
65
|
+
},
|
|
66
|
+
ApexCharts: {
|
|
67
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/apexcharts", crossOrigin: "anonymous" }]
|
|
68
|
+
// attribute prefix: apexcharts-
|
|
69
|
+
},
|
|
70
|
+
jsVectorMap: {
|
|
71
|
+
js: [
|
|
72
|
+
{ src: "https://cdn.jsdelivr.net/npm/jsvectormap", crossOrigin: "anonymous" },
|
|
73
|
+
{ src: "https://cdn.jsdelivr.net/npm/jsvectormap/dist/maps/world.js", crossOrigin: "anonymous" }
|
|
74
|
+
],
|
|
75
|
+
css: ["https://cdn.jsdelivr.net/npm/jsvectormap/dist/css/jsvectormap.min.css"]
|
|
76
|
+
// attribute prefix: jsvectormap-
|
|
77
|
+
},
|
|
78
|
+
Swiper: {
|
|
79
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js", crossOrigin: "anonymous" }],
|
|
80
|
+
css: ["https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css"]
|
|
81
|
+
// attribute prefix: swiper-
|
|
82
|
+
},
|
|
83
|
+
List: {
|
|
84
|
+
js: [
|
|
85
|
+
{ src: "https://cdnjs.cloudflare.com/ajax/libs/list.js/2.3.1/list.min.js", crossOrigin: "anonymous" },
|
|
86
|
+
{ src: "https://cdnjs.cloudflare.com/ajax/libs/list.pagination.js/0.1.1/list.pagination.min.js", crossOrigin: "anonymous" }
|
|
87
|
+
]
|
|
88
|
+
// attribute prefix: list-
|
|
89
|
+
},
|
|
90
|
+
Swal: {
|
|
91
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/sweetalert2@11", crossOrigin: "anonymous" }],
|
|
92
|
+
css: ["https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css"]
|
|
93
|
+
// attribute prefix: swal-
|
|
94
|
+
},
|
|
95
|
+
FullCalendar: {
|
|
96
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/fullcalendar@6.1.15/index.global.min.js", crossOrigin: "anonymous" }]
|
|
97
|
+
// attribute prefix: fullcalendar-
|
|
98
|
+
},
|
|
99
|
+
Cleave: {
|
|
100
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/cleave.js/dist/cleave.min.js", crossOrigin: "anonymous" }]
|
|
101
|
+
// attribute prefix: cleave-
|
|
102
|
+
},
|
|
103
|
+
noUiSlider: {
|
|
104
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/nouislider/dist/nouislider.min.js", crossOrigin: "anonymous" }],
|
|
105
|
+
css: ["https://cdn.jsdelivr.net/npm/nouislider/dist/nouislider.min.css"]
|
|
106
|
+
// attribute prefix: nouislider-
|
|
107
|
+
},
|
|
108
|
+
wNumb: {
|
|
109
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/wnumb/wNumb.min.js", crossOrigin: "anonymous" }]
|
|
110
|
+
// attribute prefix: wnumb-
|
|
111
|
+
},
|
|
112
|
+
Grid: {
|
|
113
|
+
js: [
|
|
114
|
+
{ src: "https://unpkg.com/gridjs/dist/gridjs.umd.js", crossOrigin: "anonymous" },
|
|
115
|
+
{ src: "https://unpkg.com/gridjs/plugins/selection/dist/selection.umd.js", crossOrigin: "anonymous" }
|
|
116
|
+
],
|
|
117
|
+
css: ["https://unpkg.com/gridjs/dist/theme/mermaid.min.css"]
|
|
118
|
+
// attribute prefix: grid-
|
|
119
|
+
},
|
|
120
|
+
FilePond: {
|
|
121
|
+
js: [
|
|
122
|
+
{ src: "https://unpkg.com/filepond/dist/filepond.min.js", crossOrigin: "anonymous" },
|
|
123
|
+
{ src: "https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.js", crossOrigin: "anonymous" },
|
|
124
|
+
{ src: "https://unpkg.com/filepond-plugin-file-validate-size/dist/filepond-plugin-file-validate-size.min.js", crossOrigin: "anonymous" },
|
|
125
|
+
{ src: "https://unpkg.com/filepond-plugin-image-exif-orientation/dist/filepond-plugin-image-exif-orientation.min.js", crossOrigin: "anonymous" },
|
|
126
|
+
{ src: "https://unpkg.com/filepond-plugin-file-encode/dist/filepond-plugin-file-encode.min.js", crossOrigin: "anonymous" }
|
|
127
|
+
],
|
|
128
|
+
css: [
|
|
129
|
+
"https://unpkg.com/filepond/dist/filepond.min.css",
|
|
130
|
+
"https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css"
|
|
131
|
+
]
|
|
132
|
+
// attribute prefix: filepond-
|
|
133
|
+
},
|
|
134
|
+
Prism: {
|
|
135
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/prismjs/prism.min.js", crossOrigin: "anonymous" }],
|
|
136
|
+
css: ["https://cdn.jsdelivr.net/npm/prismjs/themes/prism.min.css"]
|
|
137
|
+
// attribute prefix: prism-
|
|
138
|
+
},
|
|
139
|
+
Isotope: {
|
|
140
|
+
js: [{ src: "https://unpkg.com/isotope-layout@3/dist/isotope.pkgd.min.js", crossOrigin: "anonymous" }]
|
|
141
|
+
// attribute prefix: isotope-
|
|
142
|
+
},
|
|
143
|
+
particlesJS: {
|
|
144
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/particles.js@2.0.0/particles.min.js", crossOrigin: "anonymous" }]
|
|
145
|
+
// attribute prefix: particlesjs-
|
|
146
|
+
},
|
|
147
|
+
dragula: {
|
|
148
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/dragula/dist/dragula.min.js", crossOrigin: "anonymous" }],
|
|
149
|
+
css: ["https://cdn.jsdelivr.net/npm/dragula/dist/dragula.min.css"]
|
|
150
|
+
// attribute prefix: dragula-
|
|
151
|
+
},
|
|
152
|
+
DomAutoscroller: {
|
|
153
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/dom-autoscroller", crossOrigin: "anonymous" }]
|
|
154
|
+
// attribute prefix: domautoscroller-
|
|
155
|
+
},
|
|
156
|
+
Card: {
|
|
157
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/card/dist/card.js", crossOrigin: "anonymous" }]
|
|
158
|
+
// attribute prefix: card-
|
|
159
|
+
},
|
|
160
|
+
Chart: {
|
|
161
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/chart.js", crossOrigin: "anonymous" }]
|
|
162
|
+
// attribute prefix: chart-
|
|
163
|
+
},
|
|
164
|
+
echarts: {
|
|
165
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js", crossOrigin: "anonymous" }]
|
|
166
|
+
// attribute prefix: echarts-
|
|
167
|
+
},
|
|
168
|
+
Multi: {
|
|
169
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/multi.js/dist/multi.min.js", crossOrigin: "anonymous" }],
|
|
170
|
+
css: ["https://cdn.jsdelivr.net/npm/multi.js/dist/multi.min.css"]
|
|
171
|
+
// attribute prefix: multi-
|
|
172
|
+
},
|
|
173
|
+
autoComplete: {
|
|
174
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.7/dist/autoComplete.min.js", crossOrigin: "anonymous" }],
|
|
175
|
+
css: ["https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.7/dist/css/autoComplete.01.min.css"]
|
|
176
|
+
// attribute prefix: autocomplete-
|
|
177
|
+
},
|
|
178
|
+
Pickr: {
|
|
179
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/pickr.min.js", crossOrigin: "anonymous" }],
|
|
180
|
+
css: ["https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/themes/classic.min.css"]
|
|
181
|
+
// attribute prefix: pickr-
|
|
182
|
+
},
|
|
183
|
+
Shepherd: {
|
|
184
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/shepherd.js/dist/js/shepherd.min.js", crossOrigin: "anonymous" }],
|
|
185
|
+
css: ["https://cdn.jsdelivr.net/npm/shepherd.js/dist/css/shepherd.css"]
|
|
186
|
+
// attribute prefix: shepherd-
|
|
187
|
+
},
|
|
188
|
+
GMaps: {
|
|
189
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/gmaps/gmaps.min.js", crossOrigin: "anonymous" }]
|
|
190
|
+
// attribute prefix: gmaps-
|
|
191
|
+
},
|
|
192
|
+
L: {
|
|
193
|
+
js: [{ src: "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js", crossOrigin: "anonymous" }],
|
|
194
|
+
css: ["https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"]
|
|
195
|
+
// attribute prefix: l-
|
|
196
|
+
},
|
|
197
|
+
Masonry: {
|
|
198
|
+
js: [{ src: "https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js", crossOrigin: "anonymous" }]
|
|
199
|
+
// attribute prefix: masonry-
|
|
200
|
+
},
|
|
201
|
+
Rater: {
|
|
202
|
+
js: [{ src: "https://cdn.jsdelivr.net/npm/rater-js/index.js", crossOrigin: "anonymous" }]
|
|
203
|
+
// attribute prefix: rater-
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// --- CORE ENGINE ---
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Global Initialization Function
|
|
211
|
+
* Processes one or more elements to attach plugins.
|
|
212
|
+
* @param {HTMLElement|NodeList|Array} elements - Element, or Collection of Elements
|
|
213
|
+
*/
|
|
214
|
+
export function init(elements) {
|
|
215
|
+
if (!elements) return;
|
|
216
|
+
|
|
217
|
+
// 1. Detect Type & Normalize to Array
|
|
218
|
+
// Normalize string handling (removed), Element, NodeList, HTMLCollection, Array
|
|
219
|
+
let collection = [];
|
|
220
|
+
|
|
221
|
+
if (elements instanceof HTMLElement || elements instanceof Element) {
|
|
222
|
+
collection = [elements];
|
|
223
|
+
} else if (elements.length !== undefined && typeof elements !== 'function') {
|
|
224
|
+
// Handle Array-like objects (NodeList, HTMLCollection, Array)
|
|
225
|
+
collection = Array.from(elements);
|
|
226
|
+
} else {
|
|
227
|
+
// Fallback for single object that isn't element (unlikely given spec, but safe)
|
|
228
|
+
collection = [elements];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// 2. Iterate and Process
|
|
232
|
+
collection.forEach(el => {
|
|
233
|
+
// Ensure valid element before processing
|
|
234
|
+
if (el && typeof el.getAttribute === 'function') {
|
|
235
|
+
processPlugin(el);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Auto-Init on load
|
|
241
|
+
if (typeof document !== 'undefined') {
|
|
242
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
243
|
+
init(document.querySelectorAll("[plugin]"));
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function processPlugin(el) {
|
|
248
|
+
const rawAttr = el.getAttribute("plugin");
|
|
249
|
+
if (!rawAttr) return;
|
|
250
|
+
|
|
251
|
+
const pluginName = rawAttr.split(',')[0].trim();
|
|
252
|
+
|
|
253
|
+
// SECURITY CHECK:
|
|
254
|
+
// Look up the definition in our hardcoded allowlist.
|
|
255
|
+
const pluginDef = plugins[pluginName];
|
|
256
|
+
|
|
257
|
+
if (pluginDef) {
|
|
258
|
+
console.log(`[Plugin System] Loading ${pluginName}...`);
|
|
259
|
+
|
|
260
|
+
// Load CSS
|
|
261
|
+
if(pluginDef.css) pluginDef.css.forEach(href => {
|
|
262
|
+
if (!document.querySelector(`link[href="${href}"]`)) {
|
|
263
|
+
const link = document.createElement("link");
|
|
264
|
+
link.rel="stylesheet"; link.href=href; document.head.appendChild(link);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// Load JS with Integrity Checks
|
|
269
|
+
if(pluginDef.js) {
|
|
270
|
+
for(const item of pluginDef.js) {
|
|
271
|
+
const src = typeof item === 'string' ? item : item.src;
|
|
272
|
+
const integrity = typeof item === 'object' ? item.integrity : null;
|
|
273
|
+
const crossOrigin = typeof item === 'object' ? item.crossOrigin : null;
|
|
274
|
+
|
|
275
|
+
if (document.querySelector(`script[src="${src}"]`)) continue;
|
|
276
|
+
|
|
277
|
+
await new Promise((resolve, reject) => {
|
|
278
|
+
const s = document.createElement("script");
|
|
279
|
+
s.src = src;
|
|
280
|
+
if (integrity) {
|
|
281
|
+
s.integrity = integrity;
|
|
282
|
+
s.crossOrigin = crossOrigin || "anonymous";
|
|
283
|
+
}
|
|
284
|
+
s.onload = resolve;
|
|
285
|
+
s.onerror = reject;
|
|
286
|
+
document.head.appendChild(s);
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Initialize after load
|
|
292
|
+
executeGenericPlugin(el, pluginName);
|
|
293
|
+
|
|
294
|
+
} else {
|
|
295
|
+
console.warn(`[Plugin System] Blocked: Plugin "${pluginName}" is not in the allowlist.`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async function executeGenericPlugin(el, pluginName) {
|
|
300
|
+
// MODIFIED: Removed 'data-' prefix.
|
|
301
|
+
// Now looks for attributes like `quill=""` and `quill-theme=""`.
|
|
302
|
+
const prefix = pluginName.toLowerCase();
|
|
303
|
+
const mainAttr = el.getAttribute(prefix);
|
|
304
|
+
|
|
305
|
+
// 1. CONFIG GATHERING
|
|
306
|
+
const config = {};
|
|
307
|
+
const attributes = Array.from(el.attributes);
|
|
308
|
+
|
|
309
|
+
attributes.forEach(attr => {
|
|
310
|
+
if (!attr.name.startsWith(prefix + '-')) return;
|
|
311
|
+
const key = attr.name.substring(prefix.length + 1);
|
|
312
|
+
|
|
313
|
+
if (key.startsWith('$')) return;
|
|
314
|
+
|
|
315
|
+
let val = attr.value;
|
|
316
|
+
try { val = JSON.parse(val); } catch(e) {}
|
|
317
|
+
|
|
318
|
+
if (key.includes('-')) {
|
|
319
|
+
const parts = key.split('-');
|
|
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
|
+
});
|
|
330
|
+
|
|
331
|
+
// 2. INSTANTIATION
|
|
332
|
+
const Constructor = window[pluginName];
|
|
333
|
+
|
|
334
|
+
if (!Constructor) {
|
|
335
|
+
console.error(`Constructor for ${pluginName} not found on window.`);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
let instance = null;
|
|
340
|
+
try {
|
|
341
|
+
let args = [el, config];
|
|
342
|
+
if (mainAttr) {
|
|
343
|
+
try {
|
|
344
|
+
const json = JSON.parse(mainAttr);
|
|
345
|
+
if (Array.isArray(json)) {
|
|
346
|
+
args = processParams(el, json, config, null, pluginName);
|
|
347
|
+
}
|
|
348
|
+
} catch(e) {}
|
|
349
|
+
}
|
|
350
|
+
instance = new Constructor(...args);
|
|
351
|
+
} catch(e) {
|
|
352
|
+
console.error(`Init failed for ${pluginName}`, e);
|
|
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);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
async function executePipeline(element, pipelineArray, pluginName, currentCtx, config = {}) {
|
|
377
|
+
let parentCtx = window;
|
|
378
|
+
const instance = element[pluginName] || currentCtx;
|
|
379
|
+
|
|
380
|
+
for (let i = 0; i < pipelineArray.length; i++) {
|
|
381
|
+
const item = pipelineArray[i];
|
|
382
|
+
if (Array.isArray(item)) continue;
|
|
383
|
+
|
|
384
|
+
if (typeof item === 'string') {
|
|
385
|
+
let opName = item;
|
|
386
|
+
|
|
387
|
+
if ((typeof currentCtx === 'undefined' || currentCtx === null) && instance) {
|
|
388
|
+
currentCtx = instance;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (opName.toLowerCase() === '$this') {
|
|
392
|
+
const args = (i + 1 < pipelineArray.length && Array.isArray(pipelineArray[i + 1])) ? pipelineArray[i + 1] : [];
|
|
393
|
+
const mapDef = args[0];
|
|
394
|
+
if (Array.isArray(mapDef)) {
|
|
395
|
+
mapDef.forEach(def => {
|
|
396
|
+
applyMapping(element, instance, def);
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
continue;
|
|
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) {}
|
|
427
|
+
}
|
|
428
|
+
opName = realKey;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const [parent, val] = resolvePath(i === 0 ? window : currentCtx, opName);
|
|
432
|
+
|
|
433
|
+
if (typeof val === 'undefined') {
|
|
434
|
+
console.warn(`Method/Prop "${opName}" not found on context.`);
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
parentCtx = parent;
|
|
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);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
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));
|
|
500
|
+
if (typeof params === 'object' && params !== null) {
|
|
501
|
+
const res = {};
|
|
502
|
+
for(let k in params) res[k] = processParams(element, params[k], config, instance, pluginName);
|
|
503
|
+
return res;
|
|
504
|
+
}
|
|
505
|
+
return params;
|
|
506
|
+
}
|
|
507
|
+
|
|
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
|
+
// --- OBSERVER INTEGRATION ---
|
|
535
|
+
Observer.init({
|
|
536
|
+
name: "plugin",
|
|
537
|
+
types: ["addedNodes", "attributes"],
|
|
538
|
+
selector: "[plugin]",
|
|
539
|
+
attributeFilter: ["plugin"],
|
|
540
|
+
callback: (mutation) => {
|
|
541
|
+
init(mutation.target);
|
|
542
|
+
}
|
|
543
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
|
3
|
+
const { EsbuildPlugin } = require("esbuild-loader");
|
|
4
|
+
const { FileUploader } = require("@cocreate/webpack");
|
|
5
|
+
|
|
6
|
+
module.exports = async (env, argv) => {
|
|
7
|
+
const isProduction = argv && argv.mode === "production";
|
|
8
|
+
const config = {
|
|
9
|
+
entry: {
|
|
10
|
+
"CoCreate-plugins": "./src/index.js"
|
|
11
|
+
},
|
|
12
|
+
output: {
|
|
13
|
+
path: path.resolve(__dirname, "dist"),
|
|
14
|
+
filename: isProduction ? "[name].min.js" : "[name].js",
|
|
15
|
+
libraryExport: "default",
|
|
16
|
+
library: ["CoCreate", "plugins"],
|
|
17
|
+
clean: true
|
|
18
|
+
},
|
|
19
|
+
plugins: [
|
|
20
|
+
new MiniCssExtractPlugin({
|
|
21
|
+
filename: isProduction ? "[name].min.css" : "[name].css"
|
|
22
|
+
}),
|
|
23
|
+
new FileUploader(env, argv)
|
|
24
|
+
],
|
|
25
|
+
mode: isProduction ? "production" : "development",
|
|
26
|
+
devtool: isProduction ? "source-map" : "eval-source-map",
|
|
27
|
+
module: {
|
|
28
|
+
rules: [
|
|
29
|
+
{
|
|
30
|
+
test: /.js$/,
|
|
31
|
+
exclude: /node_modules/,
|
|
32
|
+
use: {
|
|
33
|
+
loader: "esbuild-loader",
|
|
34
|
+
options: {
|
|
35
|
+
loader: "js",
|
|
36
|
+
target: "es2017"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
test: /.css$/i,
|
|
42
|
+
use: [MiniCssExtractPlugin.loader, "css-loader"]
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
optimization: {
|
|
47
|
+
minimize: isProduction,
|
|
48
|
+
minimizer: [
|
|
49
|
+
new EsbuildPlugin({
|
|
50
|
+
target: "es2017",
|
|
51
|
+
css: true
|
|
52
|
+
})
|
|
53
|
+
],
|
|
54
|
+
splitChunks: {
|
|
55
|
+
pluginsGroups: {
|
|
56
|
+
defaultVendors: false
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
performance: {
|
|
61
|
+
hints: isProduction ? "warning" : false
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
return config;
|
|
65
|
+
};
|