talkdom 0.1.3 → 0.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/README.md +35 -1
- package/dist/talkdom.min.js +1 -1
- package/dist/talkdom.min.js.map +1 -1
- package/index.js +40 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,6 +45,7 @@ args: ["/partial", "inner"]
|
|
|
45
45
|
- Lifecycle events (`talkdom:done`, `talkdom:error`) on receiver elements
|
|
46
46
|
- Programmatic API via `talkDOM.send` (returns a promise)
|
|
47
47
|
- Extensible methods via `talkDOM.methods`
|
|
48
|
+
- Configurable max pollers via `talkDOM.maxPollers`
|
|
48
49
|
|
|
49
50
|
## Usage
|
|
50
51
|
|
|
@@ -103,7 +104,11 @@ Receivers poll by adding `poll:` as the last keyword with an interval (`s` or `m
|
|
|
103
104
|
<div receiver="feed get:apply: /updates inner poll: 10s"></div>
|
|
104
105
|
```
|
|
105
106
|
|
|
106
|
-
Polling stops automatically when the element is removed from the DOM.
|
|
107
|
+
Polling stops automatically when the element is removed from the DOM. A maximum of 64 concurrent pollers is enforced by default. Adjust via:
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
talkDOM.maxPollers = 128;
|
|
111
|
+
```
|
|
107
112
|
|
|
108
113
|
## Persist
|
|
109
114
|
|
|
@@ -218,6 +223,35 @@ talkDOM.methods["show:"] = function (el, message) {
|
|
|
218
223
|
};
|
|
219
224
|
```
|
|
220
225
|
|
|
226
|
+
## Security
|
|
227
|
+
|
|
228
|
+
talkDOM does **not** sanitize HTML. Content from `get:apply:`, `post:apply:`, server triggers, and piped `apply:` is inserted via `innerHTML` / `insertAdjacentHTML` / `outerHTML` as-is. You are responsible for ensuring that server responses do not contain untrusted markup.
|
|
229
|
+
|
|
230
|
+
The `persist` attribute stores receiver content in `localStorage` in plain text. Do not use it for sensitive data.
|
|
231
|
+
|
|
232
|
+
CSRF tokens are read from `<meta name="csrf-token">` and sent automatically on non-GET requests. Make sure this tag is present if your server requires CSRF protection.
|
|
233
|
+
|
|
234
|
+
## Browser compatibility
|
|
235
|
+
|
|
236
|
+
talkDOM works in all modern browsers. No polyfills needed.
|
|
237
|
+
|
|
238
|
+
| Browser | Minimum version |
|
|
239
|
+
|---------|-----------------|
|
|
240
|
+
| Chrome | 51+ |
|
|
241
|
+
| Firefox | 49+ |
|
|
242
|
+
| Safari | 10+ |
|
|
243
|
+
| Edge | 79+ (Chromium) |
|
|
244
|
+
|
|
245
|
+
IE is not supported.
|
|
246
|
+
|
|
247
|
+
## Performance
|
|
248
|
+
|
|
249
|
+
Receiver lookups are cached and invalidated automatically via `MutationObserver`. Repeated dispatches to the same receiver name within a stable DOM hit the cache.
|
|
250
|
+
|
|
251
|
+
Polling is capped at 64 concurrent pollers by default (configurable via `talkDOM.maxPollers`). Pollers clean up automatically when their element is removed from the DOM.
|
|
252
|
+
|
|
253
|
+
For most pages, talkDOM adds negligible overhead. On pages with thousands of receivers, keep in mind that `querySelectorAll` runs once per unique receiver name per DOM mutation cycle.
|
|
254
|
+
|
|
221
255
|
## License
|
|
222
256
|
|
|
223
257
|
MIT. See [LICENSE](LICENSE).
|
package/dist/talkdom.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(){function e(e){for(var t=e.trim(),r=t.split(/\s+/),n=r[0],o=t.substring(n.length).trim(),i=r.slice(1),c=[],u=[],s=[],a=0;a<i.length;a++){var l=i[a];l.endsWith(":")?(c.length>0&&s.length>0?(u.push(s.join(" ")),s=[]):c.length>0&&u.push(""),c.push(l)):s.push(l)}return c.length>0&&u.push(s.join(" ")),{receiver:n,selector:c.join(""),keywords:c,args:u,body:o}}function t(e){return e.getAttribute("receiver").trim().split(/\s+/)[0]}function
|
|
1
|
+
!function(){function e(e){for(var t=e.trim(),r=t.split(/\s+/),n=r[0],o=t.substring(n.length).trim(),i=r.slice(1),c=[],u=[],s=[],a=0;a<i.length;a++){var l=i[a];l.endsWith(":")?(c.length>0&&s.length>0?(u.push(s.join(" ")),s=[]):c.length>0&&u.push(""),c.push(l)):s.push(l)}return c.length>0&&u.push(s.join(" ")),{receiver:n,selector:c.join(""),keywords:c,args:u,body:o}}function t(e){return e.getAttribute("receiver").trim().split(/\s+/)[0]}var r={},n=!1;function o(e){if(n||(r={},n=!0),r[e])return r[e];var t=document.querySelectorAll('[receiver~="'+e+'"]');return r[e]=t,t}function i(e,r,n){if(function(e,t){var r=e.getAttribute("accepts");return!r||-1!==r.split(/\s+/).indexOf(t)}(e,r)){switch(r){case"inner":e.innerHTML=n;break;case"text":e.textContent=n;break;case"append":e.insertAdjacentHTML("beforeend",n);break;case"outer":e.outerHTML=n}return function(e,r){if(e.hasAttribute("receiver")&&e.hasAttribute("persist")){var n="talkDOM:"+t(e);"outer"===r?localStorage.setItem(n,JSON.stringify({op:r,content:e.outerHTML})):localStorage.setItem(n,JSON.stringify({op:r,content:e.innerHTML}))}}(e,r),n}console.error(t(e)+" does not accept "+r)}function c(e,t,r){var n,o={"X-TalkDOM-Request":"true","X-TalkDOM-Current-URL":location.href};if(r&&(o["X-TalkDOM-Receiver"]=r),"GET"!==e){var i=(n=document.querySelector('meta[name="csrf-token"]'))?n.getAttribute("content"):"";i?o["X-CSRF-Token"]=i:console.warn("talkDOM: no CSRF token found for "+e+" "+t)}return fetch(t,{method:e,headers:o}).then(function(r){if(!r.ok)return console.error("talkDOM: "+e+" "+t+" "+r.status),Promise.reject(r.status);var n=r.headers.get("X-TalkDOM-Trigger");return r.text().then(function(e){return n&&d(n),e})},function(r){return console.error("talkDOM: "+e+" "+t+" failed",r),Promise.reject(r)})}function u(e){return e.hasAttribute("receiver")?t(e):""}new MutationObserver(function(){n=!1}).observe(document,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["receiver"]});const s={"get:":function(e,t){return c("GET",t,u(e))},"post:":function(e,t){return c("POST",t,u(e))},"put:":function(e,t){return c("PUT",t,u(e))},"delete:":function(e,t){return c("DELETE",t,u(e))},"confirm:":function(e,t){if(!confirm(t))return Promise.reject("cancelled")},"apply:":function(e,t,r){return i(e,r,t)},"get:apply:":function(e,t,r){return c("GET",t,u(e)).then(function(t){return i(e,r,t)})},"post:apply:":function(e,t,r){return c("POST",t,u(e)).then(function(t){return i(e,r,t)})},"put:apply:":function(e,t,r){return c("PUT",t,u(e)).then(function(t){return i(e,r,t)})},"delete:apply:":function(e,t,r){return c("DELETE",t,u(e)).then(function(t){return i(e,r,t)})}};var a=!1;function l(e){e&&e.sender&&(a=!0,d(e.sender),a=!1)}function f(e,t){var r=o(e.receiver);if(0!==r.length){var n=s[e.selector];if(n){var i,c=void 0!==t?[t].concat(e.args):e.args;return r.forEach(function(t){var r={receiver:e.receiver,selector:e.selector,args:e.args},u=t.parentNode,s=t.nextElementSibling;function a(){return t.isConnected?t:(s&&s.isConnected?s.previousElementSibling:u&&u.isConnected?u.lastElementChild:null)||o(e.receiver)[0]}if((i=n(t,...c))&&"function"==typeof i.then)i.then(function(){var e=a();e&&e.dispatchEvent(new CustomEvent("talkdom:done",{bubbles:!0,detail:r}))},function(e){r.error=e;var t=a();t&&t.dispatchEvent(new CustomEvent("talkdom:error",{bubbles:!0,detail:r}))});else{var l=a();l&&l.dispatchEvent(new CustomEvent("talkdom:done",{bubbles:!0,detail:r}))}}),i}console.error(e.receiver+" does not understand "+e.selector)}else console.error(e.receiver+" not found")}function v(t){var r=t.split(";").map(function(t){var r=t.trim();if(!r)return Promise.resolve();var n=r.split("|").map(function(e){return e.trim()}).filter(Boolean);return 1===n.length?Promise.resolve(f(e(n[0]))):n.reduce(function(t,r){var n=e(r);return Promise.resolve(t).then(function(e){return f(n,e)})},void 0)});return Promise.all(r)}function d(e){v(e).catch(function(e){console.warn("talkDOM:",e)})}function h(t){var r=t.getAttribute("sender");d(r),a||function(t,r){if(t.hasAttribute("push-url")){var n=t.getAttribute("push-url");n||(n=e(r.split(";")[0].split("|")[0].trim()).args[0]||""),n&&location.pathname+location.search!==n&&history.pushState({sender:r},"",n)}}(t,r)}window.addEventListener("popstate",function(e){l(e.state)});var p=0,m=64;document.addEventListener("click",function(e){const t=e.target.closest("[sender]");t&&(e.preventDefault(),h(t))}),document.querySelectorAll("[persist]").forEach(function(e){if(e.hasAttribute("receiver")){var r=t(e),n=localStorage.getItem("talkDOM:"+r);if(n){var o;try{o=JSON.parse(n)}catch(e){return void localStorage.removeItem("talkDOM:"+r)}"outer"===o.op?e.outerHTML=o.content:e.innerHTML=o.content}}}),l(history.state),document.querySelectorAll("[receiver]").forEach(function(t){var r=e(t.getAttribute("receiver"));if("poll:"===r.keywords[r.keywords.length-1])if(p>=m)console.warn("talkDOM: max pollers ("+m+") reached, ignoring "+r.receiver);else{var n=function(e){var t=e.match(/^(\d+)(s|ms)$/);if(!t)return null;var r=parseInt(t[1],10);return"s"===t[2]?1e3*r:r}(r.args[r.args.length-1]);if(n){var i=r.keywords.slice(0,-1).join(""),c=r.args.slice(0,-1),u=r.receiver,a=o(u);p++;var l=setInterval(function(){if(!t.isConnected)return clearInterval(l),void p--;if(0!==a.length&&a[0].isConnected||(a=o(u)),0!==a.length){var e=s[i];e?a.forEach(function(t){e(t,...c)}):console.error(u+" does not understand "+i)}},n)}else console.error("poll: invalid interval for "+r.receiver)}}),window.talkDOM={methods:s,send:v,get maxPollers(){return m},set maxPollers(e){m=e}}}();
|
package/dist/talkdom.min.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["parseMessage","str","trimmed","trim","tokens","split","receiver","body","substring","length","rest","slice","keywords","args","currentArg","i","token","endsWith","push","join","selector","receiverName","el","getAttribute","findReceivers","name","document","querySelectorAll","apply","op","content","attr","indexOf","accepts","innerHTML","textContent","insertAdjacentHTML","outerHTML","hasAttribute","key","localStorage","setItem","JSON","stringify","persist","console","error","request","method","url","meta","headers","location","href","querySelector","warn","fetch","then","r","ok","status","Promise","reject","trigger","get","text","dispatchRaw","err","recName","methods","message","confirm","t","pushing","replayState","state","sender","send","msg","piped","els","
|
|
1
|
+
{"version":3,"names":["parseMessage","str","trimmed","trim","tokens","split","receiver","body","substring","length","rest","slice","keywords","args","currentArg","i","token","endsWith","push","join","selector","receiverName","el","getAttribute","receiverCache","cacheValid","findReceivers","name","result","document","querySelectorAll","apply","op","content","attr","indexOf","accepts","innerHTML","textContent","insertAdjacentHTML","outerHTML","hasAttribute","key","localStorage","setItem","JSON","stringify","persist","console","error","request","method","url","meta","headers","location","href","querySelector","warn","fetch","then","r","ok","status","Promise","reject","trigger","get","text","dispatchRaw","err","recName","MutationObserver","observe","childList","subtree","attributes","attributeFilter","methods","message","confirm","t","pushing","replayState","state","sender","send","msg","piped","els","undefined","concat","forEach","detail","parent","parentNode","next","nextElementSibling","resolveTarget","isConnected","previousElementSibling","lastElementChild","target","dispatchEvent","CustomEvent","bubbles","run","raw","chains","map","chain","resolve","steps","s","filter","Boolean","reduce","prev","step","all","catch","dispatch","senderEl","pathname","search","history","pushState","pushUrl","window","addEventListener","e","activePollers","maxPollers","closest","preventDefault","getItem","parse","removeItem","interval","match","n","parseInt","parseInterval","cachedTargets","id","setInterval","clearInterval","talkDOM"],"sources":["index.js"],"mappings":"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","ignoreList":[]}
|
package/index.js
CHANGED
|
@@ -38,9 +38,20 @@
|
|
|
38
38
|
return el.getAttribute("receiver").trim().split(/\s+/)[0];
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// Receiver cache: maps name -> NodeList, invalidated by DOM mutations.
|
|
42
|
+
var receiverCache = {};
|
|
43
|
+
var cacheValid = false;
|
|
44
|
+
|
|
45
|
+
new MutationObserver(function () { cacheValid = false; })
|
|
46
|
+
.observe(document, { childList: true, subtree: true, attributes: true, attributeFilter: ["receiver"] });
|
|
47
|
+
|
|
41
48
|
// Find all elements whose receiver attribute contains the given name.
|
|
42
49
|
function findReceivers(name) {
|
|
43
|
-
|
|
50
|
+
if (!cacheValid) { receiverCache = {}; cacheValid = true; }
|
|
51
|
+
if (receiverCache[name]) return receiverCache[name];
|
|
52
|
+
var result = document.querySelectorAll('[receiver~="' + name + '"]');
|
|
53
|
+
receiverCache[name] = result;
|
|
54
|
+
return result;
|
|
44
55
|
}
|
|
45
56
|
|
|
46
57
|
// Check if a receiver allows a given apply operation (inner, text, append, outer).
|
|
@@ -71,7 +82,7 @@
|
|
|
71
82
|
var raw = localStorage.getItem("talkDOM:" + name);
|
|
72
83
|
if (!raw) return;
|
|
73
84
|
var state;
|
|
74
|
-
try { state = JSON.parse(raw); } catch (e) { localStorage.removeItem("talkDOM:" + name); return; }
|
|
85
|
+
try { state = JSON.parse(raw); } catch (e) { void e; localStorage.removeItem("talkDOM:" + name); return; }
|
|
75
86
|
if (state.op === "outer") {
|
|
76
87
|
el.outerHTML = state.content;
|
|
77
88
|
} else {
|
|
@@ -195,9 +206,15 @@
|
|
|
195
206
|
var result;
|
|
196
207
|
els.forEach(function (el) {
|
|
197
208
|
var detail = { receiver: msg.receiver, selector: msg.selector, args: msg.args };
|
|
209
|
+
// Snapshot DOM neighbors before the method runs. If the method does an
|
|
210
|
+
// outer swap, `el` is replaced and disconnected, so we need these anchors
|
|
211
|
+
// to locate the replacement element for dispatching lifecycle events.
|
|
198
212
|
var parent = el.parentNode;
|
|
199
213
|
var next = el.nextElementSibling;
|
|
200
214
|
result = method(el, ...args);
|
|
215
|
+
// After an outer swap `el` is gone. Walk from the snapshotted sibling or
|
|
216
|
+
// parent to find the element that took its place; fall back to a fresh
|
|
217
|
+
// receiver query if the DOM was restructured.
|
|
201
218
|
function resolveTarget() {
|
|
202
219
|
if (el.isConnected) return el;
|
|
203
220
|
var candidate = next && next.isConnected ? next.previousElementSibling
|
|
@@ -224,13 +241,18 @@
|
|
|
224
241
|
// Programmatic API: parse and execute a raw message string (supports pipes and semicolons).
|
|
225
242
|
// Returns a promise that resolves when all chains complete.
|
|
226
243
|
function run(raw) {
|
|
244
|
+
// Semicolons split into independent chains that run in parallel.
|
|
227
245
|
var chains = raw.split(";").map(function (chain) {
|
|
228
246
|
var trimmed = chain.trim();
|
|
229
247
|
if (!trimmed) return Promise.resolve();
|
|
248
|
+
// Pipes split a chain into sequential steps where each step's return
|
|
249
|
+
// value is fed as the first argument to the next step.
|
|
230
250
|
var steps = trimmed.split("|").map(function (s) { return s.trim(); }).filter(Boolean);
|
|
231
251
|
if (steps.length === 1) {
|
|
232
252
|
return Promise.resolve(send(parseMessage(steps[0])));
|
|
233
253
|
}
|
|
254
|
+
// Reduce builds a promise chain: each step waits for the previous one,
|
|
255
|
+
// then passes its resolved value (piped) into send().
|
|
234
256
|
return steps.reduce(function (prev, step) {
|
|
235
257
|
var msg = parseMessage(step);
|
|
236
258
|
return Promise.resolve(prev).then(function (piped) {
|
|
@@ -238,6 +260,7 @@
|
|
|
238
260
|
});
|
|
239
261
|
}, undefined);
|
|
240
262
|
});
|
|
263
|
+
// All independent chains resolve together.
|
|
241
264
|
return Promise.all(chains);
|
|
242
265
|
}
|
|
243
266
|
|
|
@@ -262,10 +285,17 @@
|
|
|
262
285
|
|
|
263
286
|
// Set up a repeating interval for receivers with a poll: keyword.
|
|
264
287
|
// Stops automatically when the element is removed from the DOM.
|
|
288
|
+
var activePollers = 0;
|
|
289
|
+
var maxPollers = 64;
|
|
290
|
+
|
|
265
291
|
function startPolling(el) {
|
|
266
292
|
var attr = el.getAttribute("receiver");
|
|
267
293
|
var msg = parseMessage(attr);
|
|
268
294
|
if (msg.keywords[msg.keywords.length - 1] !== "poll:") return;
|
|
295
|
+
if (activePollers >= maxPollers) {
|
|
296
|
+
console.warn("talkDOM: max pollers (" + maxPollers + ") reached, ignoring " + msg.receiver);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
269
299
|
var interval = parseInterval(msg.args[msg.args.length - 1]);
|
|
270
300
|
if (!interval) {
|
|
271
301
|
console.error("poll: invalid interval for " + msg.receiver);
|
|
@@ -275,8 +305,9 @@
|
|
|
275
305
|
var args = msg.args.slice(0, -1);
|
|
276
306
|
var name = msg.receiver;
|
|
277
307
|
var cachedTargets = findReceivers(name);
|
|
308
|
+
activePollers++;
|
|
278
309
|
var id = setInterval(function () {
|
|
279
|
-
if (!el.isConnected) { clearInterval(id); return; }
|
|
310
|
+
if (!el.isConnected) { clearInterval(id); activePollers--; return; }
|
|
280
311
|
if (cachedTargets.length === 0 || !cachedTargets[0].isConnected) {
|
|
281
312
|
cachedTargets = findReceivers(name);
|
|
282
313
|
}
|
|
@@ -303,6 +334,11 @@
|
|
|
303
334
|
replayState(history.state);
|
|
304
335
|
document.querySelectorAll("[receiver]").forEach(startPolling);
|
|
305
336
|
|
|
306
|
-
window.talkDOM = {
|
|
337
|
+
window.talkDOM = {
|
|
338
|
+
methods: methods,
|
|
339
|
+
send: run,
|
|
340
|
+
get maxPollers() { return maxPollers; },
|
|
341
|
+
set maxPollers(n) { maxPollers = n; },
|
|
342
|
+
};
|
|
307
343
|
|
|
308
344
|
}());
|