@ynck/rendux 0.92.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 ADDED
@@ -0,0 +1,19 @@
1
+ ## Changelog
2
+
3
+ ### v0.92
4
+ - Mini Evaluator is now directly embeded in `rendux`
5
+
6
+ ### v0.91
7
+ - Added conditional logging system for plugins and core features
8
+ - Removed x-click-outside plugin
9
+ - Fixed plugin attribute handling and re-rendering
10
+ - Improved plugin logging control via `logs` attribute
11
+
12
+ ### v0.86
13
+ - Inlined plugin core into `rendux.js`, removing dependence on external raw-render-core.js.
14
+ - Added chainable plugin API: `.use()`, `.process()`, `plugins`, and `parsePluginCall` directly on `rendux`.
15
+ - Fixed plugin attribute lookup to avoid invalid CSS selector issues for `render.plugin`.
16
+
17
+ ### v0.84
18
+ - Hidden elements (`x-if`) now reside in an in-memory `DocumentFragment` instead of a `<template>`, preventing hidden nodes from cluttering the DOM or shadow DOM.
19
+ - `<template x-for>` loops are now tracked from both the live DOM and the hidden fragment for correct re-rendering.
@@ -0,0 +1,32 @@
1
+ Commercial End User License Agreement (EULA)
2
+
3
+ This Commercial EULA ("Agreement") is a legal contract between you ("Licensee") and Yannick J.A. CHARLERY ("Licensor"). By obtaining, installing, or using the software (the "Software"), Licensee agrees to be bound by the terms below.
4
+
5
+ 1. License Grant.
6
+ Licensor hereby grants Licensee a limited, non-exclusive, non-transferable, worldwide license to use the Software for commercial purposes, subject to payment of the applicable license fee.
7
+
8
+ 2. Restrictions.
9
+ Licensee shall not:
10
+ a) Copy, distribute, or sublicense the Software beyond the scope of this Agreement.
11
+ b) Reverse engineer, decompile, disassemble, modify, translate, adapt, or create derivative works of the Software.
12
+ c) Use the Software in violation of any applicable laws or regulations.
13
+
14
+ 3. Maintenance & Support.
15
+ Any updates, bug fixes, or support services are provided at Licensor's discretion and may be subject to additional fees.
16
+
17
+ 4. Termination.
18
+ This Agreement and the license granted shall terminate automatically if Licensee fails to comply with any term herein. Upon termination, Licensee must cease all use and destroy all copies of the Software.
19
+
20
+ 5. Warranty Disclaimer.
21
+ The Software is provided "AS IS", without warranty of any kind, express or implied. Licensor expressly disclaims all warranties, including but not limited to merchantability and fitness for a particular purpose.
22
+
23
+ 6. Limitation of Liability.
24
+ In no event shall Licensor be liable for any indirect, incidental, special, or consequential damages arising out of or related to the Software or this Agreement.
25
+
26
+ 7. Governing Law.
27
+ This Agreement shall be governed by the laws of France, excluding its conflict-of-law rules and the CISG. Any dispute arising out of or in connection with this Agreement shall be finally settled under the Rules of Arbitration of the International Chamber of Commerce (ICC) by one arbitrator. The seat of arbitration shall be Paris, France, and the language of the arbitration shall be English. Judgment on the award may be entered in any court of competent jurisdiction.
28
+
29
+ 8. Entire Agreement.
30
+ This Agreement constitutes the entire agreement between the parties and supersedes all prior or contemporaneous agreements.
31
+
32
+ For inquiries or to purchase a commercial license, contact: ynck.chrl@pm.me
@@ -0,0 +1,11 @@
1
+ Non-Commercial Code License (Version 1.0)
2
+
3
+ Copyright (c) JULY 2025 Yannick J.A. CHARLERY
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to use and copy the Software strictly for non-commercial purposes only. Licensee may not modify, translate, adapt, merge, publish, distribute, sublicense, or create derivative works of the Software.
6
+
7
+ Commercial use, sale, or distribution of the Software (in source or binary form) is expressly prohibited without prior written authorization and a separate commercial license.
8
+
9
+ For information on commercial licensing, please refer to the accompanying EULA-COMMERCIAL.md or contact: <ynck.chrl@pm.me>
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,465 @@
1
+ ![rendux Logo](rendux-logo.jpg)
2
+
3
+ Source-available (non‑commercial).
4
+ Commercial licenses via EULA (See EULA-COMMERCIAL.md for commercial use).
5
+
6
+
7
+ # rendux
8
+
9
+ ## Introduction
10
+
11
+ `rendux` is a lightweight dependency-free templating engine designed for use within Custom Elements (Web Components). It provides reactive templating features through directives, making it easy to create dynamic, state-driven components without a full framework.
12
+
13
+ ### Size
14
+
15
+ - Minified: 13.0 KB (ESM), 13.0 KB (CJS)
16
+ - Gzipped: 4.7 KB
17
+
18
+ ## Core Concepts
19
+
20
+ `rendux` is called within a Custom Element's context and operates on its shadow DOM. It provides several directives to handle common UI patterns:
21
+
22
+ - `x-if` for conditional rendering
23
+ - `x-for` for list rendering
24
+ - `x-class` for conditional classes
25
+ - `x-*` for dynamic attributes
26
+ - `render` for text content interpolation
27
+ - `@event` for event handling
28
+ - `render.plugin` for plugin execution
29
+
30
+ ## Logging System
31
+
32
+ rendux includes a flexible logging system controlled by the `logs` attribute. You can enable logging for specific features:
33
+
34
+ ```html
35
+ <!-- Enable all logs -->
36
+ <my-component logs="all"></my-component>
37
+
38
+ <!-- Enable specific feature logs -->
39
+ <my-component logs="for, if, attr, class, render, plugins"></my-component>
40
+ ```
41
+
42
+ Available log categories:
43
+ - `plugins`: Plugin execution and results
44
+ - `for`: x-for loop processing
45
+ - `if`: x-if conditional rendering
46
+ - `attr`: x-attr attribute processing
47
+ - `class`: x-class processing
48
+ - `render`: Text content rendering
49
+
50
+ ## Basic Usage
51
+
52
+ ### With Custom Elements (Web Components)
53
+
54
+ #### With Shadow DOM
55
+
56
+ ```js
57
+ class MyElement extends HTMLElement {
58
+ constructor() {
59
+ super();
60
+ this.attachShadow({ mode: 'open' });
61
+ }
62
+
63
+ // Example component state
64
+ users = [
65
+ { name: 'Alice', active: true },
66
+ { name: 'Bob', active: false }
67
+ ];
68
+
69
+ connectedCallback() {
70
+ this.shadowRoot.innerHTML = `
71
+ <div>
72
+ <h2 render="title">User List</h2>
73
+ <template x-for="user of users">
74
+ <div x-class="(active, user.active)">
75
+ <span render="user.name"></span>
76
+ </div>
77
+ </template>
78
+ </div>
79
+ `;
80
+ rendux.call(this);
81
+ }
82
+ }
83
+ ```
84
+
85
+ #### Without Shadow DOM (plain Light DOM)
86
+
87
+ ```js
88
+ import { rendux } from './src/rendux.js';
89
+
90
+ class MyElement extends HTMLElement {
91
+ // Example component state
92
+ title = 'User List';
93
+ users = [
94
+ { name: 'Alice', active: true },
95
+ { name: 'Bob', active: false }
96
+ ];
97
+
98
+ connectedCallback() {
99
+ // Render directly into light DOM (no shadow root)
100
+ this.innerHTML = `
101
+ <div>
102
+ <h2 render="title"></h2>
103
+ <template x-for="user of users">
104
+ <div x-class="(active, user.active)">
105
+ <span render="user.name"></span>
106
+ <button @click="toggleUser(user)">Toggle</button>
107
+ </div>
108
+ </template>
109
+ </div>
110
+ `;
111
+ rendux.call(this); // uses 'this' as root in Light DOM
112
+ }
113
+
114
+ toggleUser(user) {
115
+ user.active = !user.active;
116
+ rendux.call(this); // re-render after state change
117
+ }
118
+ }
119
+ ```
120
+
121
+
122
+ ### With Plain HTML
123
+
124
+ You can also use rendux with plain HTML by creating a context object:
125
+
126
+ ```html
127
+ <!DOCTYPE html>
128
+ <html>
129
+ <head>
130
+ <script type="module">
131
+ import { rendux } from './src/rendux.js';
132
+
133
+ // Create a context object with your data and methods
134
+ const context = {
135
+ title: 'User List',
136
+ users: [
137
+ { name: 'Alice', active: true },
138
+ { name: 'Bob', active: false }
139
+ ],
140
+
141
+ // Add methods if needed
142
+ toggleUser(user) {
143
+ user.active = !user.active;
144
+ rendux.call(this); // Re-render after state change
145
+ }
146
+ };
147
+
148
+ // Call rendux with the context
149
+ rendux.call(context);
150
+ </script>
151
+ </head>
152
+ <body>
153
+ <div>
154
+ <h2 render="title">User List</h2>
155
+ <template x-for="user of users">
156
+ <div x-class="(active, user.active)">
157
+ <span render="user.name"></span>
158
+ <button @click="toggleUser(user)">Toggle</button>
159
+ </div>
160
+ </template>
161
+ </div>
162
+ </body>
163
+ </html>
164
+ ```
165
+
166
+ **Key Points for Plain HTML Usage:**
167
+ - Create a context object with your data and methods
168
+ - Use `rendux.call(context)` to bind the context
169
+ - Call `rendux.call(context)` again after state changes to re-render
170
+ - The context object becomes the `this` reference in all expressions
171
+
172
+ ### With CommonJS (Node.js)
173
+
174
+ ```js
175
+ // Import using CommonJS
176
+ const { rendux } = require('./dist/rendux.cjs');
177
+
178
+ // Create a context with your data
179
+ const context = {
180
+ title: 'Server Context',
181
+ users: [
182
+ { name: 'Alice', active: true },
183
+ { name: 'Bob', active: false }
184
+ ]
185
+ };
186
+
187
+ // Note: rendux requires a DOM environment
188
+ // For Node.js, use with a DOM implementation like jsdom
189
+ const { JSDOM } = require('jsdom');
190
+ const dom = new JSDOM(`
191
+ <div>
192
+ <h2 render="title">User List</h2>
193
+ <template x-for="user of users">
194
+ <div x-class="(active, user.active)">
195
+ <span render="user.name"></span>
196
+ </div>
197
+ </template>
198
+ </div>
199
+ `);
200
+
201
+ // Set up global document for rendux
202
+ global.document = dom.window.document;
203
+
204
+ // Call rendux with the context
205
+ rendux.call(context, dom.window.document.body);
206
+ ```
207
+
208
+ **When to Use `rendux.cjs`:**
209
+
210
+ Use the CommonJS build (`dist/rendux.cjs`) in these scenarios:
211
+
212
+ 1. **Node.js Build Tools**: When using bundlers or build systems that require CommonJS (older Webpack configs, some testing frameworks)
213
+ 2. **Server-Side Rendering (SSR)**: When rendering components on the server with a DOM implementation like jsdom or happy-dom
214
+ 3. **Legacy Node.js Projects**: Projects that haven't migrated to ES modules (`type: "module"` in package.json)
215
+ 4. **Testing Environments**: Jest or other test runners configured for CommonJS (though modern Jest supports ESM)
216
+ 5. **npm Package Distribution**: When publishing packages that need to support both module systems
217
+
218
+ **Note**: rendux requires a DOM environment. For Node.js/server usage, you must provide a DOM implementation (jsdom, happy-dom, linkedom) since rendux uses `document`, `Element`, `querySelectorAll`, etc.
219
+
220
+ For modern projects with native ESM support, prefer the ESM build (`dist/rendux.js`) instead.
221
+
222
+
223
+
224
+ ## Directives
225
+
226
+ ### 1. Conditional Rendering (`x-if`)
227
+
228
+ ```html
229
+ <div x-if="someCondition">
230
+ Only shown when condition is true
231
+ </div>
232
+ ```
233
+
234
+ The element is completely removed from the DOM (not just hidden) when the condition is false.
235
+
236
+ ### 2. List Rendering (`x-for`)
237
+
238
+ Supports two syntaxes:
239
+ ```html
240
+ <!-- Simple iteration -->
241
+ <template x-for="item of items">
242
+ <div render="item.name"></div>
243
+ </template>
244
+
245
+ <!-- With index -->
246
+ <template x-for="(item, index) of items">
247
+ <div render="index + ': ' + item.name"></div>
248
+ </template>
249
+ ```
250
+
251
+ Features:
252
+ - Uses `<template>` tag to define the repeatable content
253
+ - Maintains its own render cache per iteration
254
+ - Supports deep change detection for arrays
255
+ - Provides iteration context to nested elements
256
+
257
+ ### 3. Class Binding (`x-class`)
258
+
259
+ Two syntaxes available:
260
+
261
+ ```html
262
+ <!-- Single class with condition -->
263
+ <div x-class="highlight, isActive">
264
+ </div>
265
+
266
+ <!-- Multiple class/condition pairs -->
267
+ <div x-class="(selected, isSelected) (highlight, isHighlighted)">
268
+ </div>
269
+ ```
270
+
271
+ ### 4. Dynamic Attributes (`x-*`)
272
+
273
+ Any attribute prefixed with `x-` (except special directives) becomes dynamic:
274
+
275
+ ```html
276
+ <input x-value="inputValue">
277
+ <img x-src="imageUrl">
278
+ <div x-data-id="getId()">
279
+ ```
280
+
281
+ ### 5. Text Content (`render`)
282
+
283
+ Renders dynamic text content:
284
+
285
+ ```html
286
+ <!-- Simple value -->
287
+ <span render="message"></span>
288
+
289
+ <!-- With condition -->
290
+ <span render="message, isVisible">
291
+ Default text when not visible
292
+ </span>
293
+ ```
294
+
295
+ Features:
296
+ - Supports expressions
297
+ - Can access component methods and properties
298
+ - Handles objects (converts to JSON)
299
+ - Supports conditional rendering
300
+
301
+ ### 6. Event Handling (`@event`)
302
+
303
+ ```html
304
+ <!-- Click events -->
305
+ <button @click="handleClick()">Click me</button>
306
+
307
+ <!-- Form events -->
308
+ <input @input="updateValue(event.target.value)"
309
+ @focus="handleFocus()"
310
+ @blur="handleBlur()">
311
+
312
+ <!-- Keyboard events -->
313
+ <input @keyup="handleKeyUp(event)"
314
+ @keydown="handleKeyDown(event)">
315
+ ```
316
+
317
+ The event object is automatically available in handlers as `event`.
318
+
319
+ ## Plugin System
320
+
321
+ rendux includes an integrated plugin system that allows you to extend templating functionality.
322
+
323
+ ### Using Plugins
324
+
325
+ ```js
326
+ import { rendux } from './src/rendux.js';
327
+ import { i18nPlugin } from './src/plugins/plugin-i18n.js';
328
+ import { xTooltip } from './src/plugins/plugin-x-tooltip.js';
329
+
330
+ // Register plugins (chainable)
331
+ rendux
332
+ .use(i18nPlugin({
333
+ translations: {
334
+ en: { greeting: 'Hello!' },
335
+ fr: { greeting: 'Bonjour!' }
336
+ }
337
+ }))
338
+ .use(xTooltip);
339
+ ```
340
+
341
+ ### Using Plugin Directives
342
+
343
+ ```html
344
+ <!-- i18n Plugin -->
345
+ <p render.i18n="greeting">Hello!</p>
346
+
347
+ <!-- Tooltip Plugin -->
348
+ <button x-tooltip="'Click to save'">Save</button>
349
+ ```
350
+
351
+ ### Building Custom Plugins
352
+
353
+ A plugin is an object with the following structure:
354
+
355
+ ```js
356
+ export const myPlugin = {
357
+ // Required properties
358
+ name: 'pluginName', // Used in render.pluginName or x-pluginName
359
+ target: 'attribute', // Optional: 'attribute' for x-* plugins
360
+ execute(el, value, ctx) {
361
+ // Main plugin logic
362
+ return 'result'; // Optional: string to use as element content
363
+ },
364
+
365
+ // Optional lifecycle hooks (in order of execution)
366
+ onBeforeRender(root, component) {
367
+ // Called ONCE at the start of rendux()
368
+ // Use for initialization, setup, or capturing initial state
369
+ },
370
+
371
+ onBeforeExecute(el, rawValue, ctx) {
372
+ // Called BEFORE each plugin execution on an element
373
+ // Use for element-specific setup or validation
374
+ },
375
+
376
+ onAfterExecute(el, rawValue, ctx) {
377
+ // Called AFTER each plugin execution on an element
378
+ // Use for cleanup or post-processing of element changes
379
+ },
380
+
381
+ onAfterRender(root, ctx) {
382
+ // Called ONCE at the end of rendux()
383
+ // Use for final cleanup or post-render operations
384
+ // This hook is async - can return a Promise
385
+ }
386
+ };
387
+ ```
388
+
389
+ ## Security
390
+
391
+ rendux is designed with security as a core principle, implementing several protective measures to prevent common web vulnerabilities:
392
+
393
+ ### XSS Prevention Through Safe Content Handling
394
+
395
+ **No innerHTML Usage for Dynamic Content**
396
+ - rendux never uses `innerHTML` for dynamic content rendering
397
+ - All user data is inserted using `textContent`, which automatically escapes HTML
398
+ - This prevents malicious script injection through user-controlled data
399
+
400
+ ```js
401
+ // Safe: User input is escaped automatically
402
+ <span render="userInput"></span> // Uses textContent internally
403
+
404
+ // Unsafe (what rendux avoids):
405
+ element.innerHTML = userInput; // Could execute scripts
406
+ ```
407
+
408
+ ### Sandboxed Expression Evaluator
409
+
410
+ **Custom Mini-Evaluator Instead of eval()**
411
+ - rendux includes a custom expression parser that never uses `eval()` or `Function()`
412
+ - Only supports safe operations: property access, method calls, basic operators
413
+ - Prevents arbitrary code execution through template expressions
414
+
415
+ **Whitelisted Global Access**
416
+ - Only safe global objects are accessible: `Math`, `Date`, `Number`, `String`, `Boolean`, `JSON`, `Array`
417
+ - No access to dangerous globals like `window`, `document`, or `eval`
418
+ - Controlled execution environment limits potential attack vectors
419
+
420
+ ```js
421
+ // Safe expressions supported:
422
+ render="user.name" ✓
423
+ render="Math.max(a, b)" ✓
424
+ render="items.length > 0 ? 'Yes' : 'No'" ✓
425
+
426
+ // Dangerous expressions blocked:
427
+ render="eval('malicious code')" ✗ (eval not accessible)
428
+ render="window.location = 'evil.com'" ✗ (window not accessible)
429
+ ```
430
+
431
+ ### Template Content Security
432
+
433
+ **Safe Template Processing**
434
+ - Template elements (`<template x-for>`) use `.content` property, not innerHTML
435
+ - Plugin system processes attributes safely without executing embedded scripts
436
+ - Event handlers are properly scoped and don't use string-to-function conversion
437
+
438
+ ### Best Practices for Secure Usage
439
+
440
+ 1. **Validate Plugin Inputs**: When creating custom plugins, validate and sanitize inputs
441
+ 2. **Context Isolation**: Each component maintains its own isolated context
442
+ 3. **No Script Injection**: User data in expressions is evaluated safely, not executed as code
443
+
444
+ ```js
445
+ // Safe plugin example
446
+ export const safePlugin = {
447
+ name: 'format',
448
+ execute(value) {
449
+ // Validate and sanitize input
450
+ if (typeof value !== 'string') return '';
451
+ return value.replace(/[<>&"']/g, char => ({
452
+ '<': '&lt;', '>': '&gt;', '&': '&amp;',
453
+ '"': '&quot;', "'": '&#39;'
454
+ })[char]);
455
+ }
456
+ };
457
+ ```
458
+
459
+ This security model makes rendux suitable for applications handling user-generated content while maintaining the flexibility of a templating engine.
460
+
461
+ ## License
462
+
463
+ This project is available under two licenses:
464
+ - Non-commercial use: See LICENSE-NONCOMMERCIAL.md
465
+ - Commercial use: See EULA-COMMERCIAL.md
@@ -0,0 +1,2 @@
1
+ /*! plugin/i18n v0.92.0 | Copyright (c) JULY 2025 Yannick J.A. CHARLERY | This software is licensed for non-commercial use only. Commercial licensing available upon request. For license details: https://github.com/ynck-chrl/rendux */
2
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e={en:{message:"Welcome to rendux!",button:"Click me",title:"Hello World"},fr:{message:"Bienvenue dans rendux!",button:"Cliquez-moi",title:"Bonjour le Monde"}};const t=function(t={}){const n=t.translations||e;let o=t.defaultLanguage||"en";return{name:"i18n",target:"render",execute:function(e,...t){const r=e.split(".");let s=n[o];for(const t of r){if(!s||"object"!=typeof s||!(t in s))return`[${e}]`;s=s[t]}if("string"==typeof s&&t.length>0){return s.replace(/\{(\d+)\}/g,(e,n)=>{const o=parseInt(n,10);return void 0!==t[o]?t[o]:e})}return s||`[${e}]`},setLanguage:e=>!!n[e]&&(o=e,!0),getLanguage:()=>o,addTranslations(e,t){n[e]||(n[e]={}),Object.assign(n[e],t)}}};exports.i18nPlugin=t;
@@ -0,0 +1,2 @@
1
+ /*! plugin/i18n v0.92.0 | Copyright (c) JULY 2025 Yannick J.A. CHARLERY | This software is licensed for non-commercial use only. Commercial licensing available upon request. For license details: https://github.com/ynck-chrl/rendux */
2
+ const e={en:{message:"Welcome to rendux!",button:"Click me",title:"Hello World"},fr:{message:"Bienvenue dans rendux!",button:"Cliquez-moi",title:"Bonjour le Monde"}};const t=function(t={}){const n=t.translations||e;let o=t.defaultLanguage||"en";return{name:"i18n",target:"render",execute:function(e,...t){const r=e.split(".");let s=n[o];for(const t of r){if(!s||"object"!=typeof s||!(t in s))return`[${e}]`;s=s[t]}if("string"==typeof s&&t.length>0){return s.replace(/\{(\d+)\}/g,(e,n)=>{const o=parseInt(n,10);return void 0!==t[o]?t[o]:e})}return s||`[${e}]`},setLanguage:e=>!!n[e]&&(o=e,!0),getLanguage:()=>o,addTranslations(e,t){n[e]||(n[e]={}),Object.assign(n[e],t)}}};export{t as i18nPlugin};
@@ -0,0 +1,2 @@
1
+ /*! plugin/x-logger v0.92.0 | Copyright (c) JULY 2025 Yannick J.A. CHARLERY | This software is licensed for non-commercial use only. Commercial licensing available upon request. For license details: https://github.com/ynck-chrl/rendux */
2
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e={name:"logger",target:"render",execute:function(e,...r){switch(e){case"logMessage":return`[Message: ${r.join(", ")}]`;case"logError":return`[Error: ${r.join(", ")}]`;case"logWarning":return`[Warning: ${r.join(", ")}]`;case"logInfo":return`[Info: ${r.join(", ")}]`;case"logDebug":return`[Debug: ${r.join(", ")}]`;case"logTable":return r.length>0&&(Array.isArray(r[0])||"object"==typeof r[0])?`[Table: ${Array.isArray(r[0])?r[0].length+" items":"object"}]`:`[Table: ${r.join(", ")}]`;default:return`[${e}: ${r.join(", ")}]`}}},r=e;exports.logger=e,exports.loggerPlugin=r;
@@ -0,0 +1,2 @@
1
+ /*! plugin/x-logger v0.92.0 | Copyright (c) JULY 2025 Yannick J.A. CHARLERY | This software is licensed for non-commercial use only. Commercial licensing available upon request. For license details: https://github.com/ynck-chrl/rendux */
2
+ const e={name:"logger",target:"render",execute:function(e,...r){switch(e){case"logMessage":return`[Message: ${r.join(", ")}]`;case"logError":return`[Error: ${r.join(", ")}]`;case"logWarning":return`[Warning: ${r.join(", ")}]`;case"logInfo":return`[Info: ${r.join(", ")}]`;case"logDebug":return`[Debug: ${r.join(", ")}]`;case"logTable":return r.length>0&&(Array.isArray(r[0])||"object"==typeof r[0])?`[Table: ${Array.isArray(r[0])?r[0].length+" items":"object"}]`:`[Table: ${r.join(", ")}]`;default:return`[${e}: ${r.join(", ")}]`}}},r=e;export{e as logger,r as loggerPlugin};
@@ -0,0 +1,2 @@
1
+ /*! plugin/x-tooltip v0.92.0 | Copyright (c) JULY 2025 Yannick J.A. CHARLERY | This software is licensed for non-commercial use only. Commercial licensing available upon request. For license details: https://github.com/ynck-chrl/rendux */
2
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e={name:"x-tooltip",target:"attribute",execute:function(e,t,o){e._xTooltipElement&&e._xTooltipElement.remove();const n=document.createElement("div");n.textContent=t,n.style.cssText="\n position: absolute;\n background: #333;\n color: white;\n padding: 8px 12px;\n border-radius: 4px;\n font-size: 12px;\n white-space: nowrap;\n z-index: 1000;\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.2s;\n box-shadow: 0 2px 8px rgba(0,0,0,0.2);\n ",document.body.appendChild(n),e._xTooltipElement=n;const i=function(t){const o=e.getBoundingClientRect(),i=window.pageXOffset||document.documentElement.scrollLeft,s=window.pageYOffset||document.documentElement.scrollTop,l=i+o.left+(o.width-n.offsetWidth)/2,p=s+o.top-n.offsetHeight-8;n.style.left=`${l}px`,n.style.top=`${p}px`,n.style.opacity="1"},s=function(){n.style.opacity="0"};if(e._xTooltipListeners&&(e.removeEventListener("mouseenter",e._xTooltipListeners.show),e.removeEventListener("mouseleave",e._xTooltipListeners.hide)),e.addEventListener("mouseenter",i),e.addEventListener("mouseleave",s),e._xTooltipListeners={show:i,hide:s},!e._xTooltipCleanup){e._xTooltipCleanup=!0;const t=new MutationObserver(o=>{o.forEach(o=>{o.removedNodes.forEach(o=>{(o===e||o.contains&&o.contains(e))&&(e._xTooltipElement&&e._xTooltipElement.remove(),t.disconnect())})})});t.observe(document.body,{childList:!0,subtree:!0})}}};exports.xTooltip=e;
@@ -0,0 +1,2 @@
1
+ /*! plugin/x-tooltip v0.92.0 | Copyright (c) JULY 2025 Yannick J.A. CHARLERY | This software is licensed for non-commercial use only. Commercial licensing available upon request. For license details: https://github.com/ynck-chrl/rendux */
2
+ const e={name:"x-tooltip",target:"attribute",execute:function(e,t,o){e._xTooltipElement&&e._xTooltipElement.remove();const n=document.createElement("div");n.textContent=t,n.style.cssText="\n position: absolute;\n background: #333;\n color: white;\n padding: 8px 12px;\n border-radius: 4px;\n font-size: 12px;\n white-space: nowrap;\n z-index: 1000;\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.2s;\n box-shadow: 0 2px 8px rgba(0,0,0,0.2);\n ",document.body.appendChild(n),e._xTooltipElement=n;const i=function(t){const o=e.getBoundingClientRect(),i=window.pageXOffset||document.documentElement.scrollLeft,s=window.pageYOffset||document.documentElement.scrollTop,l=i+o.left+(o.width-n.offsetWidth)/2,p=s+o.top-n.offsetHeight-8;n.style.left=`${l}px`,n.style.top=`${p}px`,n.style.opacity="1"},s=function(){n.style.opacity="0"};if(e._xTooltipListeners&&(e.removeEventListener("mouseenter",e._xTooltipListeners.show),e.removeEventListener("mouseleave",e._xTooltipListeners.hide)),e.addEventListener("mouseenter",i),e.addEventListener("mouseleave",s),e._xTooltipListeners={show:i,hide:s},!e._xTooltipCleanup){e._xTooltipCleanup=!0;const t=new MutationObserver(o=>{o.forEach(o=>{o.removedNodes.forEach(o=>{(o===e||o.contains&&o.contains(e))&&(e._xTooltipElement&&e._xTooltipElement.remove(),t.disconnect())})})});t.observe(document.body,{childList:!0,subtree:!0})}}};export{e as xTooltip};
@@ -0,0 +1,2 @@
1
+ /*! rendux v0.92.0 | Copyright (c) JULY 2025 Yannick J.A. CHARLERY | This software is licensed for non-commercial use only. Commercial licensing available upon request. For license details: https://github.com/ynck-chrl/rendux */
2
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e={Math:Math,Date:Date,Number:Number,String:String,Boolean:Boolean,JSON:JSON,parseInt:parseInt,parseFloat:parseFloat,isFinite:isFinite,Array:Array};function t(t,r={}){const n=function(e){const t=[];let r=0;for(;r<e.length;){let n=e[r];if(/\s/.test(n)){r++;continue}if('"'===n||"'"===n){const o=n;let s="";for(r++;r<e.length&&e[r]!==o;)"\\"===e[r]?(r++,r<e.length&&(s+=e[r++])):s+=e[r++];r++,t.push({type:"string",value:s});continue}if(/[0-9]/.test(n)||"."===n&&/[0-9]/.test(e[r+1])){let n=r;for(;r<e.length&&/[0-9]/.test(e[r]);)r++;if("."===e[r])for(r++;r<e.length&&/[0-9]/.test(e[r]);)r++;const o=parseFloat(e.slice(n,r));t.push({type:"number",value:o});continue}if(/[a-zA-Z_$]/.test(n)){let n=r;for(r++;r<e.length&&/[a-zA-Z0-9_$]/.test(e[r]);)r++;const o=e.slice(n,r);"true"===o||"false"===o?t.push({type:"boolean",value:"true"===o}):"null"===o?t.push({type:"null",value:null}):t.push({type:"identifier",value:o});continue}const o=e.substr(r,2),s=e.substr(r,3);if(["===","!=="].includes(s))t.push({type:"operator",value:s}),r+=3;else if(["==","!=",">=","<=","&&","||"].includes(o))t.push({type:"operator",value:o}),r+=2;else{if(!["+","-","*","/","%",">","<","!","?",":","(",")","[","]",".",","].includes(n))throw new Error(`Invalid character '${n}' in expression`);t.push({type:"operator",value:n}),r++}}return t.push({type:"EOF"}),t}(t);let o=0;function s(){return n[o]||{type:"EOF"}}function a(){return n[o++]||{type:"EOF"}}function i(e,t){const r=s();if(r.type!==e||void 0!==t&&r.value!==t)throw new Error(`Expected ${t||e} but got ${r.value}`);return a(),r}function l(e){const t=s();return"operator"===t.type&&t.value===e}const c=u();if("EOF"!==s().type)throw new Error(`Unexpected token: ${s().value}`);return c;function u(){let e=function(){let e=f();for(;l("||");){a();const t=f();e=e||t}return e}();if(l("?")){a();const t=u();i("operator",":");const r=u();e=e?t:r}return e}function f(){let e=p();for(;l("&&");){a();const t=p();e=e&&t}return e}function p(){let e=d();for(;["==","!=","===","!=="].includes(s().value);){const t=a().value,r=d();switch(t){case"==":e=e==r;break;case"!=":e=e!=r;break;case"===":e=e===r;break;case"!==":e=e!==r}}return e}function d(){let e=h();for(;[">","<",">=","<="].includes(s().value);){const t=a().value,r=h();switch(t){case">":e=e>r;break;case"<":e=e<r;break;case">=":e=e>=r;break;case"<=":e=e<=r}}return e}function h(){let e=x();for(;["+","-"].includes(s().value);){const t=a().value,r=x();e="+"===t?e+r:e-r}return e}function x(){let e=g();for(;["*","/","%"].includes(s().value);){const t=a().value,r=g();switch(t){case"*":e*=r;break;case"/":e/=r;break;case"%":e%=r}}return e}function g(){if(["!","+","-"].includes(s().value)){const e=a().value,t=g();switch(e){case"!":return!t;case"+":return+t;case"-":return-t}}return function(){const t=s();if("number"===t.type||"string"===t.type||"boolean"===t.type||"null"===t.type)return a(),t.value;if("identifier"===t.type){a();let n=function(t){if(t in r)return r[t];if(t in e)return e[t];if(null!=r.this&&t in r.this)return r.this[t];throw new Error(`Unknown identifier: ${t}`)}(t.value);for(;;)if(l(".")){a();const e=i("identifier").value;n=null==n?void 0:n[e]}else if(l("[")){a();const e=u();i("operator","]"),n=null==n?void 0:n[e]}else{if(!l("("))break;{a();const e=[];if(!l(")"))do{e.push(u())}while(l(",")&&a());if(i("operator",")"),"function"!=typeof n)throw new Error(`'${t.value}' is not a function`);n=n.apply(r.this,e)}}return n}if(l("(")){a();const e=u();return i("operator",")"),e}throw new Error(`Unexpected token: ${t.value}`)}()}}const r=new Map;function n(e){if(!e||!e.name)throw new Error("Invalid plugin");r.set(e.name,e)}function o(e,t={}){const n=e.match(/^([a-zA-Z_$][\w$]*)\s*\(\s*(.*)\s*\)$/);if(!n)return null;const o=n[1],s=r.get(o);if(!s)return null;const a=n[2].trim();if(""===a)return{plugin:s,args:[]};return{plugin:s,args:a.split(/\s*,\s*/).map(e=>{if(e.startsWith('"')&&e.endsWith('"')||e.startsWith("'")&&e.endsWith("'"))return e.slice(1,-1);if(/^-?\d+(?:\.\d+)?$/.test(e))return parseFloat(e);if(e in t)return t[e];throw new Error(`Unknown identifier: ${e}`)})}}async function s(e=document,t={}){const n=Array.from(e.querySelectorAll("*")).filter(e=>Array.from(e.attributes).some(e=>e.name.startsWith("render.")));for(const e of n)for(const n of Array.from(e.attributes)){if(!n.name.startsWith("render."))continue;const s=n.name,a=n.value.trim();let i,l,c;if("render.plugin"===s){let e;try{e=o(a,t)}catch(e){console.error(e);continue}if(!e)continue;i=e.plugin,l=e.args}else{const e=s.slice(7);if(i=r.get(e),!i)continue;if(a.startsWith(e+"(")){let e;try{e=o(a,t)}catch(e){console.error(e);continue}if(!e)continue;i=e.plugin,l=e.args}else l=[a]}"function"==typeof i.onBeforeExecute&&i.onBeforeExecute(e,a,t);try{c=i.execute(...l)}catch(e){continue}e.textContent=null==c?"":String(c),"function"==typeof i.onAfterExecute&&i.onAfterExecute(e,a,t)}for(const n of r.values())"function"==typeof n.onAfterRender&&await n.onAfterRender(e,t)}function rendux(e=this.shadowRoot||this){const n=this;for(const t of r.values())"function"==typeof t.onBeforeRender&&t.onBeforeRender(e,n);const o=document,a=n.getAttribute("logs"),i=Boolean(a),l=a?new Set(a.split(",").map(e=>e.trim().toLowerCase())):new Set,c=e=>l.has("all")||l.has(e.toLowerCase())||l.has(("x-"+e).toLowerCase()),u=i&&l.has("plugins"),f=(...e)=>console.warn(...e);n._xRenderCache||(n._xRenderCache=new Map),n._xRenderOriginalText||(n._xRenderOriginalText=new WeakMap),n._xIfData||(n._xIfData=new WeakMap),n._cloneContext||(n._cloneContext=new WeakMap),n._xClassCache||(n._xClassCache=new Map),n._xForCache||(n._xForCache=new WeakMap),n.__mirrorContainer||(n.__mirrorContainer=o.createDocumentFragment());const p={};function d(e){let t=e;for(;t;){const e=n._cloneContext.get(t);if(e)return n.getAttribute("logs")&&n.getAttribute("logs").includes("plugins")&&console.log("Found context for element:",t,e),e;t=t.parentNode}return n.getAttribute("logs")&&n.getAttribute("logs").includes("plugins")&&console.log("Using default component context:",n),n}function h(e,r){if(!e)return!0;const n=d(r);try{return t(e,{this:n,...p})}catch(t){return void f(`[evaluate] "${e}" failed in`,n,t)}}Object.getOwnPropertyNames(Object.getPrototypeOf(n)).forEach(e=>{"function"==typeof n[e]&&"constructor"!==e&&(p[e]=n[e].bind(n))}),p.plugins=r;const x=i&&c("for");x&&console.groupCollapsed("x-for");Array.from(e.querySelectorAll("template[x-for]")).concat(Array.from(n.__mirrorContainer.querySelectorAll("template[x-for]"))).forEach(e=>{let t,r,s;e._xForMeta||(e._xForMeta={parent:e.parentNode,next:e.nextSibling},n.__mirrorContainer.appendChild(e));try{({loopVar:t,indexVar:r,arrayPath:s}=function(e){const t=e.match(/^\s*(?:\(\s*([^,\s]+)\s*,\s*([^,\s]+)\s*\)|([^,\s()]+))\s+(?:in|of)\s+(.+)$/);if(!t)throw new Error("Invalid x-for: "+e);return{loopVar:t[1]||t[3],indexVar:t[2]||null,arrayPath:t[4].trim()}}(e.getAttribute("x-for")))}catch(e){return void f("[x-for]",e.message)}const a=function(e,t){const r=d(t),n=e.match(/([^[.\]]+)|\[(\d+)\]/g);if(n)return n.reduce((e,t)=>{if(null!=e){if(t.startsWith("[")){const r=parseInt(t.slice(1,-1),10);return Array.isArray(e)?e[r]:void 0}return e[t]}},r)}(s,e);if(!Array.isArray(a))return void f(`[x-for] expected array at ${s}`,a);x&&console.log(`iterating ${s} → length ${a.length}`);const i=function(e){return JSON.stringify(e)}(a),l=n._xForCache.get(e);l&&l.arrayRef===a&&l.length===a.length&&l.signature===i?x&&console.log(" → skipped (no change)"):(n._xForCache.set(e,{arrayRef:a,length:a.length,signature:i}),e._forClones&&e._forClones.forEach(e=>e.remove()),e._forClones=[],a.forEach((s,a)=>{x&&console.groupCollapsed(` index ${a}`);const i=o.createDocumentFragment();i.appendChild(e.content.cloneNode(!0));const l=(c=t,u=s,f=r,p=a,new Proxy(n,{has:(e,t)=>t===c||f&&t===f||t in e,get:(e,t)=>t===c?u:f&&t===f?p:e[t]}));var c,u,f,p,d,h;d=i,h=l,n._cloneContext.set(d,h),d.querySelectorAll("*").forEach(e=>n._cloneContext.set(e,h));const g=n._xRenderCache,y=n._xRenderOriginalText;for(n._xRenderCache=new Map,n._xRenderOriginalText=new WeakMap,rendux.call(n,i),n._xRenderCache=g,n._xRenderOriginalText=y;i.firstChild;){const t=i.firstChild;e._xForMeta.parent.insertBefore(t,e._xForMeta.next),e._forClones.push(t)}x&&console.groupEnd()}))}),x&&console.groupEnd();const g=i&&c("if");Array.from(e.querySelectorAll("[x-if]")).concat(Array.from(n.__mirrorContainer.querySelectorAll("[x-if]"))).forEach(e=>{const t=e.getAttribute("x-if")?.trim(),r=!t||h(t,e);g&&console.groupCollapsed("x-if",e,t,"→",r);let s=n._xIfData.get(e);s||(s={placeholder:o.createComment("x-if placeholder"),isHidden:!1},n._xIfData.set(e,s)),r||s.isHidden?r&&s.isHidden&&(s.placeholder.parentNode.replaceChild(e,s.placeholder),s.isHidden=!1):(e.parentNode.replaceChild(s.placeholder,e),n.__mirrorContainer.appendChild(e),s.isHidden=!0),g&&console.groupEnd()}),0===n._xRenderCache.size&&Array.from(e.querySelectorAll("[render]")).forEach(e=>{const t=e.getAttribute("render")?.trim();t&&(n._xRenderCache.set(e,t),n._xRenderOriginalText.set(e,e.textContent))}),Array.from(e.querySelectorAll("[x-class]")).forEach(e=>{const t=e.getAttribute("x-class");null==t||n._xClassCache.has(e)||n._xClassCache.set(e,t)});const y=i&&c("attr");Array.from(e.querySelectorAll("*")).forEach(e=>{Array.from(e.attributes).forEach(t=>{if(t.name.startsWith("x-")&&!["x-for","x-key","x-class","x-if","x-hidden"].includes(t.name)){const o=t.name,s=r.get(o);if(s&&"attribute"===s.target){const r=h(t.value.trim(),e);try{u&&console.log("[plugin]",`Executing ${o} with:`,{element:e,value:r,component:n}),s.execute(e,r,n)}catch(e){u&&console.error("[plugin]",`Error executing ${o}:`,e)}}else{const r=t.name.slice(2),n=h(t.value.trim(),e);y&&console.groupCollapsed("x-attr",e,r,"=",n),n?e.setAttribute(r,String(n)):e.removeAttribute(r),y&&console.groupEnd()}}})});const m=i&&c("class");m&&console.groupCollapsed("x-class");for(const[e,t]of n._xClassCache){m&&console.log("element →",e);const r=t.trim();if(r.includes("(")){const t=/\(\s*([^,]+?)\s*,\s*([^)]+?)\s*\)/g;n._xClassDynamicValues||(n._xClassDynamicValues=new Map);(n._xClassDynamicValues.get(e)||[]).forEach(t=>e.classList.remove(t));const o=new Set;let s;for(;s=t.exec(r);){const t=s[1].trim(),r=s[2].trim();let n;n=/^[\w\-\s]+$/.test(t)?t:h(t,e);const a=Boolean(h(r,e));let i=[];"string"==typeof n?i=n.split(/\s+/).filter(Boolean):Array.isArray(n)?i=n:n&&"object"==typeof n&&(i=Object.keys(n).filter(e=>n[e])),a&&i.forEach(t=>e.classList.add(t)),i.forEach(e=>o.add(e)),m&&console.log(` ${t} → [${i.join(" ")}] (= ${a})`)}n._xClassDynamicValues.set(e,Array.from(o))}else{n._xClassDynamicValues||(n._xClassDynamicValues=new Map);let t;(n._xClassDynamicValues.get(e)||[]).forEach(t=>e.classList.remove(t));let o=!0;if(!r.includes(",")&&/[?:]/.test(r))t=h(r,e);else{const n=r.indexOf(","),s=n>=0?r.slice(0,n).trim():r,a=n>=0?r.slice(n+1).trim():"true";o=Boolean(h(a,e)),t=s}const s=[];"string"==typeof t?s.push(...t.split(/\s+/).filter(Boolean)):Array.isArray(t)?s.push(...t):t&&"object"==typeof t&&s.push(...Object.keys(t).filter(e=>t[e])),s.forEach(t=>{o?e.classList.add(t):e.classList.remove(t)}),m&&console.log(` x-class ${r} →`,s,`ok=${o}`),n._xClassDynamicValues.set(e,s)}}m&&console.groupEnd();const _=i&&c("render");_&&console.groupCollapsed("render");for(const[e,t]of n._xRenderCache){const r=n._xIfData.get(e);if(r&&r.isHidden)continue;_&&console.log("element →",e,"expr=",t);const o=t.indexOf(","),s=o<0?t:t.slice(0,o).trim(),a=h(o<0?"true":t.slice(o+1).trim(),e);let i;if(a){let t=h(s,e);if(void 0===t)i=n._xRenderOriginalText.get(e)||"";else if(null!=t&&"object"==typeof t)try{i=JSON.stringify(t,null,2)}catch{i=String(t)}else i=null==t||"boolean"==typeof t?"":String(t)}else i=n._xRenderOriginalText.get(e)||"";e.textContent!==i&&(e.textContent=i),_&&console.log(` → "${i}" (cond=${a})`)}_&&console.groupEnd(),Array.from(e.querySelectorAll("*")).forEach(e=>{const t=n._xIfData.get(e);t&&t.isHidden||[["render","render"]].forEach(([t,r])=>{e.hasAttribute(t)&&!c(r)&&e.removeAttribute(t)})}),function e(r){Array.from(r.querySelectorAll("*")).forEach(e=>{Array.from(e.attributes).forEach(r=>{if(r.name.startsWith("@")){const o=r.name.slice(1),s=r.value.trim();e._xEventListeners&&e._xEventListeners[o]&&e.removeEventListener(o,e._xEventListeners[o]),e._xEventListeners||(e._xEventListeners={});const a=d(e),i=new Proxy(a,{get:(e,t)=>t in e?e[t]:n[t]}),l=function(e){try{return t(s,{this:i,event:e,...p})}catch(e){f(`[rendux @${o}] Error evaluating: ${s}`,e)}};e.addEventListener(o,l),e._xEventListeners[o]=l}})}),Array.from(r.querySelectorAll("slot")).forEach(t=>{(t.assignedElements?t.assignedElements({flatten:!0}):[]).forEach(t=>{e(t)})})}(e),s(e,n)}rendux.use=function(e){return n(e),rendux},"undefined"!=typeof module&&module.exports&&(module.exports={rendux:rendux,use:n,process:s,plugins:r,parsePluginCall:o}),exports.parsePluginCall=o,exports.plugins=r,exports.process=s,exports.rendux=rendux,exports.use=n;
@@ -0,0 +1,2 @@
1
+ /*! rendux v0.92.0 | Copyright (c) JULY 2025 Yannick J.A. CHARLERY | This software is licensed for non-commercial use only. Commercial licensing available upon request. For license details: https://github.com/ynck-chrl/rendux */
2
+ const e={Math:Math,Date:Date,Number:Number,String:String,Boolean:Boolean,JSON:JSON,parseInt:parseInt,parseFloat:parseFloat,isFinite:isFinite,Array:Array};function t(t,r={}){const n=function(e){const t=[];let r=0;for(;r<e.length;){let n=e[r];if(/\s/.test(n)){r++;continue}if('"'===n||"'"===n){const o=n;let s="";for(r++;r<e.length&&e[r]!==o;)"\\"===e[r]?(r++,r<e.length&&(s+=e[r++])):s+=e[r++];r++,t.push({type:"string",value:s});continue}if(/[0-9]/.test(n)||"."===n&&/[0-9]/.test(e[r+1])){let n=r;for(;r<e.length&&/[0-9]/.test(e[r]);)r++;if("."===e[r])for(r++;r<e.length&&/[0-9]/.test(e[r]);)r++;const o=parseFloat(e.slice(n,r));t.push({type:"number",value:o});continue}if(/[a-zA-Z_$]/.test(n)){let n=r;for(r++;r<e.length&&/[a-zA-Z0-9_$]/.test(e[r]);)r++;const o=e.slice(n,r);"true"===o||"false"===o?t.push({type:"boolean",value:"true"===o}):"null"===o?t.push({type:"null",value:null}):t.push({type:"identifier",value:o});continue}const o=e.substr(r,2),s=e.substr(r,3);if(["===","!=="].includes(s))t.push({type:"operator",value:s}),r+=3;else if(["==","!=",">=","<=","&&","||"].includes(o))t.push({type:"operator",value:o}),r+=2;else{if(!["+","-","*","/","%",">","<","!","?",":","(",")","[","]",".",","].includes(n))throw new Error(`Invalid character '${n}' in expression`);t.push({type:"operator",value:n}),r++}}return t.push({type:"EOF"}),t}(t);let o=0;function s(){return n[o]||{type:"EOF"}}function a(){return n[o++]||{type:"EOF"}}function i(e,t){const r=s();if(r.type!==e||void 0!==t&&r.value!==t)throw new Error(`Expected ${t||e} but got ${r.value}`);return a(),r}function l(e){const t=s();return"operator"===t.type&&t.value===e}const c=u();if("EOF"!==s().type)throw new Error(`Unexpected token: ${s().value}`);return c;function u(){let e=function(){let e=f();for(;l("||");){a();const t=f();e=e||t}return e}();if(l("?")){a();const t=u();i("operator",":");const r=u();e=e?t:r}return e}function f(){let e=p();for(;l("&&");){a();const t=p();e=e&&t}return e}function p(){let e=d();for(;["==","!=","===","!=="].includes(s().value);){const t=a().value,r=d();switch(t){case"==":e=e==r;break;case"!=":e=e!=r;break;case"===":e=e===r;break;case"!==":e=e!==r}}return e}function d(){let e=h();for(;[">","<",">=","<="].includes(s().value);){const t=a().value,r=h();switch(t){case">":e=e>r;break;case"<":e=e<r;break;case">=":e=e>=r;break;case"<=":e=e<=r}}return e}function h(){let e=x();for(;["+","-"].includes(s().value);){const t=a().value,r=x();e="+"===t?e+r:e-r}return e}function x(){let e=g();for(;["*","/","%"].includes(s().value);){const t=a().value,r=g();switch(t){case"*":e*=r;break;case"/":e/=r;break;case"%":e%=r}}return e}function g(){if(["!","+","-"].includes(s().value)){const e=a().value,t=g();switch(e){case"!":return!t;case"+":return+t;case"-":return-t}}return function(){const t=s();if("number"===t.type||"string"===t.type||"boolean"===t.type||"null"===t.type)return a(),t.value;if("identifier"===t.type){a();let n=function(t){if(t in r)return r[t];if(t in e)return e[t];if(null!=r.this&&t in r.this)return r.this[t];throw new Error(`Unknown identifier: ${t}`)}(t.value);for(;;)if(l(".")){a();const e=i("identifier").value;n=null==n?void 0:n[e]}else if(l("[")){a();const e=u();i("operator","]"),n=null==n?void 0:n[e]}else{if(!l("("))break;{a();const e=[];if(!l(")"))do{e.push(u())}while(l(",")&&a());if(i("operator",")"),"function"!=typeof n)throw new Error(`'${t.value}' is not a function`);n=n.apply(r.this,e)}}return n}if(l("(")){a();const e=u();return i("operator",")"),e}throw new Error(`Unexpected token: ${t.value}`)}()}}const r=new Map;function n(e){if(!e||!e.name)throw new Error("Invalid plugin");r.set(e.name,e)}function o(e,t={}){const n=e.match(/^([a-zA-Z_$][\w$]*)\s*\(\s*(.*)\s*\)$/);if(!n)return null;const o=n[1],s=r.get(o);if(!s)return null;const a=n[2].trim();if(""===a)return{plugin:s,args:[]};return{plugin:s,args:a.split(/\s*,\s*/).map(e=>{if(e.startsWith('"')&&e.endsWith('"')||e.startsWith("'")&&e.endsWith("'"))return e.slice(1,-1);if(/^-?\d+(?:\.\d+)?$/.test(e))return parseFloat(e);if(e in t)return t[e];throw new Error(`Unknown identifier: ${e}`)})}}async function s(e=document,t={}){const n=Array.from(e.querySelectorAll("*")).filter(e=>Array.from(e.attributes).some(e=>e.name.startsWith("render.")));for(const e of n)for(const n of Array.from(e.attributes)){if(!n.name.startsWith("render."))continue;const s=n.name,a=n.value.trim();let i,l,c;if("render.plugin"===s){let e;try{e=o(a,t)}catch(e){console.error(e);continue}if(!e)continue;i=e.plugin,l=e.args}else{const e=s.slice(7);if(i=r.get(e),!i)continue;if(a.startsWith(e+"(")){let e;try{e=o(a,t)}catch(e){console.error(e);continue}if(!e)continue;i=e.plugin,l=e.args}else l=[a]}"function"==typeof i.onBeforeExecute&&i.onBeforeExecute(e,a,t);try{c=i.execute(...l)}catch(e){continue}e.textContent=null==c?"":String(c),"function"==typeof i.onAfterExecute&&i.onAfterExecute(e,a,t)}for(const n of r.values())"function"==typeof n.onAfterRender&&await n.onAfterRender(e,t)}function rendux(e=this.shadowRoot||this){const n=this;for(const t of r.values())"function"==typeof t.onBeforeRender&&t.onBeforeRender(e,n);const o=document,a=n.getAttribute("logs"),i=Boolean(a),l=a?new Set(a.split(",").map(e=>e.trim().toLowerCase())):new Set,c=e=>l.has("all")||l.has(e.toLowerCase())||l.has(("x-"+e).toLowerCase()),u=i&&l.has("plugins"),f=(...e)=>console.warn(...e);n._xRenderCache||(n._xRenderCache=new Map),n._xRenderOriginalText||(n._xRenderOriginalText=new WeakMap),n._xIfData||(n._xIfData=new WeakMap),n._cloneContext||(n._cloneContext=new WeakMap),n._xClassCache||(n._xClassCache=new Map),n._xForCache||(n._xForCache=new WeakMap),n.__mirrorContainer||(n.__mirrorContainer=o.createDocumentFragment());const p={};function d(e){let t=e;for(;t;){const e=n._cloneContext.get(t);if(e)return n.getAttribute("logs")&&n.getAttribute("logs").includes("plugins")&&console.log("Found context for element:",t,e),e;t=t.parentNode}return n.getAttribute("logs")&&n.getAttribute("logs").includes("plugins")&&console.log("Using default component context:",n),n}function h(e,r){if(!e)return!0;const n=d(r);try{return t(e,{this:n,...p})}catch(t){return void f(`[evaluate] "${e}" failed in`,n,t)}}Object.getOwnPropertyNames(Object.getPrototypeOf(n)).forEach(e=>{"function"==typeof n[e]&&"constructor"!==e&&(p[e]=n[e].bind(n))}),p.plugins=r;const x=i&&c("for");x&&console.groupCollapsed("x-for");Array.from(e.querySelectorAll("template[x-for]")).concat(Array.from(n.__mirrorContainer.querySelectorAll("template[x-for]"))).forEach(e=>{let t,r,s;e._xForMeta||(e._xForMeta={parent:e.parentNode,next:e.nextSibling},n.__mirrorContainer.appendChild(e));try{({loopVar:t,indexVar:r,arrayPath:s}=function(e){const t=e.match(/^\s*(?:\(\s*([^,\s]+)\s*,\s*([^,\s]+)\s*\)|([^,\s()]+))\s+(?:in|of)\s+(.+)$/);if(!t)throw new Error("Invalid x-for: "+e);return{loopVar:t[1]||t[3],indexVar:t[2]||null,arrayPath:t[4].trim()}}(e.getAttribute("x-for")))}catch(e){return void f("[x-for]",e.message)}const a=function(e,t){const r=d(t),n=e.match(/([^[.\]]+)|\[(\d+)\]/g);if(n)return n.reduce((e,t)=>{if(null!=e){if(t.startsWith("[")){const r=parseInt(t.slice(1,-1),10);return Array.isArray(e)?e[r]:void 0}return e[t]}},r)}(s,e);if(!Array.isArray(a))return void f(`[x-for] expected array at ${s}`,a);x&&console.log(`iterating ${s} → length ${a.length}`);const i=function(e){return JSON.stringify(e)}(a),l=n._xForCache.get(e);l&&l.arrayRef===a&&l.length===a.length&&l.signature===i?x&&console.log(" → skipped (no change)"):(n._xForCache.set(e,{arrayRef:a,length:a.length,signature:i}),e._forClones&&e._forClones.forEach(e=>e.remove()),e._forClones=[],a.forEach((s,a)=>{x&&console.groupCollapsed(` index ${a}`);const i=o.createDocumentFragment();i.appendChild(e.content.cloneNode(!0));const l=(c=t,u=s,f=r,p=a,new Proxy(n,{has:(e,t)=>t===c||f&&t===f||t in e,get:(e,t)=>t===c?u:f&&t===f?p:e[t]}));var c,u,f,p,d,h;d=i,h=l,n._cloneContext.set(d,h),d.querySelectorAll("*").forEach(e=>n._cloneContext.set(e,h));const g=n._xRenderCache,y=n._xRenderOriginalText;for(n._xRenderCache=new Map,n._xRenderOriginalText=new WeakMap,rendux.call(n,i),n._xRenderCache=g,n._xRenderOriginalText=y;i.firstChild;){const t=i.firstChild;e._xForMeta.parent.insertBefore(t,e._xForMeta.next),e._forClones.push(t)}x&&console.groupEnd()}))}),x&&console.groupEnd();const g=i&&c("if");Array.from(e.querySelectorAll("[x-if]")).concat(Array.from(n.__mirrorContainer.querySelectorAll("[x-if]"))).forEach(e=>{const t=e.getAttribute("x-if")?.trim(),r=!t||h(t,e);g&&console.groupCollapsed("x-if",e,t,"→",r);let s=n._xIfData.get(e);s||(s={placeholder:o.createComment("x-if placeholder"),isHidden:!1},n._xIfData.set(e,s)),r||s.isHidden?r&&s.isHidden&&(s.placeholder.parentNode.replaceChild(e,s.placeholder),s.isHidden=!1):(e.parentNode.replaceChild(s.placeholder,e),n.__mirrorContainer.appendChild(e),s.isHidden=!0),g&&console.groupEnd()}),0===n._xRenderCache.size&&Array.from(e.querySelectorAll("[render]")).forEach(e=>{const t=e.getAttribute("render")?.trim();t&&(n._xRenderCache.set(e,t),n._xRenderOriginalText.set(e,e.textContent))}),Array.from(e.querySelectorAll("[x-class]")).forEach(e=>{const t=e.getAttribute("x-class");null==t||n._xClassCache.has(e)||n._xClassCache.set(e,t)});const y=i&&c("attr");Array.from(e.querySelectorAll("*")).forEach(e=>{Array.from(e.attributes).forEach(t=>{if(t.name.startsWith("x-")&&!["x-for","x-key","x-class","x-if","x-hidden"].includes(t.name)){const o=t.name,s=r.get(o);if(s&&"attribute"===s.target){const r=h(t.value.trim(),e);try{u&&console.log("[plugin]",`Executing ${o} with:`,{element:e,value:r,component:n}),s.execute(e,r,n)}catch(e){u&&console.error("[plugin]",`Error executing ${o}:`,e)}}else{const r=t.name.slice(2),n=h(t.value.trim(),e);y&&console.groupCollapsed("x-attr",e,r,"=",n),n?e.setAttribute(r,String(n)):e.removeAttribute(r),y&&console.groupEnd()}}})});const m=i&&c("class");m&&console.groupCollapsed("x-class");for(const[e,t]of n._xClassCache){m&&console.log("element →",e);const r=t.trim();if(r.includes("(")){const t=/\(\s*([^,]+?)\s*,\s*([^)]+?)\s*\)/g;n._xClassDynamicValues||(n._xClassDynamicValues=new Map);(n._xClassDynamicValues.get(e)||[]).forEach(t=>e.classList.remove(t));const o=new Set;let s;for(;s=t.exec(r);){const t=s[1].trim(),r=s[2].trim();let n;n=/^[\w\-\s]+$/.test(t)?t:h(t,e);const a=Boolean(h(r,e));let i=[];"string"==typeof n?i=n.split(/\s+/).filter(Boolean):Array.isArray(n)?i=n:n&&"object"==typeof n&&(i=Object.keys(n).filter(e=>n[e])),a&&i.forEach(t=>e.classList.add(t)),i.forEach(e=>o.add(e)),m&&console.log(` ${t} → [${i.join(" ")}] (= ${a})`)}n._xClassDynamicValues.set(e,Array.from(o))}else{n._xClassDynamicValues||(n._xClassDynamicValues=new Map);let t;(n._xClassDynamicValues.get(e)||[]).forEach(t=>e.classList.remove(t));let o=!0;if(!r.includes(",")&&/[?:]/.test(r))t=h(r,e);else{const n=r.indexOf(","),s=n>=0?r.slice(0,n).trim():r,a=n>=0?r.slice(n+1).trim():"true";o=Boolean(h(a,e)),t=s}const s=[];"string"==typeof t?s.push(...t.split(/\s+/).filter(Boolean)):Array.isArray(t)?s.push(...t):t&&"object"==typeof t&&s.push(...Object.keys(t).filter(e=>t[e])),s.forEach(t=>{o?e.classList.add(t):e.classList.remove(t)}),m&&console.log(` x-class ${r} →`,s,`ok=${o}`),n._xClassDynamicValues.set(e,s)}}m&&console.groupEnd();const _=i&&c("render");_&&console.groupCollapsed("render");for(const[e,t]of n._xRenderCache){const r=n._xIfData.get(e);if(r&&r.isHidden)continue;_&&console.log("element →",e,"expr=",t);const o=t.indexOf(","),s=o<0?t:t.slice(0,o).trim(),a=h(o<0?"true":t.slice(o+1).trim(),e);let i;if(a){let t=h(s,e);if(void 0===t)i=n._xRenderOriginalText.get(e)||"";else if(null!=t&&"object"==typeof t)try{i=JSON.stringify(t,null,2)}catch{i=String(t)}else i=null==t||"boolean"==typeof t?"":String(t)}else i=n._xRenderOriginalText.get(e)||"";e.textContent!==i&&(e.textContent=i),_&&console.log(` → "${i}" (cond=${a})`)}_&&console.groupEnd(),Array.from(e.querySelectorAll("*")).forEach(e=>{const t=n._xIfData.get(e);t&&t.isHidden||[["render","render"]].forEach(([t,r])=>{e.hasAttribute(t)&&!c(r)&&e.removeAttribute(t)})}),function e(r){Array.from(r.querySelectorAll("*")).forEach(e=>{Array.from(e.attributes).forEach(r=>{if(r.name.startsWith("@")){const o=r.name.slice(1),s=r.value.trim();e._xEventListeners&&e._xEventListeners[o]&&e.removeEventListener(o,e._xEventListeners[o]),e._xEventListeners||(e._xEventListeners={});const a=d(e),i=new Proxy(a,{get:(e,t)=>t in e?e[t]:n[t]}),l=function(e){try{return t(s,{this:i,event:e,...p})}catch(e){f(`[rendux @${o}] Error evaluating: ${s}`,e)}};e.addEventListener(o,l),e._xEventListeners[o]=l}})}),Array.from(r.querySelectorAll("slot")).forEach(t=>{(t.assignedElements?t.assignedElements({flatten:!0}):[]).forEach(t=>{e(t)})})}(e),s(e,n)}rendux.use=function(e){return n(e),rendux},"undefined"!=typeof module&&module.exports&&(module.exports={rendux:rendux,use:n,process:s,plugins:r,parsePluginCall:o});export{o as parsePluginCall,r as plugins,s as process,rendux,n as use};
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@ynck/rendux",
3
+ "version": "0.92.0",
4
+ "type": "module",
5
+ "main": "./dist/rendux.cjs.min.js",
6
+ "module": "./dist/rendux.min.js",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/rendux.min.js",
10
+ "require": "./dist/rendux.cjs.min.js"
11
+ },
12
+ "./plugin/x-tooltip": {
13
+ "import": "./dist/plugin/x-tooltip.min.js",
14
+ "require": "./dist/plugin/x-tooltip.cjs.min.js"
15
+ },
16
+ "./plugin/i18n": {
17
+ "import": "./dist/plugin/i18n.min.js",
18
+ "require": "./dist/plugin/i18n.cjs.min.js"
19
+ }
20
+ },
21
+ "description": "A lightweight dependency-free templating and rendering library for HTML and Web Components",
22
+ "author": "Yannick J.A. CHARLERY",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/ynck-chrl/rendux"
26
+ },
27
+ "homepage": "https://github.com/ynck-chrl/rendux",
28
+ "bugs": {
29
+ "url": "https://github.com/ynck-chrl/rendux/issues"
30
+ },
31
+ "sideEffects": false,
32
+ "engines": {
33
+ "node": ">=18.0.0"
34
+ },
35
+ "license": "SEE LICENSE IN LICENSE-NONCOMMERCIAL.md",
36
+ "files": [
37
+ "**/*"
38
+ ]
39
+ }
Binary file