@cocreate/plugins 1.1.1 → 1.2.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 +13 -0
- package/demo/index.html +228 -10
- package/package.json +1 -1
- package/src/index.js +294 -288
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
# [1.2.0](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.1.1...v1.2.0) (2026-02-15)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* improve key validation in executeGenericPlugin function ([447f6d2](https://github.com/CoCreate-app/CoCreate-plugins/commit/447f6d209adea010110c3bc52a17f8b45cb302ea))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* enhance demo page with improved layout and Bootstrap integration ([a74615e](https://github.com/CoCreate-app/CoCreate-plugins/commit/a74615e27be7c2f40529c5242e6c6e9b839ad020))
|
|
12
|
+
* refactor plugin management and enhance dynamic loading capabilities ([4d91958](https://github.com/CoCreate-app/CoCreate-plugins/commit/4d919582934418cfb7bfa9497b6d0dcaf3bf8d10))
|
|
13
|
+
|
|
1
14
|
## [1.1.1](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.1.0...v1.1.1) (2026-02-09)
|
|
2
15
|
|
|
3
16
|
|
package/demo/index.html
CHANGED
|
@@ -1,12 +1,230 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
<head>
|
|
4
|
+
<title>Plugins API Demo</title>
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
7
|
+
<link rel="manifest" href="/manifest.webmanifest" />
|
|
8
|
+
|
|
9
|
+
<!-- Bootstrap 5 CSS for Demo Presentation -->
|
|
10
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
11
|
+
|
|
12
|
+
<style>
|
|
13
|
+
body { background-color: #f8f9fa; padding-bottom: 50px; }
|
|
14
|
+
.demo-card { margin-bottom: 2rem; border: none; box-shadow: 0 2px 4px rgba(0,0,0,0.05); }
|
|
15
|
+
.card-header { background-color: #fff; border-bottom: 1px solid #eee; padding: 1.5rem; }
|
|
16
|
+
.operator-badge { font-family: monospace; background: #e9ecef; color: #d63384; padding: 2px 6px; border-radius: 4px; }
|
|
17
|
+
</style>
|
|
18
|
+
</head>
|
|
19
|
+
<body>
|
|
20
|
+
|
|
21
|
+
<div class="container py-5">
|
|
22
|
+
<div class="row mb-4">
|
|
23
|
+
<div class="col-12 text-center">
|
|
24
|
+
<h1 class="display-5 fw-bold text-primary">CoCreate Plugins API</h1>
|
|
25
|
+
<p class="lead text-muted">A declarative, attribute-based system for initializing and configuring JavaScript libraries.</p>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<!-- SECTION 1: THE BASICS -->
|
|
30
|
+
<div class="row">
|
|
31
|
+
<div class="col-12">
|
|
32
|
+
<div class="card demo-card">
|
|
33
|
+
<div class="card-header">
|
|
34
|
+
<h4 class="m-0">1. The Basics: Declarative Initialization</h4>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="card-body">
|
|
37
|
+
<p>Initialize any supported plugin using the <code>plugin="PluginName"</code> attribute. Configure it using the lowercase attribute matching the plugin name (e.g., <code>pluginname="{ options }"</code>).</p>
|
|
38
|
+
|
|
39
|
+
<div class="row align-items-center">
|
|
40
|
+
<div class="col-md-6">
|
|
41
|
+
<h6 class="text-muted text-uppercase fs-7 fw-bold mb-3">Live Demo</h6>
|
|
42
|
+
<!-- Toastify Example -->
|
|
43
|
+
<button
|
|
44
|
+
type="button"
|
|
45
|
+
class="btn btn-success"
|
|
46
|
+
plugin="Toastify"
|
|
47
|
+
toastify='{
|
|
48
|
+
"text": "Hello! I am a declarative toast.",
|
|
49
|
+
"duration": 3000,
|
|
50
|
+
"gravity": "top",
|
|
51
|
+
"position": "right",
|
|
52
|
+
"style": { "background": "#198754" }
|
|
53
|
+
}'
|
|
54
|
+
onclick="this.Toastify.showToast()">
|
|
55
|
+
Click me to Show Toast
|
|
56
|
+
</button>
|
|
57
|
+
</div>
|
|
58
|
+
<div class="col-md-6">
|
|
59
|
+
<h6 class="text-muted text-uppercase fs-7 fw-bold mb-3">Code</h6>
|
|
60
|
+
<pre><code class="language-html"><button
|
|
61
|
+
plugin="Toastify"
|
|
62
|
+
toastify='{
|
|
63
|
+
"text": "Hello!",
|
|
64
|
+
"duration": 3000,
|
|
65
|
+
"style": { "background": "#198754" }
|
|
66
|
+
}'
|
|
67
|
+
onclick="this.Toastify.showToast()">
|
|
68
|
+
Click Me
|
|
69
|
+
</button></code></pre>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<!-- SECTION 2: OPERATORS & VARIABLES -->
|
|
78
|
+
<div class="row">
|
|
79
|
+
<div class="col-12">
|
|
80
|
+
<div class="card demo-card">
|
|
81
|
+
<div class="card-header">
|
|
82
|
+
<h4 class="m-0">2. Variable Operators</h4>
|
|
83
|
+
</div>
|
|
84
|
+
<div class="card-body">
|
|
85
|
+
<p>The API supports special variables to reference DOM elements and global objects dynamically within your JSON configuration.</p>
|
|
86
|
+
|
|
87
|
+
<div class="table-responsive mb-4">
|
|
88
|
+
<table class="table table-bordered">
|
|
89
|
+
<thead class="table-light">
|
|
90
|
+
<tr>
|
|
91
|
+
<th>Operator</th>
|
|
92
|
+
<th>Description</th>
|
|
93
|
+
<th>Usage Example</th>
|
|
94
|
+
</tr>
|
|
95
|
+
</thead>
|
|
96
|
+
<tbody>
|
|
97
|
+
<tr>
|
|
98
|
+
<td><span class="operator-badge">$this</span></td>
|
|
99
|
+
<td>References the current DOM element where the attribute is placed.</td>
|
|
100
|
+
<td><code>"element": "$this"</code></td>
|
|
101
|
+
</tr>
|
|
102
|
+
<tr>
|
|
103
|
+
<td><span class="operator-badge">$window</span></td>
|
|
104
|
+
<td>Accesses the global <code>window</code> object (useful for callbacks).</td>
|
|
105
|
+
<td><code>"callback": "$window.console.log"</code></td>
|
|
106
|
+
</tr>
|
|
107
|
+
</tbody>
|
|
108
|
+
</table>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<!-- RaterJs Example ($this) -->
|
|
112
|
+
<div class="border rounded p-3 bg-light mb-3">
|
|
113
|
+
<h5 class="mb-3">Example: Using <code>$this</code> for Element Binding</h5>
|
|
114
|
+
<div class="row">
|
|
115
|
+
<div class="col-md-6">
|
|
116
|
+
<!-- RaterJs Implementation -->
|
|
117
|
+
<div dir="ltr"
|
|
118
|
+
plugin="raterJs"
|
|
119
|
+
raterjs='[{
|
|
120
|
+
"element": "$this",
|
|
121
|
+
"starSize": 32,
|
|
122
|
+
"rating": 3.5,
|
|
123
|
+
"step": 0.5
|
|
124
|
+
}]'>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
<div class="col-md-6">
|
|
128
|
+
<pre><code class="language-html"><div
|
|
129
|
+
plugin="raterJs"
|
|
130
|
+
raterjs='[{
|
|
131
|
+
"element": "$this",
|
|
132
|
+
"starSize": 32,
|
|
133
|
+
"rating": 3.5
|
|
134
|
+
}]'>
|
|
135
|
+
</div></code></pre>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<!-- SECTION 3: CONFIGURATION PATTERNS -->
|
|
146
|
+
<div class="row">
|
|
147
|
+
<div class="col-12">
|
|
148
|
+
<div class="card demo-card">
|
|
149
|
+
<div class="card-header">
|
|
150
|
+
<h4 class="m-0">3. Configuration Patterns (Array vs. Object)</h4>
|
|
151
|
+
</div>
|
|
152
|
+
<div class="card-body">
|
|
153
|
+
<p>Plugins have different constructor signatures. The API handles both by inspecting the JSON structure.</p>
|
|
154
|
+
|
|
155
|
+
<div class="row">
|
|
156
|
+
<div class="col-md-6">
|
|
157
|
+
<div class="card h-100 bg-light border-0">
|
|
158
|
+
<div class="card-body">
|
|
159
|
+
<h6 class="fw-bold">Pattern A: Single Config Object</h6>
|
|
160
|
+
<p class="small text-muted">Used when the plugin accepts a single object containing all settings (e.g., <code>raterJs({ element: div })</code>).</p>
|
|
161
|
+
<hr>
|
|
162
|
+
<code>config='[{ "element": "$this", "opt": "val" }]'</code>
|
|
163
|
+
<br><small class="text-danger">*Note: Enclosed in an array to represent arguments list.</small>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
<div class="col-md-6">
|
|
168
|
+
<div class="card h-100 bg-light border-0">
|
|
169
|
+
<div class="card-body">
|
|
170
|
+
<h6 class="fw-bold">Pattern B: Argument List</h6>
|
|
171
|
+
<p class="small text-muted">Used when the plugin accepts multiple distinct arguments (e.g., <code>Swiper(element, options)</code>).</p>
|
|
172
|
+
<hr>
|
|
173
|
+
<code>config='["$this", { "opt": "val" }]'</code>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
<!-- SECTION 4: ADVANCED (Callbacks) -->
|
|
184
|
+
<div class="row">
|
|
185
|
+
<div class="col-12">
|
|
186
|
+
<div class="card demo-card">
|
|
187
|
+
<div class="card-header">
|
|
188
|
+
<h4 class="m-0">4. Advanced: Callbacks & Global Functions</h4>
|
|
189
|
+
</div>
|
|
190
|
+
<div class="card-body">
|
|
191
|
+
<p>Use <code>$window</code> or specific global names (like <code>$Swal</code>) to pass functions into configuration attributes.</p>
|
|
192
|
+
|
|
193
|
+
<div class="row align-items-center">
|
|
194
|
+
<div class="col-md-6">
|
|
195
|
+
<!-- SweetAlert Example -->
|
|
196
|
+
<button
|
|
197
|
+
type="button"
|
|
198
|
+
class="btn btn-primary"
|
|
199
|
+
onclick="Swal.fire({
|
|
200
|
+
title: 'Are you sure?',
|
|
201
|
+
text: 'You can define this entire config in HTML!',
|
|
202
|
+
icon: 'warning',
|
|
203
|
+
showCancelButton: true,
|
|
204
|
+
confirmButtonText: 'Yes, delete it!'
|
|
205
|
+
})">
|
|
206
|
+
Trigger SweetAlert
|
|
207
|
+
</button>
|
|
208
|
+
</div>
|
|
209
|
+
<div class="col-md-6">
|
|
210
|
+
<pre><code class="language-html"><button
|
|
211
|
+
onclick="Swal.fire({
|
|
212
|
+
title: 'Ready?',
|
|
213
|
+
preConfirm: '$window.myCustomFunction'
|
|
214
|
+
}) >
|
|
215
|
+
Launch
|
|
216
|
+
</button></code></pre>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<!-- CoCreate Engine -->
|
|
227
|
+
<script src="https://CoCreate.app/dist/CoCreate.js"></script>
|
|
228
|
+
|
|
229
|
+
</body>
|
|
230
|
+
</html>
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,184 +1,42 @@
|
|
|
1
1
|
import Observer from "@cocreate/observer";
|
|
2
|
+
import {dotNotationToObject} from "@cocreate/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {Object} PluginDefinition
|
|
6
|
+
* @property {Array<string|Object>} [js] - List of JS files to load. Can be strings (URLs) or objects with src, integrity, etc.
|
|
7
|
+
* @property {Array<string>} [css] - List of CSS files to load.
|
|
8
|
+
*/
|
|
2
9
|
|
|
3
10
|
// --- CONFIGURATION ---
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js", crossOrigin: "anonymous" }],
|
|
12
|
-
css: ["https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css"]
|
|
13
|
-
},
|
|
14
|
-
flatpickr: {
|
|
15
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/flatpickr", crossOrigin: "anonymous" }],
|
|
16
|
-
css: ["https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css"]
|
|
17
|
-
},
|
|
18
|
-
Quill: {
|
|
19
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.min.js", crossOrigin: "anonymous" }],
|
|
20
|
-
css: [
|
|
21
|
-
"https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.core.css",
|
|
22
|
-
"https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.snow.css",
|
|
23
|
-
"https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.bubble.css"
|
|
24
|
-
]
|
|
25
|
-
},
|
|
26
|
-
ClassicEditor: {
|
|
27
|
-
js: [{ src: "https://cdn.ckeditor.com/ckeditor5/41.2.0/classic/ckeditor.js", crossOrigin: "anonymous" }]
|
|
28
|
-
},
|
|
29
|
-
Dropzone: {
|
|
30
|
-
js: [{ src: "https://unpkg.com/dropzone@5/dist/min/dropzone.min.js", crossOrigin: "anonymous" }],
|
|
31
|
-
css: ["https://unpkg.com/dropzone@5/dist/min/dropzone.min.css"]
|
|
32
|
-
},
|
|
33
|
-
SimpleBar: {
|
|
34
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/simplebar@latest/dist/simplebar.min.js", crossOrigin: "anonymous" }],
|
|
35
|
-
css: ["https://cdn.jsdelivr.net/npm/simplebar@latest/dist/simplebar.css"]
|
|
36
|
-
},
|
|
37
|
-
GLightbox: {
|
|
38
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/glightbox/dist/js/glightbox.min.js", crossOrigin: "anonymous" }],
|
|
39
|
-
css: ["https://cdn.jsdelivr.net/npm/glightbox/dist/css/glightbox.min.css"]
|
|
40
|
-
},
|
|
41
|
-
FgEmojiPicker: {
|
|
42
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/fg-emoji-picker/fgEmojiPicker.js", crossOrigin: "anonymous" }]
|
|
43
|
-
},
|
|
44
|
-
bootstrap: {
|
|
45
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js", crossOrigin: "anonymous" }]
|
|
46
|
-
},
|
|
47
|
-
Waves: {
|
|
48
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/node-waves/dist/waves.min.js", crossOrigin: "anonymous" }],
|
|
49
|
-
css: ["https://cdn.jsdelivr.net/npm/node-waves/dist/waves.min.css"]
|
|
50
|
-
},
|
|
51
|
-
feather: {
|
|
52
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js", crossOrigin: "anonymous" }]
|
|
53
|
-
},
|
|
54
|
-
ApexCharts: {
|
|
55
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/apexcharts", crossOrigin: "anonymous" }]
|
|
56
|
-
},
|
|
57
|
-
jsVectorMap: {
|
|
58
|
-
js: [
|
|
59
|
-
{ src: "https://cdn.jsdelivr.net/npm/jsvectormap", crossOrigin: "anonymous" },
|
|
60
|
-
{ src: "https://cdn.jsdelivr.net/npm/jsvectormap/dist/maps/world.js", crossOrigin: "anonymous" }
|
|
61
|
-
],
|
|
62
|
-
css: ["https://cdn.jsdelivr.net/npm/jsvectormap/dist/css/jsvectormap.min.css"]
|
|
63
|
-
},
|
|
64
|
-
Swiper: {
|
|
65
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js", crossOrigin: "anonymous" }],
|
|
66
|
-
css: ["https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css"]
|
|
67
|
-
},
|
|
68
|
-
List: {
|
|
69
|
-
js: [
|
|
70
|
-
{ src: "https://cdnjs.cloudflare.com/ajax/libs/list.js/2.3.1/list.min.js", crossOrigin: "anonymous" },
|
|
71
|
-
{ src: "https://cdnjs.cloudflare.com/ajax/libs/list.pagination.js/0.1.1/list.pagination.min.js", crossOrigin: "anonymous" }
|
|
72
|
-
]
|
|
73
|
-
},
|
|
74
|
-
Swal: {
|
|
75
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/sweetalert2@11", crossOrigin: "anonymous" }],
|
|
76
|
-
css: ["https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css"]
|
|
77
|
-
},
|
|
78
|
-
FullCalendar: {
|
|
79
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/fullcalendar@6.1.15/index.global.min.js", crossOrigin: "anonymous" }]
|
|
80
|
-
},
|
|
81
|
-
Cleave: {
|
|
82
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/cleave.js/dist/cleave.min.js", crossOrigin: "anonymous" }]
|
|
83
|
-
},
|
|
84
|
-
noUiSlider: {
|
|
85
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/nouislider/dist/nouislider.min.js", crossOrigin: "anonymous" }],
|
|
86
|
-
css: ["https://cdn.jsdelivr.net/npm/nouislider/dist/nouislider.min.css"]
|
|
87
|
-
},
|
|
88
|
-
wNumb: {
|
|
89
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/wnumb/wNumb.min.js", crossOrigin: "anonymous" }]
|
|
90
|
-
},
|
|
91
|
-
Grid: {
|
|
92
|
-
js: [
|
|
93
|
-
{ src: "https://unpkg.com/gridjs/dist/gridjs.umd.js", crossOrigin: "anonymous" },
|
|
94
|
-
{ src: "https://unpkg.com/gridjs/plugins/selection/dist/selection.umd.js", crossOrigin: "anonymous" }
|
|
95
|
-
],
|
|
96
|
-
css: ["https://unpkg.com/gridjs/dist/theme/mermaid.min.css"]
|
|
97
|
-
},
|
|
98
|
-
FilePond: {
|
|
99
|
-
js: [
|
|
100
|
-
{ src: "https://unpkg.com/filepond/dist/filepond.min.js", crossOrigin: "anonymous" },
|
|
101
|
-
{ src: "https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.js", crossOrigin: "anonymous" },
|
|
102
|
-
{ src: "https://unpkg.com/filepond-plugin-file-validate-size/dist/filepond-plugin-file-validate-size.min.js", crossOrigin: "anonymous" },
|
|
103
|
-
{ src: "https://unpkg.com/filepond-plugin-image-exif-orientation/dist/filepond-plugin-image-exif-orientation.min.js", crossOrigin: "anonymous" },
|
|
104
|
-
{ src: "https://unpkg.com/filepond-plugin-file-encode/dist/filepond-plugin-file-encode.min.js", crossOrigin: "anonymous" }
|
|
105
|
-
],
|
|
106
|
-
css: [
|
|
107
|
-
"https://unpkg.com/filepond/dist/filepond.min.css",
|
|
108
|
-
"https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css"
|
|
109
|
-
]
|
|
110
|
-
},
|
|
111
|
-
Prism: {
|
|
112
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/prismjs/prism.min.js", crossOrigin: "anonymous" }],
|
|
113
|
-
css: ["https://cdn.jsdelivr.net/npm/prismjs/themes/prism.min.css"]
|
|
114
|
-
},
|
|
115
|
-
Isotope: {
|
|
116
|
-
js: [{ src: "https://unpkg.com/isotope-layout@3/dist/isotope.pkgd.min.js", crossOrigin: "anonymous" }]
|
|
117
|
-
},
|
|
118
|
-
particlesJS: {
|
|
119
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/particles.js@2.0.0/particles.min.js", crossOrigin: "anonymous" }]
|
|
120
|
-
},
|
|
121
|
-
dragula: {
|
|
122
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/dragula/dist/dragula.min.js", crossOrigin: "anonymous" }],
|
|
123
|
-
css: ["https://cdn.jsdelivr.net/npm/dragula/dist/dragula.min.css"]
|
|
124
|
-
},
|
|
125
|
-
DomAutoscroller: {
|
|
126
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/dom-autoscroller", crossOrigin: "anonymous" }]
|
|
127
|
-
},
|
|
128
|
-
Card: {
|
|
129
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/card/dist/card.js", crossOrigin: "anonymous" }]
|
|
130
|
-
},
|
|
131
|
-
Chart: {
|
|
132
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/chart.js", crossOrigin: "anonymous" }]
|
|
133
|
-
},
|
|
134
|
-
echarts: {
|
|
135
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js", crossOrigin: "anonymous" }]
|
|
136
|
-
},
|
|
137
|
-
Multi: {
|
|
138
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/multi.js/dist/multi.min.js", crossOrigin: "anonymous" }],
|
|
139
|
-
css: ["https://cdn.jsdelivr.net/npm/multi.js/dist/multi.min.css"]
|
|
140
|
-
},
|
|
141
|
-
autoComplete: {
|
|
142
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.7/dist/autoComplete.min.js", crossOrigin: "anonymous" }],
|
|
143
|
-
css: ["https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.7/dist/css/autoComplete.01.min.css"]
|
|
144
|
-
},
|
|
145
|
-
Pickr: {
|
|
146
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/pickr.min.js", crossOrigin: "anonymous" }],
|
|
147
|
-
css: ["https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/themes/classic.min.css"]
|
|
148
|
-
},
|
|
149
|
-
Shepherd: {
|
|
150
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/shepherd.js/dist/js/shepherd.min.js", crossOrigin: "anonymous" }],
|
|
151
|
-
css: ["https://cdn.jsdelivr.net/npm/shepherd.js/dist/css/shepherd.css"]
|
|
152
|
-
},
|
|
153
|
-
GMaps: {
|
|
154
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/gmaps/gmaps.min.js", crossOrigin: "anonymous" }]
|
|
155
|
-
},
|
|
156
|
-
L: {
|
|
157
|
-
js: [{ src: "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js", crossOrigin: "anonymous" }],
|
|
158
|
-
css: ["https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"]
|
|
159
|
-
},
|
|
160
|
-
Masonry: {
|
|
161
|
-
js: [{ src: "https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js", crossOrigin: "anonymous" }]
|
|
162
|
-
},
|
|
163
|
-
Rater: {
|
|
164
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/rater-js/index.js", crossOrigin: "anonymous" }]
|
|
165
|
-
},
|
|
166
|
-
Anime: {
|
|
167
|
-
js: [{ src: "https://cdn.jsdelivr.net/npm/animejs@3.2.2/lib/anime.min.js", crossOrigin: "anonymous" }]
|
|
168
|
-
}
|
|
169
|
-
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @type {Object.<string, PluginDefinition>}
|
|
14
|
+
* Configuration object containing plugin definitions.
|
|
15
|
+
* Populated dynamically from CoCreate.config.js or defaults.
|
|
16
|
+
*/
|
|
17
|
+
const plugins = {};
|
|
170
18
|
|
|
171
19
|
// --- CORE ENGINE ---
|
|
172
20
|
|
|
173
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Global Cache for script promises to prevent race conditions and duplicate loads.
|
|
23
|
+
* Stores the pending Promise for a script source URL.
|
|
24
|
+
* @type {Map<string, Promise<void>>}
|
|
25
|
+
*/
|
|
174
26
|
const scriptCache = new Map();
|
|
175
|
-
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Cache the CSS marker once on load to determine injection point.
|
|
30
|
+
* Used to ensure plugin CSS is injected in the correct order relative to user styles.
|
|
31
|
+
* @type {Element|null}
|
|
32
|
+
*/
|
|
176
33
|
const cssMarker = typeof document !== 'undefined' ? document.querySelector('link[plugins]') : null;
|
|
177
34
|
|
|
178
35
|
/**
|
|
179
|
-
* Global Initialization Function
|
|
180
|
-
* Processes one or more elements to attach plugins.
|
|
181
|
-
* @param {HTMLElement|NodeList|Array} elements - Element, or
|
|
36
|
+
* Global Initialization Function.
|
|
37
|
+
* Processes one or more elements to detect and attach plugins.
|
|
38
|
+
* * @param {HTMLElement|NodeList|Array<HTMLElement>} elements - Single Element, NodeList, or Array of Elements to initialize.
|
|
39
|
+
* @returns {void}
|
|
182
40
|
*/
|
|
183
41
|
function init(elements) {
|
|
184
42
|
if (!elements) return;
|
|
@@ -199,6 +57,13 @@ const cssMarker = typeof document !== 'undefined' ? document.querySelector('link
|
|
|
199
57
|
});
|
|
200
58
|
}
|
|
201
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Processes an individual element to detect, load resources for, and execute plugins.
|
|
62
|
+
* Reads the 'plugin' attribute (e.g., plugin="chart, map") to identify targets.
|
|
63
|
+
* * @async
|
|
64
|
+
* @param {HTMLElement} el - The DOM element to process.
|
|
65
|
+
* @returns {Promise<void>} Resolves when all resources for the plugins are loaded.
|
|
66
|
+
*/
|
|
202
67
|
async function processPlugin(el) {
|
|
203
68
|
const rawAttr = el.getAttribute("plugin");
|
|
204
69
|
if (!rawAttr) return;
|
|
@@ -207,140 +72,248 @@ async function processPlugin(el) {
|
|
|
207
72
|
|
|
208
73
|
for (const pluginName of pluginNames) {
|
|
209
74
|
const pluginDef = plugins[pluginName];
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if (
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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');
|
|
75
|
+
|
|
76
|
+
// Only attempt to load resources if a configuration exists
|
|
77
|
+
if (pluginDef) {
|
|
78
|
+
// Load CSS
|
|
79
|
+
if (pluginDef.css) pluginDef.css.forEach(href => {
|
|
80
|
+
if (!document.querySelector(`link[href="${href}"]`)) {
|
|
81
|
+
const link = document.createElement("link");
|
|
82
|
+
link.rel = "stylesheet";
|
|
83
|
+
link.href = href;
|
|
84
|
+
|
|
85
|
+
// CSS INJECTION STRATEGY:
|
|
86
|
+
// 1. Priority: Check for a specific marker element <link plugin>
|
|
87
|
+
// (Cached globally in cssMarker)
|
|
231
88
|
|
|
232
|
-
if (
|
|
233
|
-
|
|
89
|
+
if (cssMarker) {
|
|
90
|
+
// Insert before the marker
|
|
91
|
+
cssMarker.parentNode.insertBefore(link, cssMarker);
|
|
234
92
|
} else {
|
|
235
|
-
//
|
|
236
|
-
|
|
93
|
+
// 2. Fallback: Prepend before existing CSS
|
|
94
|
+
// To allow custom CSS to easily override plugin defaults, we must ensure
|
|
95
|
+
// plugin CSS loads BEFORE user CSS.
|
|
96
|
+
const firstStyle = document.head.querySelector('link[rel="stylesheet"], style');
|
|
97
|
+
|
|
98
|
+
if (firstStyle) {
|
|
99
|
+
document.head.insertBefore(link, firstStyle);
|
|
100
|
+
} else {
|
|
101
|
+
// If no CSS exists yet, appending is safe
|
|
102
|
+
document.head.appendChild(link);
|
|
103
|
+
}
|
|
237
104
|
}
|
|
238
105
|
}
|
|
239
|
-
}
|
|
240
|
-
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Load JS with Promise Cache
|
|
109
|
+
if (pluginDef.js) {
|
|
110
|
+
for (const item of pluginDef.js) {
|
|
111
|
+
const src = typeof item === 'string' ? item : item.src;
|
|
112
|
+
const integrity = typeof item === 'object' ? item.integrity : null;
|
|
113
|
+
const crossOrigin = typeof item === 'object' ? item.crossOrigin : null;
|
|
241
114
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
115
|
+
if (!scriptCache.has(src)) {
|
|
116
|
+
const scriptPromise = new Promise((resolve, reject) => {
|
|
117
|
+
// Check if already in DOM (manual load)
|
|
118
|
+
const existing = document.querySelector(`script[src="${src}"]`);
|
|
119
|
+
if (existing) {
|
|
120
|
+
if (existing.dataset.loaded === "true") {
|
|
121
|
+
resolve();
|
|
122
|
+
} else {
|
|
123
|
+
const prevOnload = existing.onload;
|
|
124
|
+
existing.onload = () => {
|
|
125
|
+
if (prevOnload) prevOnload();
|
|
126
|
+
existing.dataset.loaded = "true";
|
|
127
|
+
resolve();
|
|
128
|
+
};
|
|
129
|
+
existing.onerror = reject;
|
|
130
|
+
}
|
|
256
131
|
} else {
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
132
|
+
const s = document.createElement("script");
|
|
133
|
+
s.src = src;
|
|
134
|
+
if (integrity) {
|
|
135
|
+
s.integrity = integrity;
|
|
136
|
+
s.crossOrigin = crossOrigin || "anonymous";
|
|
137
|
+
}
|
|
138
|
+
s.onload = () => {
|
|
139
|
+
s.dataset.loaded = "true";
|
|
261
140
|
resolve();
|
|
262
141
|
};
|
|
263
|
-
|
|
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";
|
|
142
|
+
s.onerror = reject;
|
|
143
|
+
document.head.appendChild(s);
|
|
271
144
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
};
|
|
276
|
-
s.onerror = reject;
|
|
277
|
-
document.head.appendChild(s);
|
|
278
|
-
}
|
|
279
|
-
});
|
|
280
|
-
scriptCache.set(src, scriptPromise);
|
|
281
|
-
}
|
|
145
|
+
});
|
|
146
|
+
scriptCache.set(src, scriptPromise);
|
|
147
|
+
}
|
|
282
148
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
149
|
+
try {
|
|
150
|
+
await scriptCache.get(src);
|
|
151
|
+
} catch (e) {
|
|
152
|
+
console.error(`Failed to load script: ${src}`, e);
|
|
153
|
+
}
|
|
287
154
|
}
|
|
288
155
|
}
|
|
289
156
|
}
|
|
290
157
|
|
|
158
|
+
// Attempt to execute plugin even if no config was found (it might be on window already)
|
|
291
159
|
executeGenericPlugin(el, pluginName);
|
|
292
160
|
}
|
|
293
161
|
}
|
|
294
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Helper to determine if a function should be called with 'new'.
|
|
165
|
+
* Uses heuristics like ES6 class syntax, lack of prototype (arrow function), or PascalCase naming.
|
|
166
|
+
* * @param {Function} func - The function to check.
|
|
167
|
+
* @param {string} [name] - The property name associated with the function (for casing check).
|
|
168
|
+
* @returns {boolean} True if the function appears to be a constructor.
|
|
169
|
+
*/
|
|
170
|
+
const isConstructor = (func, name) => {
|
|
171
|
+
try {
|
|
172
|
+
if (typeof func !== 'function') return false;
|
|
173
|
+
if (/^\s*class\s+/.test(func.toString())) return true;
|
|
174
|
+
if (!func.prototype) return false;
|
|
175
|
+
const n = name || func.name;
|
|
176
|
+
if (n && /^[A-Z]/.test(n)) return true;
|
|
177
|
+
} catch(e) {}
|
|
178
|
+
return false;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Executes the logic for a generic plugin on a specific element.
|
|
183
|
+
* Handles:
|
|
184
|
+
* 1. Resolving the target class/function from window.
|
|
185
|
+
* 2. Initializing the base instance.
|
|
186
|
+
* 3. Processing attribute paths and nested JSON objects to execute methods or set properties.
|
|
187
|
+
* * @param {HTMLElement} el - The target element.
|
|
188
|
+
* @param {string} name - The name of the plugin (case-insensitive identifier).
|
|
189
|
+
* @returns {void}
|
|
190
|
+
*/
|
|
295
191
|
function executeGenericPlugin(el, name) {
|
|
296
192
|
const prefix = name.toLowerCase();
|
|
297
193
|
const mainAttr = el.getAttribute(prefix);
|
|
298
|
-
let rawData;
|
|
194
|
+
let rawData = {};
|
|
299
195
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
196
|
+
for (let attr of el.attributes) {
|
|
197
|
+
let key = attr.name;
|
|
198
|
+
if (key === prefix) {
|
|
199
|
+
key = name;
|
|
200
|
+
} else if (key.startsWith(prefix + '-')) {
|
|
201
|
+
key = key.replaceAll("-", ".");
|
|
202
|
+
} else if (!key.startsWith(prefix + '.')) {
|
|
203
|
+
continue
|
|
204
|
+
}
|
|
305
205
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
try { val = JSON.parse(val); } catch (e) { }
|
|
314
|
-
rawData[key] = val;
|
|
315
|
-
});
|
|
316
|
-
}
|
|
206
|
+
try {
|
|
207
|
+
rawData[key] = JSON.parse(attr.value);
|
|
208
|
+
} catch(e) {
|
|
209
|
+
rawData[key] = attr.value;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
};
|
|
317
213
|
|
|
318
214
|
// 2. Resolve parameters (Token Resolver)
|
|
319
|
-
|
|
215
|
+
let resolved = processParams(el, rawData);
|
|
216
|
+
resolved = dotNotationToObject(resolved);
|
|
320
217
|
|
|
321
|
-
|
|
322
|
-
if (!
|
|
323
|
-
console.error(`
|
|
218
|
+
let Plugin = window[name] || window[prefix];
|
|
219
|
+
if (!Plugin) {
|
|
220
|
+
console.error(`Plugin for ${name} not found on window.`);
|
|
324
221
|
return;
|
|
325
222
|
}
|
|
326
223
|
|
|
224
|
+
// Iterate over resolved object.
|
|
225
|
+
// Since we use dotNotationToObject, keys like "swiper.effect" are already nested as { swiper: { effect: ... } }
|
|
226
|
+
for (let key in resolved) {
|
|
227
|
+
// We generally expect the root key to match the plugin name (e.g., 'swiper')
|
|
228
|
+
// We unwrap this root key to pass the actual config to the Plugin.
|
|
229
|
+
if (key === name || key.toLowerCase() === prefix) {
|
|
230
|
+
try {
|
|
231
|
+
// Determine Target: Use existing instance on element if available, else use Window Plugin
|
|
232
|
+
let Target = el[name] || Plugin;
|
|
233
|
+
let val = resolved[key];
|
|
234
|
+
|
|
235
|
+
// Pass context: Window as parent, Plugin Name as property (for potential context binding)
|
|
236
|
+
// el and name used to store the result on the element.
|
|
237
|
+
update(Target, val, window, name, el, name);
|
|
238
|
+
|
|
239
|
+
console.log(`Processed ${name}`);
|
|
240
|
+
} catch (e) {
|
|
241
|
+
console.error(`Error processing ${name}:`, e);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function update(Target, val, parent, property, elParent, elProperty) {
|
|
248
|
+
// RESOLUTION: Handle case-insensitivity before processing targets.
|
|
249
|
+
// If Target is missing, check parent for a property matching 'property' (case-insensitive).
|
|
250
|
+
if (!Target && parent && property) {
|
|
251
|
+
const lowerProp = String(property).toLowerCase();
|
|
252
|
+
for (const key in parent) {
|
|
253
|
+
if (key.toLowerCase() === lowerProp) {
|
|
254
|
+
Target = parent[key];
|
|
255
|
+
property = key;
|
|
256
|
+
if (elProperty) elProperty = key; // Update element structure key to match real property
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
327
262
|
let instance;
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
263
|
+
if (typeof Target === 'function') {
|
|
264
|
+
if (!isConstructor(Target, property)) {
|
|
265
|
+
// Call as a function (method or standalone)
|
|
266
|
+
// Use 'parent' as context (this) if available to maintain class references
|
|
267
|
+
if (parent) {
|
|
268
|
+
if (Array.isArray(val)) {
|
|
269
|
+
instance = Target.apply(parent, val);
|
|
270
|
+
} else {
|
|
271
|
+
instance = Target.call(parent, val);
|
|
272
|
+
}
|
|
273
|
+
} else {
|
|
274
|
+
if (Array.isArray(val)) {
|
|
275
|
+
instance = Target(...val);
|
|
276
|
+
} else {
|
|
277
|
+
instance = Target(val);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
332
280
|
} else {
|
|
333
|
-
|
|
334
|
-
|
|
281
|
+
// Call as a Constructor
|
|
282
|
+
if (Array.isArray(val)) {
|
|
283
|
+
instance = new Target(...val);
|
|
335
284
|
} else {
|
|
336
|
-
instance = new Target(
|
|
285
|
+
instance = new Target(val);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Assign the result to the element structure
|
|
290
|
+
if (elParent && elProperty) {
|
|
291
|
+
elParent[elProperty] = instance;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
} else if (typeof Target === 'object' && Target !== null && typeof val === 'object' && val !== null && !Array.isArray(val)) {
|
|
295
|
+
// Prepare the next level of the element structure
|
|
296
|
+
if (elParent && elProperty) {
|
|
297
|
+
if (!elParent[elProperty]) {
|
|
298
|
+
elParent[elProperty] = {};
|
|
299
|
+
}
|
|
300
|
+
const nextElParent = elParent[elProperty];
|
|
301
|
+
|
|
302
|
+
for (let key in val) {
|
|
303
|
+
update(Target[key], val[key], Target, key, nextElParent, key);
|
|
337
304
|
}
|
|
338
305
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
306
|
+
} else if (parent && property) {
|
|
307
|
+
// If it's not a function, we are setting a value on the plugin object
|
|
308
|
+
parent[property] = val;
|
|
309
|
+
|
|
310
|
+
// Map the value to the element structure
|
|
311
|
+
if (elParent && elProperty) {
|
|
312
|
+
elParent[elProperty] = val;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
console.log(`Set plugin property ${property} to`, val);
|
|
342
316
|
}
|
|
343
|
-
if (instance) el[name] = instance;
|
|
344
317
|
}
|
|
345
318
|
|
|
346
319
|
/**
|
|
@@ -349,6 +322,7 @@ function executeGenericPlugin(el, name) {
|
|
|
349
322
|
* - $this / $this.children
|
|
350
323
|
* - $window.path.to.function(arg)
|
|
351
324
|
* - $anime.stagger(100)
|
|
325
|
+
* - Global access: $document, $window, etc.
|
|
352
326
|
*/
|
|
353
327
|
function processParams(el, params) {
|
|
354
328
|
if (typeof params === 'string' && params.startsWith('\u0024')) {
|
|
@@ -375,13 +349,22 @@ function processParams(el, params) {
|
|
|
375
349
|
if (propMatch) {
|
|
376
350
|
const [_, root, path] = propMatch;
|
|
377
351
|
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
352
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
353
|
+
if (obj) {
|
|
354
|
+
if (!path) return (obj instanceof HTMLCollection) ? Array.from(obj) : obj;
|
|
355
|
+
|
|
356
|
+
const val = path.split('.').reduce((o, k) => (o || {})[k], obj);
|
|
357
|
+
// Convert HTMLCollections to Arrays
|
|
358
|
+
return (val instanceof HTMLCollection) ? Array.from(val) : val;
|
|
359
|
+
}
|
|
384
360
|
}
|
|
361
|
+
|
|
362
|
+
// 3. Check for standalone globals like $document or $window
|
|
363
|
+
const globalKey = params.substring(1);
|
|
364
|
+
if (window[globalKey]) {
|
|
365
|
+
return window[globalKey];
|
|
366
|
+
}
|
|
367
|
+
|
|
385
368
|
} catch (e) {
|
|
386
369
|
console.warn("Failed to resolve dynamic token:", params);
|
|
387
370
|
}
|
|
@@ -396,20 +379,43 @@ function processParams(el, params) {
|
|
|
396
379
|
return params;
|
|
397
380
|
}
|
|
398
381
|
|
|
399
|
-
// ---
|
|
400
|
-
Observer.init({
|
|
401
|
-
name: "plugin",
|
|
402
|
-
types: ["addedNodes", "attributes"],
|
|
403
|
-
selector: "[plugin]",
|
|
404
|
-
attributeFilter: ["plugin"],
|
|
405
|
-
callback: (mutation) => {
|
|
406
|
-
init(mutation.target);
|
|
407
|
-
}
|
|
408
|
-
});
|
|
382
|
+
// --- STARTUP LOGIC ---
|
|
409
383
|
|
|
410
|
-
// Auto-init for existing elements
|
|
411
384
|
if (typeof document !== 'undefined') {
|
|
412
|
-
|
|
385
|
+
// Dynamic Import: Loads config if available, handles error if missing.
|
|
386
|
+
// Works with 'npm start' (Bundlers) by creating a code-split chunk.
|
|
387
|
+
import("./CoCreate.config.js")
|
|
388
|
+
.then((Config) => {
|
|
389
|
+
// LOGIC: Merge exports into plugins object
|
|
390
|
+
if (Config.plugins) {
|
|
391
|
+
Object.assign(plugins, Config.plugins);
|
|
392
|
+
}
|
|
393
|
+
else if (Config.default) {
|
|
394
|
+
if (Config.default.plugins) {
|
|
395
|
+
Object.assign(plugins, Config.default.plugins);
|
|
396
|
+
} else {
|
|
397
|
+
Object.assign(plugins, Config.default);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
})
|
|
401
|
+
.catch((err) => {
|
|
402
|
+
// Optional: fail silently for optional config
|
|
403
|
+
})
|
|
404
|
+
.finally(() => {
|
|
405
|
+
// Start Observer
|
|
406
|
+
Observer.init({
|
|
407
|
+
name: "plugin",
|
|
408
|
+
types: ["addedNodes", "attributes"],
|
|
409
|
+
selector: "[plugin]",
|
|
410
|
+
attributeFilter: ["plugin"],
|
|
411
|
+
callback: (mutation) => {
|
|
412
|
+
init(mutation.target);
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
// Initial Init
|
|
417
|
+
init(document.querySelectorAll("[plugin]"));
|
|
418
|
+
});
|
|
413
419
|
}
|
|
414
420
|
|
|
415
|
-
export default { init }
|
|
421
|
+
export default { init, plugins }
|