@livenetworks/ashlar 1.4.1 → 1.6.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 +189 -177
- package/js/COMPONENTS.md +55 -49
- package/js/index.js +22 -5
- package/js/ln-accordion/README.md +186 -137
- package/js/ln-accordion/ln-accordion.js +1 -1
- package/js/ln-ajax/README.md +73 -4
- package/js/ln-ajax/ln-ajax.js +1 -1
- package/js/ln-ajax/src/ln-ajax.js +13 -13
- package/js/ln-api-connector/README.md +261 -150
- package/js/ln-api-connector/ln-api-connector.js +1 -1
- package/js/ln-api-connector/src/ln-api-connector.js +368 -265
- package/js/ln-api-queue/README.md +219 -0
- package/js/ln-api-queue/ln-api-queue.js +1 -0
- package/js/ln-api-queue/src/ln-api-queue.js +320 -0
- package/js/ln-api-queue/src/queue-storage.js +455 -0
- package/js/ln-autoresize/README.md +47 -0
- package/js/ln-autoresize/ln-autoresize.js +1 -1
- package/js/ln-autosave/README.md +33 -1
- package/js/ln-autosave/ln-autosave.js +1 -1
- package/js/ln-autosave/src/ln-autosave.js +4 -4
- package/js/ln-circular-progress/README.md +27 -13
- package/js/ln-circular-progress/ln-circular-progress.js +1 -1
- package/js/ln-circular-progress/src/ln-circular-progress.js +18 -5
- package/js/ln-confirm/README.md +162 -86
- package/js/ln-confirm/_ln-confirm.scss +11 -0
- package/js/ln-confirm/ln-confirm.js +1 -1
- package/js/ln-confirm/src/ln-confirm.js +77 -41
- package/js/ln-core/README.md +896 -0
- package/js/ln-core/hash.js +92 -0
- package/js/ln-core/helpers.js +337 -8
- package/js/ln-core/index.js +4 -2
- package/js/ln-core/persist.js +29 -3
- package/js/ln-core/positioning.js +1 -35
- package/js/ln-core/window-cache.js +213 -0
- package/js/ln-couchdb-connector/README.md +177 -156
- package/js/ln-couchdb-connector/ln-couchdb-connector.js +1 -1
- package/js/ln-couchdb-connector/src/ln-couchdb-connector.js +398 -348
- package/js/ln-data-coordinator/README.md +536 -165
- package/js/ln-data-coordinator/ln-data-coordinator.js +1 -1
- package/js/ln-data-coordinator/src/data-read-policy.js +18 -0
- package/js/ln-data-coordinator/src/ln-data-coordinator.js +1042 -249
- package/js/ln-data-coordinator/src/mutation-receipts.js +46 -0
- package/js/ln-data-store/README.md +241 -94
- package/js/ln-data-store/ln-data-store.js +1 -1
- package/js/ln-data-store/src/ln-data-store.js +694 -699
- package/js/ln-date/README.md +39 -1
- package/js/ln-date/ln-date-dev.scss +14 -0
- package/js/ln-date/ln-date.js +1 -1
- package/js/ln-date/src/ln-date.js +236 -178
- package/js/ln-debug/README.md +58 -0
- package/js/ln-debug/ln-debug.js +1 -0
- package/js/ln-debug/src/ln-debug.js +19 -0
- package/js/ln-dropdown/README.md +31 -8
- package/js/ln-dropdown/ln-dropdown.js +1 -1
- package/js/ln-dropdown/src/ln-dropdown.js +30 -13
- package/js/ln-editor/README.md +274 -0
- package/js/ln-editor/ln-editor.js +1 -0
- package/js/ln-editor/ln-editor.scss +43 -0
- package/js/ln-editor/src/ln-editor.js +619 -0
- package/js/ln-external-links/README.md +44 -5
- package/js/ln-external-links/ln-external-links.js +1 -1
- package/js/ln-fill/README.md +199 -0
- package/js/ln-fill/ln-fill.js +1 -0
- package/js/ln-fill/src/ln-fill.js +87 -0
- package/js/ln-filter/README.md +189 -99
- package/js/ln-filter/ln-filter-dev.scss +8 -0
- package/js/ln-filter/ln-filter.js +1 -1
- package/js/ln-filter/src/ln-filter.js +22 -7
- package/js/ln-form/README.md +212 -49
- package/js/ln-form/ln-form.js +1 -1
- package/js/ln-form/src/ln-form.js +99 -199
- package/js/ln-http/README.md +125 -89
- package/js/ln-http/ln-http.js +1 -1
- package/js/ln-http/src/ln-http.js +32 -13
- package/js/ln-icon/README.md +130 -0
- package/js/ln-icon/ln-icon.js +1 -0
- package/js/{ln-icons/src/ln-icons.js → ln-icon/src/ln-icon.js} +20 -15
- package/js/ln-link/README.md +47 -9
- package/js/ln-link/ln-link.js +1 -1
- package/js/ln-link/src/ln-link.js +22 -6
- package/js/ln-list/README.md +129 -0
- package/js/ln-list/ln-list.js +1 -0
- package/js/ln-list/ln-list.scss +28 -0
- package/js/ln-list/src/ln-list.js +1216 -0
- package/js/ln-modal/README.md +185 -154
- package/js/ln-modal/ln-modal-dev.scss +13 -0
- package/js/ln-modal/ln-modal.js +1 -1
- package/js/ln-modal/ln-modal.scss +28 -4
- package/js/ln-modal/src/ln-modal.js +137 -201
- package/js/ln-modal-coordinator/README.md +99 -0
- package/js/ln-modal-coordinator/ln-modal-coordinator-dev.scss +8 -0
- package/js/ln-modal-coordinator/ln-modal-coordinator.js +1 -0
- package/js/ln-modal-coordinator/src/ln-modal-coordinator.js +345 -0
- package/js/ln-nav/README.md +46 -3
- package/js/ln-nav/ln-nav.js +1 -1
- package/js/ln-nav/src/ln-nav.js +92 -127
- package/js/ln-number/README.md +28 -1
- package/js/ln-number/ln-number-dev.scss +13 -0
- package/js/ln-number/ln-number.js +1 -1
- package/js/ln-number/src/ln-number.js +443 -350
- package/js/ln-options/README.md +66 -0
- package/js/ln-options/ln-options.js +1 -0
- package/js/ln-options/src/ln-options.js +74 -0
- package/js/ln-popover/README.md +40 -5
- package/js/ln-popover/ln-popover.js +1 -1
- package/js/ln-popover/src/ln-popover.js +38 -11
- package/js/ln-progress/README.md +413 -442
- package/js/ln-progress/ln-progress.js +1 -1
- package/js/ln-progress/src/ln-progress.js +98 -150
- package/js/ln-router/README.md +432 -0
- package/js/ln-router/ln-router.js +1 -0
- package/js/ln-router/src/ln-router.js +539 -0
- package/js/ln-search/README.md +158 -83
- package/js/ln-search/ln-search-dev.scss +8 -0
- package/js/ln-search/ln-search.js +1 -1
- package/js/ln-search/src/ln-search.js +12 -9
- package/js/ln-slug/README.md +110 -0
- package/js/ln-slug/ln-slug.js +1 -0
- package/js/ln-slug/src/ln-slug.js +74 -0
- package/js/ln-sort/README.md +170 -0
- package/js/ln-sort/ln-sort-dev.scss +8 -0
- package/js/ln-sort/ln-sort.js +1 -0
- package/js/ln-sort/src/ln-sort.js +178 -0
- package/js/ln-sortable/README.md +133 -95
- package/js/ln-sortable/ln-sortable.js +1 -1
- package/js/ln-stat/README.md +55 -0
- package/js/ln-stat/ln-stat.js +1 -0
- package/js/ln-stat/src/ln-stat.js +55 -0
- package/js/ln-table/README.md +285 -101
- package/js/ln-table/ln-table-dev.scss +17 -0
- package/js/ln-table/ln-table.js +1 -1
- package/js/ln-table/ln-table.scss +1 -1
- package/js/ln-table/src/ln-table.js +1493 -473
- package/js/ln-table-coordinator/README.md +73 -0
- package/js/ln-table-coordinator/ln-table-coordinator.js +1 -0
- package/js/ln-table-coordinator/src/ln-table-coordinator.js +160 -0
- package/js/ln-tabs/README.md +221 -137
- package/js/ln-tabs/ln-tabs-dev.scss +18 -0
- package/js/ln-tabs/ln-tabs.js +1 -1
- package/js/ln-tabs/src/ln-tabs.js +41 -24
- package/js/ln-time/README.md +14 -0
- package/js/ln-time/ln-time.js +1 -1
- package/js/ln-time/src/ln-time.js +223 -192
- package/js/ln-toast/README.md +178 -122
- package/js/ln-toast/ln-toast.js +1 -15
- package/js/ln-toast/src/ln-toast.js +232 -210
- package/js/ln-toast/template.html +16 -12
- package/js/ln-toggle/README.md +61 -2
- package/js/ln-toggle/ln-toggle-dev.scss +13 -0
- package/js/ln-toggle/ln-toggle.js +1 -1
- package/js/ln-toggle/src/ln-toggle.js +56 -43
- package/js/ln-tooltip/README.md +95 -58
- package/js/ln-tooltip/ln-tooltip-dev.scss +9 -0
- package/js/ln-tooltip/ln-tooltip.js +1 -1
- package/js/ln-tooltip/src/ln-tooltip.js +45 -6
- package/js/ln-translations/README.md +118 -96
- package/js/ln-translations/ln-translations.js +1 -1
- package/js/ln-translations/src/ln-translations.js +10 -5
- package/js/ln-upload/README.md +85 -10
- package/js/ln-upload/ln-upload.js +1 -1
- package/js/ln-upload/src/ln-upload.js +26 -10
- package/js/ln-validate/README.md +72 -0
- package/js/ln-validate/ln-validate-dev.scss +15 -0
- package/js/ln-validate/ln-validate.js +1 -1
- package/js/ln-validate/src/ln-validate.js +64 -0
- package/package.json +61 -55
- package/scss/base/_global.scss +2 -2
- package/scss/base/_typography.scss +1 -2
- package/scss/components/_date.scss +1 -0
- package/scss/components/_dropdown.scss +0 -5
- package/scss/components/_editor.scss +5 -0
- package/scss/components/_filter.scss +26 -0
- package/scss/components/_form.scss +7 -0
- package/scss/components/_ln-table.scss +14 -5
- package/scss/components/_sort.scss +10 -0
- package/scss/components/_table.scss +68 -1
- package/scss/components/_toast.scss +68 -41
- package/scss/components/_toggle.scss +16 -0
- package/scss/config/_density.scss +205 -117
- package/scss/config/_icons.scss +10 -9
- package/scss/config/_tokens.scss +69 -47
- package/scss/config/mixins/_accordion.scss +2 -7
- package/scss/config/mixins/_alert.scss +4 -4
- package/scss/config/mixins/_breadcrumbs.scss +2 -2
- package/scss/config/mixins/_btn.scss +1 -1
- package/scss/config/mixins/_card.scss +89 -14
- package/scss/config/mixins/_collapsible.scss +3 -0
- package/scss/config/mixins/_colors.scss +1 -1
- package/scss/config/mixins/_diagnostics.scss +48 -0
- package/scss/config/mixins/_display.scss +1 -0
- package/scss/config/mixins/_dropdown.scss +3 -5
- package/scss/config/mixins/_editor.scss +124 -0
- package/scss/config/mixins/_empty-state.scss +7 -0
- package/scss/config/mixins/_filter.scss +47 -0
- package/scss/config/mixins/_footer.scss +1 -2
- package/scss/config/mixins/_form.scss +207 -21
- package/scss/config/mixins/_index.scss +4 -1
- package/scss/config/mixins/_ln-table.scss +51 -93
- package/scss/config/mixins/_modal.scss +149 -66
- package/scss/config/mixins/_nav.scss +3 -4
- package/scss/config/mixins/_popover.scss +39 -0
- package/scss/config/mixins/_sidebar.scss +1 -1
- package/scss/config/mixins/_sort.scss +42 -0
- package/scss/config/mixins/_table.scss +232 -38
- package/scss/config/mixins/_toast.scss +179 -152
- package/scss/config/mixins/_tooltip.scss +17 -7
- package/scss/config/mixins/_typography.scss +4 -2
- package/scss/ln-ashlar-dev.scss +39 -0
- package/scss/ln-ashlar.scss +8 -2
- package/js/ln-data-table/README.md +0 -110
- package/js/ln-data-table/ln-data-table.js +0 -1
- package/js/ln-data-table/ln-data-table.scss +0 -10
- package/js/ln-data-table/src/ln-data-table.js +0 -1103
- package/js/ln-icons/README.md +0 -88
- package/js/ln-icons/ln-icons.js +0 -1
- package/js/ln-table/ln-table-sort.js +0 -1
- package/js/ln-table/src/ln-table-sort.js +0 -168
- package/scss/components/_data-table.scss +0 -83
- package/scss/config/mixins/_data-table.scss +0 -346
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// ─── Windowed Sparse Cache ─────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// DOM-agnostic sliding-window data engine for server-side virtualization.
|
|
4
|
+
// Owns a sparse cache keyed by logical index, logical/grand totals, request
|
|
5
|
+
// dedup, a stale-guard via queryGen, LRU eviction to a bounded cap, and a
|
|
6
|
+
// debounced fetch trigger. Transport-agnostic: the consumer supplies
|
|
7
|
+
// requestPage() (kick a fetch — any transport) and onChange() (cache mutated,
|
|
8
|
+
// re-render). Render clients pull rows via get()/has() and read logicalTotal
|
|
9
|
+
// for spacer geometry; they hold no cache of their own. Shared by the ln-table
|
|
10
|
+
// and ln-list windowed paths; a future ln-data-store windowed mode plugs in by
|
|
11
|
+
// making requestPage resolve getAll({offset,limit}) then call ingest().
|
|
12
|
+
|
|
13
|
+
export function createWindowCache(config) {
|
|
14
|
+
config = config || {};
|
|
15
|
+
let windowSize = config.windowSize > 0 ? config.windowSize : 1000;
|
|
16
|
+
let pageSize = config.pageSize > 0 ? config.pageSize : 200;
|
|
17
|
+
let threshold = config.threshold != null ? config.threshold : 25;
|
|
18
|
+
let fetchDebounce = config.fetchDebounce != null ? config.fetchDebounce : 120;
|
|
19
|
+
const requestPage = typeof config.requestPage === 'function' ? config.requestPage : function () {};
|
|
20
|
+
const onChange = typeof config.onChange === 'function' ? config.onChange : function () {};
|
|
21
|
+
|
|
22
|
+
const map = new Map(); // logicalIndex → record
|
|
23
|
+
const touch = new Map(); // logicalIndex → seq (LRU stamp)
|
|
24
|
+
const inflight = new Set(); // offsets currently requested (dedup)
|
|
25
|
+
let logicalTotal = 0;
|
|
26
|
+
let grandTotal = 0;
|
|
27
|
+
let queryGen = 0;
|
|
28
|
+
let query = { sort: null, filters: {}, search: '' };
|
|
29
|
+
let debounceId = null;
|
|
30
|
+
let seq = 0;
|
|
31
|
+
let lastRangeStart = 0; // last ensure() visible range — revalidate() re-targets here, not page 0
|
|
32
|
+
let lastRangeEnd = 0;
|
|
33
|
+
let pendingSwap = false; // set by invalidate()/revalidate(), consumed by the new generation's first ingest()
|
|
34
|
+
|
|
35
|
+
function stamp(i) { touch.set(i, ++seq); }
|
|
36
|
+
|
|
37
|
+
function hasActiveQuery() {
|
|
38
|
+
return !!(query && (query.search || (query.filters && Object.keys(query.filters).length)));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function evict() {
|
|
42
|
+
if (map.size <= windowSize) return;
|
|
43
|
+
// LRU: drop least-recently touched/requested rows first
|
|
44
|
+
const keys = Array.from(map.keys()).sort(function (a, b) {
|
|
45
|
+
return (touch.get(a) || 0) - (touch.get(b) || 0);
|
|
46
|
+
});
|
|
47
|
+
let i = 0;
|
|
48
|
+
while (map.size > windowSize && i < keys.length) {
|
|
49
|
+
map.delete(keys[i]);
|
|
50
|
+
touch.delete(keys[i]);
|
|
51
|
+
i++;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function fire(offset, limit) {
|
|
56
|
+
inflight.add(offset);
|
|
57
|
+
requestPage(query, offset, limit);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
get: function (i) { return map.get(i); },
|
|
62
|
+
has: function (i) { return map.has(i); },
|
|
63
|
+
peek: function () { return map.size ? map.values().next().value : undefined; },
|
|
64
|
+
|
|
65
|
+
get logicalTotal() { return logicalTotal; },
|
|
66
|
+
get grandTotal() { return grandTotal; },
|
|
67
|
+
get queryGen() { return queryGen; },
|
|
68
|
+
get size() { return map.size; },
|
|
69
|
+
|
|
70
|
+
// Render client hands its visible logical range; stamps in-range resident
|
|
71
|
+
// rows as freshly used, then checks if any page in range (padded by threshold)
|
|
72
|
+
// is missing from cache and needs to be fetched (page-aligned).
|
|
73
|
+
ensure: function (startRow, endRow) {
|
|
74
|
+
clearTimeout(debounceId);
|
|
75
|
+
lastRangeStart = startRow;
|
|
76
|
+
lastRangeEnd = endRow;
|
|
77
|
+
for (let i = startRow; i < endRow; i++) {
|
|
78
|
+
if (map.has(i)) stamp(i);
|
|
79
|
+
}
|
|
80
|
+
if (logicalTotal <= 0) return;
|
|
81
|
+
|
|
82
|
+
const checkStart = Math.max(0, startRow - threshold);
|
|
83
|
+
const checkEnd = Math.min(logicalTotal, endRow + threshold);
|
|
84
|
+
|
|
85
|
+
const firstPage = Math.floor(checkStart / pageSize);
|
|
86
|
+
const lastPage = Math.floor(Math.max(0, checkEnd - 1) / pageSize);
|
|
87
|
+
|
|
88
|
+
let targetPageOffset = -1;
|
|
89
|
+
let targetPageLimit = pageSize;
|
|
90
|
+
|
|
91
|
+
for (let p = firstPage; p <= lastPage; p++) {
|
|
92
|
+
const pOffset = p * pageSize;
|
|
93
|
+
const pLimit = Math.min(pageSize, logicalTotal - pOffset);
|
|
94
|
+
|
|
95
|
+
let hasMissing = false;
|
|
96
|
+
for (let i = pOffset; i < pOffset + pLimit; i++) {
|
|
97
|
+
if (!map.has(i)) {
|
|
98
|
+
hasMissing = true;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (hasMissing && !inflight.has(pOffset)) {
|
|
104
|
+
targetPageOffset = pOffset;
|
|
105
|
+
targetPageLimit = pLimit;
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (targetPageOffset === -1) return;
|
|
111
|
+
|
|
112
|
+
debounceId = setTimeout(function () {
|
|
113
|
+
fire(targetPageOffset, targetPageLimit);
|
|
114
|
+
}, fetchDebounce);
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
// Splice a fetched page. Stale (superseded-query) responses are dropped.
|
|
118
|
+
// Out-of-order pages splice at their own offset, so order is irrelevant.
|
|
119
|
+
ingest: function (detail) {
|
|
120
|
+
detail = detail || {};
|
|
121
|
+
if (detail.queryGen != null && detail.queryGen !== queryGen) return;
|
|
122
|
+
|
|
123
|
+
// First response of a new generation: drop the stale rows now, at the
|
|
124
|
+
// swap moment, not at invalidate()/revalidate() time — stale-while-revalidate.
|
|
125
|
+
if (pendingSwap) {
|
|
126
|
+
map.clear();
|
|
127
|
+
touch.clear();
|
|
128
|
+
pendingSwap = false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
grandTotal = detail.total != null ? detail.total : grandTotal;
|
|
132
|
+
logicalTotal = detail.filtered != null
|
|
133
|
+
? detail.filtered
|
|
134
|
+
: (detail.data ? detail.data.length : logicalTotal);
|
|
135
|
+
|
|
136
|
+
const offset = detail.offset || 0;
|
|
137
|
+
const rows = detail.data || [];
|
|
138
|
+
for (let i = 0; i < rows.length; i++) {
|
|
139
|
+
map.set(offset + i, rows[i]);
|
|
140
|
+
stamp(offset + i);
|
|
141
|
+
}
|
|
142
|
+
inflight.delete(offset);
|
|
143
|
+
evict();
|
|
144
|
+
onChange();
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
// First load: fetch page 0 at the current generation (no bump).
|
|
148
|
+
requestInitial: function (q) {
|
|
149
|
+
if (q) query = q;
|
|
150
|
+
fire(0, pageSize);
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
// Query change: new generation, stale rows stay visible until the first
|
|
154
|
+
// response of the new generation lands in ingest() — no blanking, no
|
|
155
|
+
// placeholder flash (ln-table--loading is the refresh affordance).
|
|
156
|
+
invalidate: function (q) {
|
|
157
|
+
queryGen++;
|
|
158
|
+
inflight.clear();
|
|
159
|
+
clearTimeout(debounceId);
|
|
160
|
+
if (q) query = q;
|
|
161
|
+
pendingSwap = true;
|
|
162
|
+
fire(0, pageSize);
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
// Post-mutation refresh of a windowed view: same stale-while-revalidate
|
|
166
|
+
// swap as invalidate(), but re-requests the page at the CURRENT scroll
|
|
167
|
+
// position instead of jumping back to page 0.
|
|
168
|
+
revalidate: function () {
|
|
169
|
+
queryGen++;
|
|
170
|
+
inflight.clear();
|
|
171
|
+
clearTimeout(debounceId);
|
|
172
|
+
pendingSwap = true;
|
|
173
|
+
const offset = Math.max(0, Math.floor(lastRangeStart / pageSize) * pageSize);
|
|
174
|
+
const limit = logicalTotal > 0 ? Math.min(pageSize, Math.max(1, logicalTotal - offset)) : pageSize;
|
|
175
|
+
fire(offset, limit);
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
// Failed page fetch: release the offset so the next ensure() (scroll,
|
|
179
|
+
// filter, resize) can re-request it. No onChange(), no auto-retry.
|
|
180
|
+
release: function (offset) {
|
|
181
|
+
inflight.delete(offset);
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
destroy: function () {
|
|
185
|
+
clearTimeout(debounceId);
|
|
186
|
+
map.clear();
|
|
187
|
+
touch.clear();
|
|
188
|
+
inflight.clear();
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
configure: function (partial) {
|
|
192
|
+
partial = partial || {};
|
|
193
|
+
let geometryChanged = false;
|
|
194
|
+
if (partial.windowSize != null && partial.windowSize > 0 && partial.windowSize !== windowSize) {
|
|
195
|
+
const shrank = partial.windowSize < windowSize;
|
|
196
|
+
windowSize = partial.windowSize;
|
|
197
|
+
if (shrank) evict();
|
|
198
|
+
geometryChanged = true;
|
|
199
|
+
}
|
|
200
|
+
if (partial.pageSize != null && partial.pageSize > 0) pageSize = partial.pageSize;
|
|
201
|
+
if (partial.threshold != null && partial.threshold >= 0) threshold = partial.threshold;
|
|
202
|
+
if (partial.fetchDebounce != null && partial.fetchDebounce >= 0) fetchDebounce = partial.fetchDebounce;
|
|
203
|
+
if (geometryChanged) onChange();
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
setGrandTotal: function (n) {
|
|
207
|
+
if (n == null || isNaN(n) || n < 0) return;
|
|
208
|
+
grandTotal = n;
|
|
209
|
+
if (!hasActiveQuery()) logicalTotal = n;
|
|
210
|
+
onChange();
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
@@ -1,156 +1,177 @@
|
|
|
1
|
-
# `data-ln-couchdb-connector`
|
|
2
|
-
|
|
3
|
-
A zero-dependency, Local-First sync transport component that implements the Transport Gateway pattern of `ln-ashlar` for CouchDB and Sync Gateway instances.
|
|
4
|
-
|
|
5
|
-
This component encapsulates all connection parameters (CouchDB base URL, database name, auth credentials, headers) and provides a declarative, event-driven, or programmatic way to talk to any CouchDB-compatible backend using standard Changes Feed (`_changes`) protocols. It isolates networking concerns completely, making your cache store (`data-ln-data-store`) and visual presentation layers fully network-agnostic.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Declarative DOM Setup
|
|
10
|
-
|
|
11
|
-
Place the connector inside your parent coordinator element alongside your store:
|
|
12
|
-
|
|
13
|
-
```html
|
|
14
|
-
<div data-ln-data-coordinator="tasks">
|
|
15
|
-
<!-- Storage Layer Cache (Blind to networking) -->
|
|
16
|
-
<div data-ln-data-store
|
|
17
|
-
data-ln-store-indexes="due_date,priority">
|
|
18
|
-
</div>
|
|
19
|
-
|
|
20
|
-
<!-- Transport Gateway (CouchDB Connector) -->
|
|
21
|
-
<div data-ln-couchdb-connector
|
|
22
|
-
data-ln-couchdb-url="https://couch.livenetworks.com"
|
|
23
|
-
data-ln-couchdb-db="tasks"
|
|
24
|
-
data-ln-couchdb-auth="Basic dXNlcjpwYXNz">
|
|
25
|
-
</div>
|
|
26
|
-
</div>
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## Attributes
|
|
32
|
-
|
|
33
|
-
All attributes are dynamically observed. Any runtime changes to these attributes instantly update the connector's internal configurations.
|
|
34
|
-
|
|
35
|
-
| Attribute | Category | Description |
|
|
36
|
-
|-----------|----------|-------------|
|
|
37
|
-
| `data-ln-couchdb-connector` | Selector | Creates the component instance. |
|
|
38
|
-
| `data-ln-couchdb-url` | Connection | The base URL of the CouchDB Server or Sync Gateway (e.g. `https://couch.example.com`). Fallbacks: `data-ln-couchdb-connector-url`, `data-ln-api-base-url`, `data-ln-rest-base-url`. |
|
|
39
|
-
| `data-ln-couchdb-db` | Connection | Database name endpoint (e.g. `tasks`). Fallbacks: `data-ln-couchdb-connector-db`, `data-ln-api-path`, `data-ln-rest-path`. |
|
|
40
|
-
| `data-ln-couchdb-auth` | Credentials | Authorization header value (e.g. `Basic dXNlcjpwYXNz` or standard token). |
|
|
41
|
-
| `data-ln-couchdb-headers` | Credentials | JSON-formatted string of custom request headers. |
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
## JavaScript API Methods
|
|
46
|
-
|
|
47
|
-
You can access the instance methods directly on the element via the `.lnCouchDbConnector` or `.lnConnector` properties:
|
|
48
|
-
|
|
49
|
-
```javascript
|
|
50
|
-
const connectorEl = document.querySelector('[data-ln-couchdb-connector]');
|
|
51
|
-
const connector = connectorEl.lnCouchDbConnector; // or connectorEl.lnConnector
|
|
52
|
-
|
|
53
|
-
// 1. Fetch changed records since a sequence ID (CouchDB delta protocol)
|
|
54
|
-
connector.fetchDelta("1-g1AAA...")
|
|
55
|
-
.then(payload => {
|
|
56
|
-
console.log('Upserted docs:', payload.data);
|
|
57
|
-
console.log('Deleted IDs:', payload.deleted);
|
|
58
|
-
console.log('New sync seq:', payload.synced_at);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
// 2. Create a new document (maps payload to CouchDB _id if id is provided)
|
|
62
|
-
connector.create({ title: 'New task', priority: 'High' })
|
|
63
|
-
.then(doc => console.log('Created CouchDB document:', doc));
|
|
64
|
-
|
|
65
|
-
// 3. Update an existing document (handles _rev fetching automatically if omitted)
|
|
66
|
-
connector.update("doc_123", { title: 'Updated Task Title' })
|
|
67
|
-
.then(doc => console.log('Updated CouchDB document:', doc));
|
|
68
|
-
|
|
69
|
-
// 4. Delete a document (handles _rev fetching automatically if omitted)
|
|
70
|
-
connector.delete("doc_123")
|
|
71
|
-
.then(res => console.log('Deleted successfully:', res));
|
|
72
|
-
|
|
73
|
-
// 5. Bulk Delete records (queries all revisions first, then performs _bulk_docs deletion)
|
|
74
|
-
connector.bulkDelete(["doc_17", "doc_23"])
|
|
75
|
-
.then(res => console.log('Bulk deleted result:', res));
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
## DOM Events
|
|
81
|
-
|
|
82
|
-
### Commands (Dispatched TO the connector)
|
|
83
|
-
|
|
84
|
-
You can trigger mutations and fetches asynchronously by dispatching standard events directly on the connector DOM element. All events are supported in `ln-couchdb-connector`, `ln-api-connector`, and `ln-rest-connector` namespaces for 100% backward compatibility.
|
|
85
|
-
|
|
86
|
-
| Event | `detail` Payload | Description |
|
|
87
|
-
|-------|------------------|-------------|
|
|
88
|
-
| `ln-couchdb-connector:request-sync` | `{ since }` | Triggers a Changes Feed fetch request. |
|
|
89
|
-
| `ln-couchdb-connector:request-create` | `{ data, tempId }` | Triggers a creation request. |
|
|
90
|
-
| `ln-couchdb-connector:request-update` | `{ id, data, expected_version }` | Triggers a PUT update (expected_version maps to CouchDB's revision). |
|
|
91
|
-
| `ln-couchdb-connector:request-delete` | `{ id, rev }` | Triggers a deletion request. |
|
|
92
|
-
| `ln-couchdb-connector:request-bulk-delete` | `{ ids }` | Triggers a bulk-deletion request. |
|
|
93
|
-
|
|
94
|
-
### Notifications (Emitted BY the connector)
|
|
95
|
-
|
|
96
|
-
The connector dispatches bubbles-enabled events to notify parent coordinators of response states.
|
|
97
|
-
|
|
98
|
-
| Event | `detail` Payload | Description |
|
|
99
|
-
|-------|------------------|-------------|
|
|
100
|
-
| `ln-couchdb-connector:fetched` | `{ data, since }` | Dispatched upon successful fetch completion. |
|
|
101
|
-
| `ln-couchdb-connector:created` | `{ record, tempId }` | Dispatched upon successful server record confirmation. |
|
|
102
|
-
| `ln-couchdb-connector:updated` | `{ record, id }` | Dispatched upon successful server update confirmation. |
|
|
103
|
-
| `ln-couchdb-connector:deleted` | `{ response, id }` | Dispatched upon successful server deletion. |
|
|
104
|
-
| `ln-couchdb-connector:bulk-deleted`| `{ response, ids }` | Dispatched upon successful server bulk deletion. |
|
|
105
|
-
| `ln-couchdb-connector:error` | `{ action, error, status, ... }` | Dispatched on failures.
|
|
106
|
-
| `ln-couchdb-connector:config-changed` | `{ url, db, auth, headers }` | Dispatched when configuration attributes are mutated. |
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
##
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
1
|
+
# `data-ln-couchdb-connector`
|
|
2
|
+
|
|
3
|
+
A zero-dependency, Local-First sync transport component that implements the Transport Gateway pattern of `ln-ashlar` for CouchDB and Sync Gateway instances.
|
|
4
|
+
|
|
5
|
+
This component encapsulates all connection parameters (CouchDB base URL, database name, auth credentials, headers) and provides a declarative, event-driven, or programmatic way to talk to any CouchDB-compatible backend using standard Changes Feed (`_changes`) protocols. It isolates networking concerns completely, making your cache store (`data-ln-data-store`) and visual presentation layers fully network-agnostic.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Declarative DOM Setup
|
|
10
|
+
|
|
11
|
+
Place the connector inside your parent coordinator element alongside your store:
|
|
12
|
+
|
|
13
|
+
```html
|
|
14
|
+
<div data-ln-data-coordinator="tasks">
|
|
15
|
+
<!-- Storage Layer Cache (Blind to networking) -->
|
|
16
|
+
<div data-ln-data-store
|
|
17
|
+
data-ln-data-store-indexes="due_date,priority">
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<!-- Transport Gateway (CouchDB Connector) -->
|
|
21
|
+
<div data-ln-couchdb-connector
|
|
22
|
+
data-ln-couchdb-url="https://couch.livenetworks.com"
|
|
23
|
+
data-ln-couchdb-db="tasks"
|
|
24
|
+
data-ln-couchdb-auth="Basic dXNlcjpwYXNz">
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Attributes
|
|
32
|
+
|
|
33
|
+
All attributes are dynamically observed. Any runtime changes to these attributes instantly update the connector's internal configurations.
|
|
34
|
+
|
|
35
|
+
| Attribute | Category | Description |
|
|
36
|
+
|-----------|----------|-------------|
|
|
37
|
+
| `data-ln-couchdb-connector` | Selector | Creates the component instance. |
|
|
38
|
+
| `data-ln-couchdb-url` | Connection | The base URL of the CouchDB Server or Sync Gateway (e.g. `https://couch.example.com`). Fallbacks: `data-ln-couchdb-connector-url`, `data-ln-api-base-url`, `data-ln-rest-base-url`. |
|
|
39
|
+
| `data-ln-couchdb-db` | Connection | Database name endpoint (e.g. `tasks`). Fallbacks: `data-ln-couchdb-connector-db`, `data-ln-api-path`, `data-ln-rest-path`. |
|
|
40
|
+
| `data-ln-couchdb-auth` | Credentials | Authorization header value (e.g. `Basic dXNlcjpwYXNz` or standard token). |
|
|
41
|
+
| `data-ln-couchdb-headers` | Credentials | JSON-formatted string of custom request headers. |
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## JavaScript API Methods
|
|
46
|
+
|
|
47
|
+
You can access the instance methods directly on the element via the `.lnCouchDbConnector` or `.lnConnector` properties:
|
|
48
|
+
|
|
49
|
+
```javascript
|
|
50
|
+
const connectorEl = document.querySelector('[data-ln-couchdb-connector]');
|
|
51
|
+
const connector = connectorEl.lnCouchDbConnector; // or connectorEl.lnConnector
|
|
52
|
+
|
|
53
|
+
// 1. Fetch changed records since a sequence ID (CouchDB delta protocol)
|
|
54
|
+
connector.fetchDelta("1-g1AAA...")
|
|
55
|
+
.then(payload => {
|
|
56
|
+
console.log('Upserted docs:', payload.data);
|
|
57
|
+
console.log('Deleted IDs:', payload.deleted);
|
|
58
|
+
console.log('New sync seq:', payload.synced_at);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// 2. Create a new document (maps payload to CouchDB _id if id is provided)
|
|
62
|
+
connector.create({ title: 'New task', priority: 'High' })
|
|
63
|
+
.then(doc => console.log('Created CouchDB document:', doc));
|
|
64
|
+
|
|
65
|
+
// 3. Update an existing document (handles _rev fetching automatically if omitted)
|
|
66
|
+
connector.update("doc_123", { title: 'Updated Task Title' })
|
|
67
|
+
.then(doc => console.log('Updated CouchDB document:', doc));
|
|
68
|
+
|
|
69
|
+
// 4. Delete a document (handles _rev fetching automatically if omitted)
|
|
70
|
+
connector.delete("doc_123")
|
|
71
|
+
.then(res => console.log('Deleted successfully:', res));
|
|
72
|
+
|
|
73
|
+
// 5. Bulk Delete records (queries all revisions first, then performs _bulk_docs deletion)
|
|
74
|
+
connector.bulkDelete(["doc_17", "doc_23"])
|
|
75
|
+
.then(res => console.log('Bulk deleted result:', res));
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## DOM Events
|
|
81
|
+
|
|
82
|
+
### Commands (Dispatched TO the connector)
|
|
83
|
+
|
|
84
|
+
You can trigger mutations and fetches asynchronously by dispatching standard events directly on the connector DOM element. All events are supported in `ln-couchdb-connector`, `ln-api-connector`, and `ln-rest-connector` namespaces for 100% backward compatibility.
|
|
85
|
+
|
|
86
|
+
| Event | `detail` Payload | Description |
|
|
87
|
+
|-------|------------------|-------------|
|
|
88
|
+
| `ln-couchdb-connector:request-sync` | `{ since }` | Triggers a Changes Feed fetch request. |
|
|
89
|
+
| `ln-couchdb-connector:request-create` | `{ data, tempId, idempotencyKey? }` | Triggers a creation request. |
|
|
90
|
+
| `ln-couchdb-connector:request-update` | `{ id, data, expected_version, idempotencyKey? }` | Triggers a PUT update (expected_version maps to CouchDB's revision). |
|
|
91
|
+
| `ln-couchdb-connector:request-delete` | `{ id, rev, idempotencyKey? }` | Triggers a deletion request. |
|
|
92
|
+
| `ln-couchdb-connector:request-bulk-delete` | `{ ids, idempotencyKey? }` | Triggers a bulk-deletion request. |
|
|
93
|
+
|
|
94
|
+
### Notifications (Emitted BY the connector)
|
|
95
|
+
|
|
96
|
+
The connector dispatches bubbles-enabled events to notify parent coordinators of response states.
|
|
97
|
+
|
|
98
|
+
| Event | `detail` Payload | Description |
|
|
99
|
+
|-------|------------------|-------------|
|
|
100
|
+
| `ln-couchdb-connector:fetched` | `{ data, since }` | Dispatched upon successful fetch completion. |
|
|
101
|
+
| `ln-couchdb-connector:created` | `{ record, tempId, message, meta }` | Dispatched upon successful server record confirmation. `message` opaque from the response envelope (null if absent). `meta` echoes the request's `meta` unchanged. |
|
|
102
|
+
| `ln-couchdb-connector:updated` | `{ record, id, message, meta }` | Dispatched upon successful server update confirmation. |
|
|
103
|
+
| `ln-couchdb-connector:deleted` | `{ response, id, message, meta }` | Dispatched upon successful server deletion. |
|
|
104
|
+
| `ln-couchdb-connector:bulk-deleted`| `{ response, ids, message, meta }` | Dispatched upon successful server bulk deletion. |
|
|
105
|
+
| `ln-couchdb-connector:error` | `{ action, error, status, meta, ... }` | Dispatched on failures. Update errors additionally include `data`/`conflictData` (same value) if status is 409. `meta` echoes the request's `meta` unchanged — required for coordinator-side queue ack/nack and id-swap reconciliation. |
|
|
106
|
+
| `ln-couchdb-connector:config-changed` | `{ url, db, auth, headers }` | Dispatched when configuration attributes are mutated. |
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Mutation Response Envelope
|
|
111
|
+
|
|
112
|
+
`create`/`update`/`delete`/`bulkDelete` presence-check the parsed JSON body for an optional `{ message, content }` envelope, mirroring `ln-api-connector`'s contract:
|
|
113
|
+
|
|
114
|
+
- `content` present → used as the CouchDB response body (`{id, rev}` etc.) in place of the bare body.
|
|
115
|
+
- `message` present → passed opaquely on the `:created`/`:updated`/`:deleted`/`:bulk-deleted` event detail; a coordinator surfaces it as a success toast. Absent → `message: null`, no toast.
|
|
116
|
+
- Raw CouchDB never sends this envelope — `content` falls back to the bare body, `message` is always `null`, so a direct CouchDB backend never produces a toast. A proxy/gateway placed in front of CouchDB (e.g. to add cross-cutting audit messages) can opt in by wrapping its response in `{ message, content }`.
|
|
117
|
+
- `fetchDelta` (sync) is NOT part of this envelope — sync keeps its own `{ data, deleted, synced_at }` collection-delta shape.
|
|
118
|
+
- The JS API methods (`create`, `update`, `delete`, `bulkDelete`) resolve to the SAME shape as before this change (record / record / response / response) — the envelope unwrap is internal; direct JS callers are unaffected. `message` is only surfaced via the DOM-event path (`:created`/`:updated`/`:deleted`/`:bulk-deleted` `detail.message`).
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## 3-Tier Integration Architecture (Coordinator Example)
|
|
123
|
+
|
|
124
|
+
Here is how a parent Coordinator links a Cache Store and the CouchDB Connector using standard DOM events:
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
(function () {
|
|
128
|
+
const parent = document.querySelector('[data-ln-data-coordinator="tasks"]');
|
|
129
|
+
// ln-data-coordinator wires store <-> connector automatically once both
|
|
130
|
+
// are present in its subtree — no hand-written glue is needed for the
|
|
131
|
+
// write pipeline. This snippet only illustrates what the coordinator
|
|
132
|
+
// does internally; in a real page, just declare the markup (see
|
|
133
|
+
// "Declarative DOM Setup" above) and let ln-data-coordinator do the rest.
|
|
134
|
+
|
|
135
|
+
// Server confirmation reconciles via an ORDINARY store update (id-swap),
|
|
136
|
+
// not a special confirm/revert method:
|
|
137
|
+
// connectorEl.addEventListener('ln-couchdb-connector:created', function (e) {
|
|
138
|
+
// const meta = e.detail.meta || {};
|
|
139
|
+
// storeEl.dispatchEvent(new CustomEvent('ln-data-store:request-update', {
|
|
140
|
+
// detail: { id: meta.tempId, data: e.detail.record }
|
|
141
|
+
// }));
|
|
142
|
+
// });
|
|
143
|
+
//
|
|
144
|
+
// See js/ln-data-coordinator/README.md for the full fan-out + error
|
|
145
|
+
// reconciliation policy (auth/transient/deterministic) that ships with
|
|
146
|
+
// the coordinator — this connector never needs to be wired by hand.
|
|
147
|
+
})();
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 🔧 Internals
|
|
153
|
+
|
|
154
|
+
Source: `js/ln-couchdb-connector/*`. Bridges CouchDB's revision-based (`_rev`) document protocol and `_changes`/`_bulk_docs` endpoints to the same coordinator-facing event contract as `ln-api-connector`.
|
|
155
|
+
|
|
156
|
+
### Sync flow
|
|
157
|
+
|
|
158
|
+
`request-sync {since}` → `GET /{db}/_changes?include_docs=true&since=<since>` → maps `rows.doc` to `{..., id: _id}` and `rows.deleted` to `deletedIds` → `:fetched { data, deleted, synced_at }`. The coordinator hands this straight to `store.applySync`.
|
|
159
|
+
|
|
160
|
+
### Revision resolution (zero-friction `_rev`)
|
|
161
|
+
|
|
162
|
+
Every update/delete payload is inspected for `_rev`/`rev`. If present, `PUT`/`DELETE` fires immediately with `If-Match`. If absent, the connector first `GET`s the current document to read `_rev`, then issues the mutation — callers never need to track revisions themselves. A `409 Conflict` fires `:error` with `status: 409` and `data`/`conflictData` set to CouchDB's raw `{error, reason}` body. Unlike a REST backend, CouchDB's 409 carries no document snapshot, so the coordinator's server-wins reconciliation is a no-op on this path — only the error toast fires; the optimistic local write stays in place until the next sync reconciles it.
|
|
163
|
+
|
|
164
|
+
### ID translation
|
|
165
|
+
|
|
166
|
+
Ingress: every fetched/created/updated document gets `id = _id` before reaching the coordinator. Egress: `create`/`update` map `id` back to `_id` before the request body is sent. The mapping is entirely internal — JS API callers and DOM-event consumers never see `_id`.
|
|
167
|
+
|
|
168
|
+
### Bulk delete
|
|
169
|
+
|
|
170
|
+
1. `POST /{db}/_all_docs` with `{"keys": [...]}` to resolve current `_rev` per id.
|
|
171
|
+
2. Filter out any ids that errored or are missing.
|
|
172
|
+
3. Build `{_id, _rev, _deleted: true}` per surviving doc.
|
|
173
|
+
4. `POST /{db}/_bulk_docs` with the array.
|
|
174
|
+
|
|
175
|
+
### Envelope unwrap
|
|
176
|
+
|
|
177
|
+
Same `{message, content}` presence-check as `ln-api-connector` (see its README) — `content` replaces the bare CouchDB body when present, `message` rides opaquely on the `:created`/`:updated`/`:deleted`/`:bulk-deleted` event detail. Raw CouchDB never sends this envelope, so a direct CouchDB backend never toasts; only a proxy/gateway that wraps responses can opt in.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(){"use strict";function h(c,d,l){c.dispatchEvent(new CustomEvent(d,{bubbles:!0,detail:l||{}}))}function C(c,d){if(!document.body){document.addEventListener("DOMContentLoaded",function(){C(c,d)}),console.warn("["+d+'] Script loaded before <body> — add "defer" to your <script> tag');return}c()}function _(c,d,l,f){if(c.nodeType!==1)return;const v=d.indexOf("[")!==-1||d.indexOf(".")!==-1||d.indexOf("#")!==-1?d:"["+d+"]",e=Array.from(c.querySelectorAll(v));c.matches&&c.matches(v)&&e.push(c);for(const o of e)o[l]||(o[l]=new f(o))}function x(c,d,l,f,g={}){const v=g.extraAttributes||[],e=g.onAttributeChange||null,o=g.onInit||null;function t(r){const n=r||document.body;_(n,c,d,l),o&&o(n)}return C(function(){const r=new MutationObserver(function(i){for(let s=0;s<i.length;s++){const a=i[s];if(a.type==="childList"){for(let m=0;m<a.addedNodes.length;m++){const u=a.addedNodes[m];u.nodeType===1&&(_(u,c,d,l),o&&o(u))}for(let m=0;m<a.removedNodes.length;m++){const u=a.removedNodes[m];if(u.nodeType===1){const w=c.indexOf("[")!==-1||c.indexOf(".")!==-1||c.indexOf("#")!==-1?c:"["+c+"]",E=Array.from(u.querySelectorAll(w));u.matches&&u.matches(w)&&E.push(u);for(let T=0;T<E.length;T++){const j=E[T];if(!document.contains(j)){const O=j[d];O&&typeof O.destroy=="function"&&O.destroy()}}}}}else a.type==="attributes"&&(e&&a.target[d]?e(a.target,a.attributeName):(_(a.target,c,d,l),o&&o(a.target)))}});let n=[];if(c.indexOf("[")!==-1){const i=/\[([\w-]+)/g;let s;for(;(s=i.exec(c))!==null;)n.push(s[1])}else n.push(c);r.observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:n.concat(v)})},f),window[d]=t,document.readyState==="loading"?document.addEventListener("DOMContentLoaded",function(){t(document.body)}):t(document.body),t}function b(...c){return c.filter(d=>d!=null&&d!=="").map((d,l)=>l===0?d.replace(/\/+$/,""):d.replace(/^\/+/,"").replace(/\/+$/,"")).filter(Boolean).join("/")}function p(c,d){return Object.assign({"Content-Type":"application/json",Accept:"application/json"},c,d?{Authorization:d}:null)}function S(c,d="ln-core"){try{return c?JSON.parse(c):{}}catch(l){return console.error(`[${d}] Invalid headers JSON:`,l),{}}}const k={};function L(c,d){k[c]=d}function A(c){return k[c]||{ingress:d=>d,egress:d=>d}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=L,window.lnCore.getDataMapper=A),(function(){const c="data-ln-couchdb-connector",d="lnCouchDbConnector",l="lnConnector";if(window[d]!==void 0)return;function f(e){return this.dom=e,e[d]=this,e[l]=this,this.refreshConfig(),this._handlers=null,g(this),this}f.prototype.refreshConfig=function(){const e=this.dom;this.url=e.getAttribute("data-ln-couchdb-url")||"",this.db=e.getAttribute("data-ln-couchdb-db")||"",this.auth=e.getAttribute("data-ln-couchdb-auth")||"",this.credentials="same-origin";const o=e.getAttribute("data-ln-couchdb-headers")||"";this.headers=S(o,"ln-couchdb-connector"),this.auth&&console.warn("[ln-couchdb-connector] Security Warning: Sensitive authorization credentials detected in data-ln-couchdb-auth attribute. Storing basic authentication credentials in HTML DOM attributes is highly discouraged and vulnerable to XSS credential extraction. Please use HttpOnly session cookies or a Backend Proxy Gateway instead."),o.toLowerCase().includes("authorization")&&console.warn("[ln-couchdb-connector] Security Warning: Sensitive authorization credentials detected in data-ln-couchdb-headers attribute. Please use HttpOnly session cookies or a Backend Proxy Gateway instead."),h(e,"ln-couchdb-connector:config-changed",{url:this.url,db:this.db,auth:this.auth?"[REDACTED]":"",headers:this.headers})},f.prototype.fetchDelta=function(e){const o=this,t=["include_docs=true","feed=normal"];e&&t.push("since="+encodeURIComponent(e));const r=b(o.url,o.db,"_changes")+"?"+t.join("&");return window.fetch(r,{method:"GET",headers:p(o.headers,o.auth),credentials:o.credentials}).then(n=>{if(!n.ok)throw new Error("HTTP "+n.status+": "+n.statusText);return n.json()}).then(n=>{const i=n.results||[];return{data:i.filter(s=>!s.deleted&&s.doc).map(s=>Object.assign({},s.doc,{id:s.doc._id})),deleted:i.filter(s=>s.deleted).map(s=>s.id),synced_at:n.last_seq||e||""}})},f.prototype.create=function(e){const o=this,t=Object.assign({_id:e.id},e);return t._id||delete t._id,window.fetch(b(o.url,o.db),{method:"POST",headers:p(o.headers,o.auth),credentials:o.credentials,body:JSON.stringify(t)}).then(r=>{if(!r.ok)throw new Error("HTTP "+r.status+": "+r.statusText);return r.json()}).then(r=>Object.assign({},t,{id:r.id,_id:r.id,_rev:r.rev}))},f.prototype.update=function(e,o){const t=this,r=Object.assign({id:String(e),_id:String(e)},o),n=r._rev||r.rev;return(n?Promise.resolve(n):window.fetch(b(t.url,t.db,null,e),{method:"GET",headers:p(t.headers,t.auth),credentials:t.credentials}).then(s=>{if(!s.ok)throw new Error("Could not retrieve document for revision mapping");return s.json().then(a=>a._rev)})).then(s=>{const a=Object.assign({},r,{_rev:s});delete a.rev;const m=Object.assign(p(t.headers,t.auth),{"If-Match":s});return window.fetch(b(t.url,t.db,null,e),{method:"PUT",headers:m,credentials:t.credentials,body:JSON.stringify(a)}).then(u=>{if(u.ok)return u.json().then(y=>Object.assign({},a,{_rev:y.rev}));if(u.status===409)return u.json().then(y=>{const w=new Error("Conflict");throw w.status=409,w.data=y,w});throw new Error("HTTP "+u.status+": "+u.statusText)})})},f.prototype.delete=function(e,o){const t=this;return(o?Promise.resolve(o):window.fetch(b(t.url,t.db,null,e),{method:"GET",headers:p(t.headers,t.auth),credentials:t.credentials}).then(n=>{if(!n.ok)throw new Error("Could not retrieve document for revision delete");return n.json().then(i=>i._rev)})).then(n=>{const i=b(t.url,t.db,null,e)+"?rev="+encodeURIComponent(n);return window.fetch(i,{method:"DELETE",headers:p(t.headers,t.auth),credentials:t.credentials}).then(s=>{if(!s.ok)throw new Error("HTTP "+s.status+": "+s.statusText);return s.json()})})},f.prototype.bulkDelete=function(e){const o=this;return!e||e.length===0?Promise.resolve({ok:!0,deletedCount:0}):window.fetch(b(o.url,o.db,"_all_docs"),{method:"POST",headers:p(o.headers,o.auth),credentials:o.credentials,body:JSON.stringify({keys:e})}).then(t=>{if(!t.ok)throw new Error("HTTP "+t.status+": "+t.statusText);return t.json()}).then(t=>{const n=(t.rows||[]).filter(i=>!i.error&&i.value&&i.value.rev).map(i=>({_id:i.id,_rev:i.value.rev,_deleted:!0}));return n.length===0?{ok:!0,deletedCount:0}:window.fetch(b(o.url,o.db,"_bulk_docs"),{method:"POST",headers:p(o.headers,o.auth),credentials:o.credentials,body:JSON.stringify({docs:n})}).then(i=>{if(!i.ok)throw new Error("HTTP "+i.status+": "+i.statusText);return i.json()}).then(i=>({ok:!0,results:i,deletedCount:n.length}))})};function g(e){e._handlers={sync:function(t){const r=t.detail||{};e.fetchDelta(r.since).then(function(n){h(e.dom,"ln-couchdb-connector:fetched",{data:n,since:r.since})}).catch(function(n){h(e.dom,"ln-couchdb-connector:error",{action:"sync",error:n.message,status:n.status||0,since:r.since})})},create:function(t){const r=t.detail||{};e.create(r.data).then(function(n){h(e.dom,"ln-couchdb-connector:created",{record:n,tempId:r.tempId})}).catch(function(n){h(e.dom,"ln-couchdb-connector:error",{action:"create",error:n.message,status:n.status||0,tempId:r.tempId})})},update:function(t){const r=t.detail||{},n=Object.assign({},r.data);r.expected_version!==void 0&&(n._rev=r.expected_version),e.update(r.id,n).then(function(i){h(e.dom,"ln-couchdb-connector:updated",{record:i,id:r.id})}).catch(function(i){h(e.dom,"ln-couchdb-connector:error",{action:"update",error:i.message,status:i.status||0,id:r.id,conflictData:i.status===409?i.data:null})})},delete:function(t){const r=t.detail||{};e.delete(r.id,r.rev).then(function(n){h(e.dom,"ln-couchdb-connector:deleted",{response:n,id:r.id})}).catch(function(n){h(e.dom,"ln-couchdb-connector:error",{action:"delete",error:n.message,status:n.status||0,id:r.id})})},bulkDelete:function(t){const r=t.detail||{};e.bulkDelete(r.ids).then(function(n){h(e.dom,"ln-couchdb-connector:bulk-deleted",{response:n,ids:r.ids})}).catch(function(n){h(e.dom,"ln-couchdb-connector:error",{action:"bulk-delete",error:n.message,status:n.status||0,ids:r.ids})})}},["ln-couchdb-connector","ln-api-connector","ln-rest-connector"].forEach(function(t){e.dom.addEventListener(t+":request-sync",e._handlers.sync),e.dom.addEventListener(t+":request-fetch",e._handlers.sync),e.dom.addEventListener(t+":request-create",e._handlers.create),e.dom.addEventListener(t+":request-update",e._handlers.update),e.dom.addEventListener(t+":request-delete",e._handlers.delete),e.dom.addEventListener(t+":request-bulk-delete",e._handlers.bulkDelete)})}f.prototype.destroy=function(){if(!this.dom[d])return;const e=this;e._handlers&&(["ln-couchdb-connector","ln-api-connector","ln-rest-connector"].forEach(function(t){e.dom.removeEventListener(t+":request-sync",e._handlers.sync),e.dom.removeEventListener(t+":request-fetch",e._handlers.sync),e.dom.removeEventListener(t+":request-create",e._handlers.create),e.dom.removeEventListener(t+":request-update",e._handlers.update),e.dom.removeEventListener(t+":request-delete",e._handlers.delete),e.dom.removeEventListener(t+":request-bulk-delete",e._handlers.bulkDelete)}),e._handlers=null),h(this.dom,"ln-couchdb-connector:destroyed",{target:this.dom}),delete this.dom[d],delete this.dom[l]};function v(e){const o=e[d];o&&o.refreshConfig()}x(c,d,f,"ln-couchdb-connector",{extraAttributes:["data-ln-couchdb-url","data-ln-couchdb-db","data-ln-couchdb-auth","data-ln-couchdb-headers"],onAttributeChange:v})})()})();
|
|
1
|
+
(function(){"use strict";if(typeof window<"u"){const t=console.warn;console.warn=function(...n){typeof n[0]=="string"&&(n[0].startsWith("[ln-")||n[0].startsWith("[lnCore"))&&!(document.documentElement.hasAttribute("data-ln-debug")||document.body&&document.body.hasAttribute("data-ln-debug"))||t.apply(console,n)}}const L={};function M(t,n){L[t]||(L[t]=document.querySelector('[data-ln-template="'+t+'"]'));const c=L[t];return c?c.content.cloneNode(!0):(console.warn("["+(n||"ln-core")+'] Template "'+t+'" not found'),null)}function v(t,n,c){t.dispatchEvent(new CustomEvent(n,{bubbles:!0,detail:c||{}}))}function j(t,n){if(!t||!n)return t;const c=t.querySelectorAll("[data-ln-field]");for(let d=0;d<c.length;d++){const a=c[d],l=a.getAttribute("data-ln-field");n[l]!=null&&(a.textContent=n[l])}const g=t.querySelectorAll("[data-ln-attr]");for(let d=0;d<g.length;d++){const a=g[d],l=a.getAttribute("data-ln-attr").split(",");for(let p=0;p<l.length;p++){const b=l[p].trim().split(":");if(b.length!==2)continue;const y=b[0].trim(),e=b[1].trim();n[e]!=null&&a.setAttribute(y,n[e])}}const m=t.querySelectorAll("[data-ln-show]");for(let d=0;d<m.length;d++){const a=m[d],l=a.getAttribute("data-ln-show");l in n&&a.classList.toggle("hidden",!n[l])}const h=t.querySelectorAll("[data-ln-class]");for(let d=0;d<h.length;d++){const a=h[d],l=a.getAttribute("data-ln-class").split(",");for(let p=0;p<l.length;p++){const b=l[p].trim().split(":");if(b.length!==2)continue;const y=b[0].trim(),e=b[1].trim();e in n&&a.classList.toggle(y,!!n[e])}}return t}function R(t,n){t.matches&&t.matches("[data-ln-form], [data-ln-fillable]")&&t.dispatchEvent(new CustomEvent("ln-fill",{detail:n??null,bubbles:!0}));const c=t.querySelectorAll("[data-ln-form], [data-ln-fillable]");for(let g=0;g<c.length;g++)c[g].dispatchEvent(new CustomEvent("ln-fill",{detail:n??null,bubbles:!0}));return t}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._fillBound||(window.lnCore._fillBound=!0,document.addEventListener("ln-fill",function(t){if(!(!t.target.matches||!t.target.matches("[data-ln-fillable]")))if(t.detail)j(t.target,t.detail);else{const n=t.target.querySelectorAll("[data-ln-field]");for(let c=0;c<n.length;c++)n[c].textContent=""}})));function D(t,n){if(!t||!n)return t;const c=document.createTreeWalker(t,NodeFilter.SHOW_TEXT);for(;c.nextNode();){const h=c.currentNode;h.textContent.indexOf("{{")!==-1&&(h.textContent=h.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(d,a){return n[a]!==void 0?n[a]:""}))}const g=function(h,d){return n[d]!==void 0?n[d]:""},m=Array.from(t.querySelectorAll("*"));t.nodeType===1&&m.push(t);for(let h=0;h<m.length;h++){const d=m[h],a=d.attributes;for(let l=0;l<a.length;l++){const p=a[l];p.value.indexOf("{{")!==-1&&d.setAttribute(p.name,p.value.replace(/\{\{\s*(\w+)\s*\}\}/g,g))}}return t}function B(t,n,c,g,m,h){const d={};for(let l=0;l<t.children.length;l++){const p=t.children[l],b=p.getAttribute("data-ln-key");b&&(d[b]=p)}const a=document.createDocumentFragment();for(let l=0;l<n.length;l++){const p=n[l],b=String(g(p));let y=d[b];if(y)m(y,p,l);else{const e=M(c,h);if(!e||(D(e,p),y=e.firstElementChild,!y))continue;y.setAttribute("data-ln-key",b),m(y,p,l)}a.appendChild(y)}t.textContent="",t.appendChild(a)}function q(t,n){if(!document.body){document.addEventListener("DOMContentLoaded",function(){q(t,n)}),console.warn("["+n+'] Script loaded before <body> — add "defer" to your <script> tag');return}t()}function O(t,n,c,g){if(t.nodeType!==1)return;const h=n.indexOf("[")!==-1||n.indexOf(".")!==-1||n.indexOf("#")!==-1?n:"["+n+"]",d=Array.from(t.querySelectorAll(h));t.matches&&t.matches(h)&&d.push(t);for(const a of d)a[c]||(a[c]=new g(a))}function U(t,n,c,g,m={}){const h=m.extraAttributes||[],d=m.onAttributeChange||null,a=m.onInit||null;function l(p){const b=p||document.body;O(b,t,n,c),a&&a(b)}return q(function(){const p=new MutationObserver(function(y){for(let e=0;e<y.length;e++){const i=y[e];if(i.type==="childList"){for(let r=0;r<i.addedNodes.length;r++){const o=i.addedNodes[r];o.nodeType===1&&(O(o,t,n,c),a&&a(o))}for(let r=0;r<i.removedNodes.length;r++){const o=i.removedNodes[r];if(o.nodeType===1){const f=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",u=Array.from(o.querySelectorAll(f));o.matches&&o.matches(f)&&u.push(o);for(let w=0;w<u.length;w++){const C=u[w];if(!document.contains(C)){const A=C[n];A&&typeof A.destroy=="function"&&A.destroy()}}}}}else i.type==="attributes"&&(d&&i.target[n]?d(i.target,i.attributeName):(O(i.target,t,n,c),a&&a(i.target)))}});let b=[];if(t.indexOf("[")!==-1){const y=/\[([\w-]+)/g;let e;for(;(e=y.exec(t))!==null;)b.push(e[1])}else b.push(t);p.observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:b.concat(h)})},g),window[n]=l,document.readyState==="loading"?document.addEventListener("DOMContentLoaded",function(){l(document.body)}):l(document.body),l}function _(...t){return t.filter(n=>n!=null&&n!=="").map((n,c)=>c===0?n.replace(/\/+$/,""):n.replace(/^\/+/,"").replace(/\/+$/,"")).filter(Boolean).join("/")}function k(t,n){return Object.assign({"Content-Type":"application/json",Accept:"application/json"},t,n?{Authorization:n}:null)}function W(t,n="ln-core"){try{return t?JSON.parse(t):{}}catch(c){return console.error(`[${n}] Invalid headers JSON:`,c),{}}}const P={};function K(t,n){P[t]=n}function G(t){return P[t]||{ingress:n=>n,egress:n=>n}}const I={};function N(t,n){if(!t||typeof n!="object")return;const c=t.toLowerCase().split("-")[0];I[c]=n}function F(t){if(!t)return null;const n=t.toLowerCase().split("-")[0];return I[n]||null}N("mk",{monthsLong:["јануари","февруари","март","април","мај","јуни","јули","август","септември","октомври","ноември","декември"],monthsShort:["јан","фев","мар","апр","мај","јун","јул","авг","септ","окт","ноем","дек"],daysLong:["недела","понеделник","вторник","среда","четврток","петок","сабота"],daysShort:["нед","пон","вт","ср","чет","пет","саб"]}),typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=K,window.lnCore.getDataMapper=G,window.lnCore.registerLocaleFallback=N,window.lnCore.getLocaleFallback=F,window.lnCore.fillTemplate=D,window.lnCore.fill=j,window.lnCore.lnFill=R,window.lnCore.renderList=B);function H(t){return(t||"").replace(/^#/,"")}function S(t){const n=t===void 0?location.hash:t,c={},g=H(n);if(!g)return c;const m=g.split("&");for(let h=0;h<m.length;h++){const d=m[h];if(!d)continue;const a=d.indexOf(":"),l=a>-1?d.slice(0,a):d,p=a>-1?d.slice(a+1):"";if(l)try{c[l]=decodeURIComponent(p)}catch{c[l]=p}}return c}function J(t){if(!t)return null;const n=S();return t in n?n[t]:null}function z(t,n){if(!t)return;const c=S();n==null?delete c[t]:c[t]=String(n);const m=Object.keys(c).map(function(h){const d=c[h];return d===""?h:h+":"+encodeURIComponent(d)}).join("&");H(location.hash)!==m&&(location.hash=m)}function $(t){return t.button===1||t.ctrlKey||t.metaKey||t.shiftKey?!1:(t.preventDefault(),!0)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=S,window.lnCore.hashGet=J,window.lnCore.hashSet=z,window.lnCore.hashLinkClick=$),(function(){const t="data-ln-couchdb-connector",n="lnCouchDbConnector",c="lnConnector";if(window[n]!==void 0)return;function g(e){const i=e&&e.content!==void 0?e.content:e,r=e&&e.message?e.message:null;return{content:i,message:r}}function m(e){return this.dom=e,e[n]=this,e[c]=this,this.refreshConfig(),this._handlers=null,b(this),this}m.prototype.refreshConfig=function(){const e=this.dom;this.url=e.getAttribute("data-ln-couchdb-url")||"",this.db=e.getAttribute("data-ln-couchdb-db")||"",this.auth=e.getAttribute("data-ln-couchdb-auth")||"",this.credentials="same-origin";const i=e.getAttribute("data-ln-couchdb-headers")||"";this.headers=W(i,"ln-couchdb-connector"),this.auth&&console.warn("[ln-couchdb-connector] Security Warning: Sensitive authorization credentials detected in data-ln-couchdb-auth attribute. Storing basic authentication credentials in HTML DOM attributes is highly discouraged and vulnerable to XSS credential extraction. Please use HttpOnly session cookies or a Backend Proxy Gateway instead."),i.toLowerCase().includes("authorization")&&console.warn("[ln-couchdb-connector] Security Warning: Sensitive authorization credentials detected in data-ln-couchdb-headers attribute. Please use HttpOnly session cookies or a Backend Proxy Gateway instead."),v(e,"ln-couchdb-connector:config-changed",{url:this.url,db:this.db,auth:this.auth?"[REDACTED]":"",headers:this.headers})};function h(e,i,r){const o=Object.assign({},k(e.headers,e.auth),r||{});return i&&(o["Idempotency-Key"]=i),o}m.prototype.fetchDelta=function(e){const i=this,r=["include_docs=true","feed=normal"];e&&r.push("since="+encodeURIComponent(e));const o=_(i.url,i.db,"_changes")+"?"+r.join("&");return window.fetch(o,{method:"GET",headers:k(i.headers,i.auth),credentials:i.credentials}).then(s=>{if(!s.ok)throw new Error("HTTP "+s.status+": "+s.statusText);return s.json()}).then(s=>{const f=s.results||[];return{data:f.filter(u=>!u.deleted&&u.doc).map(u=>Object.assign({},u.doc,{id:u.doc._id})),deleted:f.filter(u=>u.deleted).map(u=>u.id),synced_at:s.last_seq||e||""}})};function d(e,i,r){const o=Object.assign({_id:i.id},i);return o._id||delete o._id,window.fetch(_(e.url,e.db),{method:"POST",headers:h(e,r),credentials:e.credentials,body:JSON.stringify(o)}).then(s=>{if(!s.ok)throw new Error("HTTP "+s.status+": "+s.statusText);return s.json()}).then(s=>{const f=g(s),u=f.content;return{record:Object.assign({},o,{id:u.id,_id:u.id,_rev:u.rev}),message:f.message}})}m.prototype.create=function(e,i){return d(this,e,i).then(r=>r.record)};function a(e,i,r,o){const s=Object.assign({id:String(i),_id:String(i)},r),f=s._rev||s.rev;return(f?Promise.resolve(f):window.fetch(_(e.url,e.db,null,i),{method:"GET",headers:k(e.headers,e.auth),credentials:e.credentials}).then(w=>{if(!w.ok)throw new Error("Could not retrieve document for revision mapping");return w.json().then(C=>C._rev)})).then(w=>{const C=Object.assign({},s,{_rev:w});delete C.rev;const A=h(e,o,{"If-Match":w});return window.fetch(_(e.url,e.db,null,i),{method:"PUT",headers:A,credentials:e.credentials,body:JSON.stringify(C)}).then(E=>{if(E.ok)return E.json().then(x=>{const T=g(x);return{record:Object.assign({},C,{_rev:T.content.rev}),message:T.message}});if(E.status===409)return E.json().then(x=>{const T=new Error("Conflict");throw T.status=409,T.data=x,T});throw new Error("HTTP "+E.status+": "+E.statusText)})})}m.prototype.update=function(e,i,r){return a(this,e,i,r).then(o=>o.record)};function l(e,i,r,o){return(r?Promise.resolve(r):window.fetch(_(e.url,e.db,null,i),{method:"GET",headers:k(e.headers,e.auth),credentials:e.credentials}).then(f=>{if(!f.ok)throw new Error("Could not retrieve document for revision delete");return f.json().then(u=>u._rev)})).then(f=>{const u=_(e.url,e.db,null,i)+"?rev="+encodeURIComponent(f);return window.fetch(u,{method:"DELETE",headers:h(e,o),credentials:e.credentials}).then(w=>{if(!w.ok)throw new Error("HTTP "+w.status+": "+w.statusText);return w.json()}).then(w=>{const C=g(w);return{response:C.content,message:C.message}})})}m.prototype.delete=function(e,i,r){return l(this,e,i,r).then(o=>o.response)};function p(e,i,r){return!i||i.length===0?Promise.resolve({response:{ok:!0,deletedCount:0},message:null}):window.fetch(_(e.url,e.db,"_all_docs"),{method:"POST",headers:k(e.headers,e.auth),credentials:e.credentials,body:JSON.stringify({keys:i})}).then(o=>{if(!o.ok)throw new Error("HTTP "+o.status+": "+o.statusText);return o.json()}).then(o=>{const f=(o.rows||[]).filter(u=>!u.error&&u.value&&u.value.rev).map(u=>({_id:u.id,_rev:u.value.rev,_deleted:!0}));return f.length===0?{response:{ok:!0,deletedCount:0},message:null}:window.fetch(_(e.url,e.db,"_bulk_docs"),{method:"POST",headers:h(e,r),credentials:e.credentials,body:JSON.stringify({docs:f})}).then(u=>{if(!u.ok)throw new Error("HTTP "+u.status+": "+u.statusText);return u.json()}).then(u=>{const w=g(u);return{response:{ok:!0,results:w.content,deletedCount:f.length},message:w.message}})})}m.prototype.bulkDelete=function(e,i){return p(this,e,i).then(r=>r.response)};function b(e){e._handlers={sync:function(r){const o=r.detail||{};e.fetchDelta(o.since).then(function(s){v(e.dom,"ln-couchdb-connector:fetched",{data:s,since:o.since,meta:o.meta||null})}).catch(function(s){v(e.dom,"ln-couchdb-connector:error",{action:"sync",error:s.message,status:s.status||0,since:o.since,meta:o.meta||null})})},create:function(r){const o=r.detail||{};d(e,o.data,o.idempotencyKey).then(function(s){v(e.dom,"ln-couchdb-connector:created",{record:s.record,tempId:o.tempId,message:s.message,meta:o.meta||null})}).catch(function(s){v(e.dom,"ln-couchdb-connector:error",{action:"create",error:s.message,status:s.status||0,tempId:o.tempId,meta:o.meta||null})})},update:function(r){const o=r.detail||{},s=Object.assign({},o.data);o.expected_version!==void 0&&(s._rev=o.expected_version),a(e,o.id,s,o.idempotencyKey).then(function(f){v(e.dom,"ln-couchdb-connector:updated",{record:f.record,id:o.id,message:f.message,meta:o.meta||null})}).catch(function(f){v(e.dom,"ln-couchdb-connector:error",{action:"update",error:f.message,status:f.status||0,id:o.id,data:f.status===409?f.data:null,conflictData:f.status===409?f.data:null,meta:o.meta||null})})},delete:function(r){const o=r.detail||{};l(e,o.id,o.rev,o.idempotencyKey).then(function(s){v(e.dom,"ln-couchdb-connector:deleted",{response:s.response,id:o.id,message:s.message,meta:o.meta||null})}).catch(function(s){v(e.dom,"ln-couchdb-connector:error",{action:"delete",error:s.message,status:s.status||0,id:o.id,meta:o.meta||null})})},bulkDelete:function(r){const o=r.detail||{};p(e,o.ids,o.idempotencyKey).then(function(s){v(e.dom,"ln-couchdb-connector:bulk-deleted",{response:s.response,ids:o.ids,message:s.message,meta:o.meta||null})}).catch(function(s){v(e.dom,"ln-couchdb-connector:error",{action:"bulk-delete",error:s.message,status:s.status||0,ids:o.ids,meta:o.meta||null})})}},["ln-couchdb-connector","ln-api-connector","ln-rest-connector"].forEach(function(r){e.dom.addEventListener(r+":request-sync",e._handlers.sync),e.dom.addEventListener(r+":request-fetch",e._handlers.sync),e.dom.addEventListener(r+":request-create",e._handlers.create),e.dom.addEventListener(r+":request-update",e._handlers.update),e.dom.addEventListener(r+":request-delete",e._handlers.delete),e.dom.addEventListener(r+":request-bulk-delete",e._handlers.bulkDelete)})}m.prototype.destroy=function(){if(!this.dom[n])return;const e=this;e._handlers&&(["ln-couchdb-connector","ln-api-connector","ln-rest-connector"].forEach(function(r){e.dom.removeEventListener(r+":request-sync",e._handlers.sync),e.dom.removeEventListener(r+":request-fetch",e._handlers.sync),e.dom.removeEventListener(r+":request-create",e._handlers.create),e.dom.removeEventListener(r+":request-update",e._handlers.update),e.dom.removeEventListener(r+":request-delete",e._handlers.delete),e.dom.removeEventListener(r+":request-bulk-delete",e._handlers.bulkDelete)}),e._handlers=null),v(this.dom,"ln-couchdb-connector:destroyed",{target:this.dom}),delete this.dom[n],delete this.dom[c]};function y(e){const i=e[n];i&&i.refreshConfig()}U(t,n,m,"ln-couchdb-connector",{extraAttributes:["data-ln-couchdb-url","data-ln-couchdb-db","data-ln-couchdb-auth","data-ln-couchdb-headers"],onAttributeChange:y})})()})();
|