|
5 | 5 | * |
6 | 6 | * All custom tags MUST include a unique 'id' attribute. |
7 | 7 | * Most attributes/classes can be combined for powerful UI behaviors. |
8 | | - * See below for supported elements, attributes, and their usage. |
| 8 | + * Inline macros can be used via the 'inline' attribute for dynamic, per-element logic. |
| 9 | + * See below for supported elements, attributes, and inline macro usage. |
9 | 10 | * |
10 | 11 | * ────────────────────────────────────────────────────────────── |
11 | 12 | * CUSTOM TAGS |
|
30 | 31 | * UNIVERSAL ATTRIBUTES |
31 | 32 | * |
32 | 33 | * id REQUIRED for all custom tags. Must be unique. |
| 34 | + * inline Optional inline macro string to define dynamic logic per element. |
| 35 | + * Supports operators: |, |!, |$, |$id:varName, nop:varName, |@id.prop:varName, |#varName:id.prop, |%funcName:[args] |
33 | 36 | * ajax Fetch remote resource (HTML, JSON, etc.) for tag. |
34 | 37 | * insert Target ID to render AJAX response. |
35 | 38 | * query Key-value pairs for AJAX requests, e.g. "key:value&" |
|
76 | 79 | * lazy-load For <csv>; enable/disable lazy loading (default true). |
77 | 80 | * |
78 | 81 | * ────────────────────────────────────────────────────────────── |
| 82 | + * INLINE MACROS |
| 83 | + * |
| 84 | + * The 'inline' attribute allows per-element dynamic logic using dotPipe pipe operators: |
| 85 | + * |
| 86 | + * Operators: |
| 87 | + * | Self-retained (current element context passes forward) |
| 88 | + * |! Pass previous return value forward |
| 89 | + * |$id Inject current value into element by ID |
| 90 | + * |$id:varName Inject stored variable into element by ID |
| 91 | + * nop:varName Store current value in dpVars for later use |
| 92 | + * |@id.prop:varName Write stored variable into target element property |
| 93 | + * |#varName:id.prop Read element property into variable |
| 94 | + * |%funcName:[args] Execute function with arguments (supports #varName and @id.prop) |
| 95 | + * |
| 96 | + * Example usage: |
| 97 | + * <div id="fa" inline="ajax:/api/new:GET|!nop:newData|$resultsDiv:newData"></div> |
| 98 | + * <div id="fb" inline="ajax:/api/list:GET|!nop:list|%processData:[#list,@outputDiv.innerText]|@outputDiv.innerText:list"></div> |
| 99 | + * |
| 100 | + * Inline macros can be automatically executed on DOMContentLoaded or triggered manually via: |
| 101 | + * dotPipe.runInline('fa'); |
| 102 | + * |
| 103 | + * Variables used in inline macros are scoped per element in a dpVars object. |
| 104 | + * Functions called with |% can be native, plugin-registered, or globally available. |
| 105 | + * |
| 106 | + * ────────────────────────────────────────────────────────────── |
79 | 107 | * SUPPORT CLASSES |
80 | 108 | * |
81 | 109 | * download Enables file download behavior. |
|
123 | 151 | * <item id="gadget-2" name="Gadget" price="14.99"></item> |
124 | 152 | * </cart> |
125 | 153 | * |
126 | | - * <!-- Search box for filtering content --> |
127 | | - * <search id="search" use-id="product-list;order-list" placeholder="Find products..."></search> |
| 154 | + * <!-- Inline macros --> |
| 155 | + * <div id="fa" inline="ajax:/api/new:GET|!nop:newData|$resultsDiv:newData"></div> |
| 156 | + * <div id="fb" inline="ajax:/api/list:GET|!nop:list|%processData:[#list,@outputDiv.innerText]|@outputDiv.innerText:list"></div> |
128 | 157 | * |
129 | 158 | * <!-- Carousel slider --> |
130 | 159 | * <carousel id="image-carousel" sources="img1.jpg;img2.jpg;img3.jpg" delay="3000" boxes="1"></carousel> |
|
135 | 164 | * ────────────────────────────────────────────────────────────── |
136 | 165 | * SYSTEM FLOW: |
137 | 166 | * - On DOMContentLoaded, dotPipe processes all supported custom tags. |
138 | | - * - pipes() manages all custom tag logic, triggers AJAX, updates DOM, and runs callbacks. |
| 167 | + * - pipes() manages all custom tag logic, triggers AJAX, updates DOM, runs callbacks, and executes inline macros. |
| 168 | + * - Inline macros are parsed via regex, support all pipe operators, and store variables per element in dpVars. |
139 | 169 | * - navigate() performs AJAX requests and inserts responses. |
140 | 170 | * - modala() loads and renders JSON templates for modals and complex UIs. |
141 | 171 | * |
142 | | - * For advanced usage, refer to the full documentation or source code. |
143 | | - * |
144 | 172 | * (c) dotPipe.js – https://github.com/dotpipe/dotPipe |
145 | 173 | */ |
146 | 174 |
|
| 175 | + |
147 | 176 | document.addEventListener("DOMContentLoaded", function () { |
148 | 177 | try { |
149 | 178 | if (document.body != null && JSON.parse(document.body.textContent)) { |
@@ -178,6 +207,136 @@ document.addEventListener("DOMContentLoaded", function () { |
178 | 207 | }); |
179 | 208 | }); |
180 | 209 |
|
| 210 | +const dotPipe = { |
| 211 | + matrix: {}, |
| 212 | + |
| 213 | + // Register all elements with inline macros |
| 214 | + register: function(selector = '[inline]') { |
| 215 | + const elements = document.querySelectorAll(selector); |
| 216 | + elements.forEach(el => { |
| 217 | + const key = el.id || el.getAttribute('pipe') || Symbol(); |
| 218 | + this.matrix[key] = { |
| 219 | + element: el, |
| 220 | + inlineMacro: el.getAttribute('inline'), |
| 221 | + dpVars: {} // plain storage; no auto-run |
| 222 | + }; |
| 223 | + }); |
| 224 | + }, |
| 225 | + |
| 226 | + // Run inline macro for a given element manually |
| 227 | + runInline: async function(key) { |
| 228 | + const entry = this.matrix[key]; |
| 229 | + if (!entry || !entry.inlineMacro) return; |
| 230 | + |
| 231 | + let currentValue = null; |
| 232 | + const segments = entry.inlineMacro.split('|').filter(s => s.trim() !== ''); |
| 233 | + |
| 234 | + for (let seg of segments) { |
| 235 | + seg = seg.trim(); |
| 236 | + let m; |
| 237 | + |
| 238 | + // |&varName:value → store literal |
| 239 | + if (m = /^\&([a-zA-Z0-9_]+):(.+)$/.exec(seg)) { |
| 240 | + const varName = m[1]; |
| 241 | + const value = m[2]; |
| 242 | + entry.dpVars[varName] = value; |
| 243 | + currentValue = value; |
| 244 | + continue; |
| 245 | + } |
| 246 | + |
| 247 | + // nop:varName → store current value |
| 248 | + if (m = /^nop:([a-zA-Z0-9_]+)$/.exec(seg)) { |
| 249 | + const varName = m[1]; |
| 250 | + entry.dpVars[varName] = currentValue; |
| 251 | + continue; |
| 252 | + } |
| 253 | + |
| 254 | + // $id or $id:varName → inject value into element |
| 255 | + if (m = /^\$([a-zA-Z0-9_-]+)(?::([a-zA-Z0-9_!]+))?$/.exec(seg)) { |
| 256 | + const targetId = m[1]; |
| 257 | + const varName = m[2]; |
| 258 | + const targetEl = document.getElementById(targetId); |
| 259 | + if (targetEl) { |
| 260 | + let value; |
| 261 | + if (!varName) value = currentValue; |
| 262 | + else if (varName.startsWith('!')) value = entry.dpVars[varName.slice(1)]; |
| 263 | + else value = entry.dpVars[varName]; |
| 264 | + targetEl.innerHTML = value; |
| 265 | + } |
| 266 | + continue; |
| 267 | + } |
| 268 | + |
| 269 | + // @id.prop:varName → set element property |
| 270 | + if (m = /^@([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_]+):([a-zA-Z0-9_!]+)$/.exec(seg)) { |
| 271 | + const targetEl = document.getElementById(m[1]); |
| 272 | + const prop = m[2]; |
| 273 | + const varName = m[3]; |
| 274 | + if (targetEl) { |
| 275 | + let value = varName.startsWith('!') ? entry.dpVars[varName.slice(1)] : entry.dpVars[varName]; |
| 276 | + targetEl[prop] = value; |
| 277 | + } |
| 278 | + continue; |
| 279 | + } |
| 280 | + |
| 281 | + // #varName:id.prop → read element property |
| 282 | + if (m = /^#([a-zA-Z0-9_]+):([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_]+)$/.exec(seg)) { |
| 283 | + const varName = m[1]; |
| 284 | + const targetEl = document.getElementById(m[2]); |
| 285 | + const prop = m[3]; |
| 286 | + if (targetEl) { |
| 287 | + entry.dpVars[varName] = targetEl[prop]; |
| 288 | + currentValue = entry.dpVars[varName]; |
| 289 | + } |
| 290 | + continue; |
| 291 | + } |
| 292 | + |
| 293 | + // %funcName:[args] → call function |
| 294 | + if (m = /^\%([a-zA-Z0-9_]+):\[(.+)\]$/.exec(seg)) { |
| 295 | + const funcName = m[1]; |
| 296 | + let args = m[2].split(',').map(a => a.trim()).map(arg => { |
| 297 | + if (arg.startsWith('!')) return entry.dpVars[arg.slice(1)]; |
| 298 | + if (arg.startsWith('#')) return entry.dpVars[arg.slice(1)]; |
| 299 | + if (arg.startsWith('@')) { |
| 300 | + const [elId, prop] = arg.slice(1).split('.'); |
| 301 | + const targetEl = document.getElementById(elId); |
| 302 | + return targetEl ? targetEl[prop] : undefined; |
| 303 | + } |
| 304 | + return arg; |
| 305 | + }); |
| 306 | + if (typeof window[funcName] === 'function') { |
| 307 | + currentValue = await window[funcName](...args); |
| 308 | + } |
| 309 | + continue; |
| 310 | + } |
| 311 | + |
| 312 | + // Standard verb: e.g., ajax:url:GET |
| 313 | + if (m = /^([a-zA-Z0-9_]+):?(.*)$/.exec(seg)) { |
| 314 | + const verb = m[1]; |
| 315 | + const params = m[2] ? m[2].split(':') : []; |
| 316 | + if (typeof this.verbs[verb] === 'function') { |
| 317 | + currentValue = await this.verbs[verb](...params); |
| 318 | + } |
| 319 | + continue; |
| 320 | + } |
| 321 | + |
| 322 | + console.warn('Unknown pipe segment:', seg); |
| 323 | + } |
| 324 | + }, |
| 325 | + |
| 326 | + // Built-in verbs (AJAX, log, etc.) |
| 327 | + verbs: { |
| 328 | + async ajax(url, method = 'GET') { |
| 329 | + const res = await fetch(url, { method }); |
| 330 | + const text = await res.text(); |
| 331 | + return text; |
| 332 | + }, |
| 333 | + log(value) { |
| 334 | + console.log(value); |
| 335 | + return value; |
| 336 | + } |
| 337 | + } |
| 338 | +}; |
| 339 | + |
181 | 340 | let domContentLoad = (again = false) => { |
182 | 341 | doc_set = document.getElementsByTagName("pipe"); |
183 | 342 | if (again == false) { |
@@ -220,7 +379,7 @@ let domContentLoad = (again = false) => { |
220 | 379 | processCartTags(); |
221 | 380 | processOrderConfirmationTags(); |
222 | 381 | processColumnsTags(); |
223 | | - |
| 382 | + dotPipe.register(); |
224 | 383 |
|
225 | 384 | let elements_Carousel = document.getElementsByTagName("carousel"); |
226 | 385 | Array.from(elements_Carousel).forEach(function (elem) { |
@@ -5172,6 +5331,9 @@ function pipes(elem, stop = false) { |
5172 | 5331 | if (elem.id === null) |
5173 | 5332 | return; |
5174 | 5333 |
|
| 5334 | + if (elem.hasAttribute("inline")) |
| 5335 | + dotPipe.runInline(elem.id); |
| 5336 | + |
5175 | 5337 | if (elem.hasAttribute("callback") && typeof window[elem.getAttribute("callback")] === "function") { |
5176 | 5338 | var params = []; |
5177 | 5339 | const calls = sortNodesByName("." + elem.getAttribute("callback-class")); |
|
0 commit comments