@doyosi/laraisy 1.0.2 → 1.0.3

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.
Files changed (51) hide show
  1. package/LICENSE +1 -1
  2. package/package.json +1 -1
  3. package/src/CodeInput.js +48 -48
  4. package/src/DSAlert.js +352 -352
  5. package/src/DSAvatar.js +207 -207
  6. package/src/DSDelete.js +274 -274
  7. package/src/DSForm.js +568 -568
  8. package/src/DSGridOrTable.js +453 -453
  9. package/src/DSLocaleSwitcher.js +239 -239
  10. package/src/DSLogout.js +293 -293
  11. package/src/DSNotifications.js +365 -365
  12. package/src/DSRestore.js +181 -181
  13. package/src/DSSelect.js +1071 -1071
  14. package/src/DSSelectBox.js +563 -563
  15. package/src/DSSimpleSlider.js +517 -517
  16. package/src/DSSvgFetch.js +69 -69
  17. package/src/DSTable/DSTableExport.js +68 -68
  18. package/src/DSTable/DSTableFilter.js +224 -224
  19. package/src/DSTable/DSTablePagination.js +136 -136
  20. package/src/DSTable/DSTableSearch.js +40 -40
  21. package/src/DSTable/DSTableSelection.js +192 -192
  22. package/src/DSTable/DSTableSort.js +58 -58
  23. package/src/DSTable.js +353 -353
  24. package/src/DSTabs.js +488 -488
  25. package/src/DSUpload.js +887 -887
  26. package/dist/CodeInput.d.ts +0 -10
  27. package/dist/DSAlert.d.ts +0 -112
  28. package/dist/DSAvatar.d.ts +0 -45
  29. package/dist/DSDelete.d.ts +0 -61
  30. package/dist/DSForm.d.ts +0 -151
  31. package/dist/DSGridOrTable/DSGOTRenderer.d.ts +0 -60
  32. package/dist/DSGridOrTable/DSGOTViewToggle.d.ts +0 -26
  33. package/dist/DSGridOrTable.d.ts +0 -296
  34. package/dist/DSLocaleSwitcher.d.ts +0 -71
  35. package/dist/DSLogout.d.ts +0 -76
  36. package/dist/DSNotifications.d.ts +0 -54
  37. package/dist/DSRestore.d.ts +0 -56
  38. package/dist/DSSelect.d.ts +0 -221
  39. package/dist/DSSelectBox.d.ts +0 -123
  40. package/dist/DSSimpleSlider.d.ts +0 -136
  41. package/dist/DSSvgFetch.d.ts +0 -17
  42. package/dist/DSTable/DSTableExport.d.ts +0 -11
  43. package/dist/DSTable/DSTableFilter.d.ts +0 -40
  44. package/dist/DSTable/DSTablePagination.d.ts +0 -12
  45. package/dist/DSTable/DSTableSearch.d.ts +0 -8
  46. package/dist/DSTable/DSTableSelection.d.ts +0 -46
  47. package/dist/DSTable/DSTableSort.d.ts +0 -8
  48. package/dist/DSTable.d.ts +0 -116
  49. package/dist/DSTabs.d.ts +0 -156
  50. package/dist/DSUpload.d.ts +0 -220
  51. package/dist/index.d.ts +0 -17
package/src/DSLogout.js CHANGED
@@ -1,293 +1,293 @@
1
- /**
2
- * DSLogout
3
- *
4
- * JS-only, multi-element Laravel logout helper.
5
- *
6
- * Features:
7
- * - Bind to one or many elements (selector/NodeList/Array/Element)
8
- * - Optional event delegation: { root, match }
9
- * - Prevent multi-click per element with logical + visual disable
10
- * - Axios → fetch → XHR fallback; JSON redirect support
11
- * - CSRF auto-resolution from <meta>, data attribute, window, cookie
12
- * - i18n messages via `translations`
13
- *
14
- * @example
15
- * // Blade <head>:
16
- * // <meta name="csrf-token" content="{{ csrf_token() }}">
17
- *
18
- * // HTML:
19
- * // <a class="js-logout" href="#">Logout</a>
20
- * // <button class="js-logout">Logout 2</button>
21
- *
22
- * // JS:
23
- * import { DSLogout } from '/js/modules/DSLogout.js';
24
- * new DSLogout({
25
- * elements: '.js-logout', // one or many elements
26
- * // or: element: '#header-logout'
27
- * // or delegation:
28
- * // delegate: { root: document, match: '.js-logout' },
29
- * url: '/logout', // or "{{ route('logout') }}"
30
- * requestLibrary: 'axios', // 'auto'|'axios'|'fetch'|'xhr'
31
- * translations: { loading: 'Çıkış yapılıyor...', error: 'Çıkış yapılamadı' },
32
- * disabledClasses: ['pointer-events-none','opacity-60','cursor-not-allowed']
33
- * });
34
- */
35
- export class DSLogout {
36
- constructor({
37
- element = null, // Element | string
38
- elements = null, // Element[] | NodeList | string
39
- delegate = null, // { root: Element|string, match: string }
40
- url = '/logout',
41
- redirect = null,
42
- requestLibrary = 'auto',
43
- csrfToken = null,
44
- translations = {},
45
- disabledClasses = ['pointer-events-none','opacity-60','cursor-not-allowed'],
46
- eventType = 'click'
47
- } = {}) {
48
- this.url = url;
49
- this.redirect = redirect;
50
- this.requestLibrary = requestLibrary;
51
- this.translations = {
52
- loading: 'Logging out...',
53
- error: 'Error logging out',
54
- ...translations
55
- };
56
- this.disabledClasses = disabledClasses;
57
- this.eventType = eventType;
58
-
59
- // Resolve CSRF once (can be overridden)
60
- this.csrf = csrfToken || this._resolveCsrfToken();
61
-
62
- // Per-element state & unbind storage
63
- this._bound = new WeakMap(); // el -> handler
64
- this._isDelegated = !!delegate;
65
- this._delegated = null;
66
-
67
- // Bind either direct elements or delegation
68
- if (this._isDelegated) {
69
- const root = typeof delegate.root === 'string'
70
- ? document.querySelector(delegate.root)
71
- : (delegate.root || document);
72
- if (!root) throw new Error('DSLogout: delegate.root not found');
73
- if (!delegate.match) throw new Error('DSLogout: delegate.match selector required');
74
- this._delegated = { root, match: delegate.match };
75
- this._bindDelegated();
76
- } else {
77
- const list = this._normalizeElements(element, elements);
78
- if (!list.length) throw new Error('DSLogout: no elements to bind');
79
- list.forEach((el) => this._bindElement(el));
80
- }
81
- }
82
-
83
- // Public: cleanup
84
- destroy() {
85
- if (this._isDelegated && this._delegated) {
86
- this._delegated.root.removeEventListener(this.eventType, this._delegated._handler);
87
- this._delegated = null;
88
- return;
89
- }
90
- // Unbind direct handlers
91
- for (const [el, handler] of this._entries(this._bound)) {
92
- el.removeEventListener(this.eventType, handler);
93
- }
94
- this._bound = new WeakMap();
95
- }
96
-
97
- // -------------------------------------------------------------------------
98
- // Internal binding helpers
99
- _normalizeElements(element, elements) {
100
- const out = [];
101
- const pushEl = (el) => { if (el && el.nodeType === 1) out.push(el); };
102
-
103
- if (element) {
104
- if (typeof element === 'string') {
105
- document.querySelectorAll(element).forEach(pushEl);
106
- } else {
107
- pushEl(element);
108
- }
109
- }
110
-
111
- if (elements) {
112
- if (typeof elements === 'string') {
113
- document.querySelectorAll(elements).forEach(pushEl);
114
- } else if (elements instanceof NodeList || Array.isArray(elements)) {
115
- elements.forEach(pushEl);
116
- } else {
117
- pushEl(elements);
118
- }
119
- }
120
- return out;
121
- }
122
-
123
- _bindElement(el) {
124
- if (!el) return;
125
- const handler = (e) => {
126
- e.preventDefault();
127
- this._handleClick(el);
128
- };
129
- el.addEventListener(this.eventType, handler);
130
- this._bound.set(el, handler);
131
- }
132
-
133
- _bindDelegated() {
134
- const { root, match } = this._delegated;
135
- const handler = (e) => {
136
- const el = e.target.closest(match);
137
- if (!el || !root.contains(el)) return;
138
- e.preventDefault();
139
- this._handleClick(el);
140
- };
141
- root.addEventListener(this.eventType, handler);
142
- this._delegated._handler = handler;
143
- }
144
-
145
- // -------------------------------------------------------------------------
146
- // Click + request pipeline
147
- async _handleClick(el) {
148
- if (this._isDisabled(el)) return;
149
-
150
- const restore = this._disableWithLoading(el);
151
-
152
- try {
153
- const data = await this._post(); // axios/fetch/xhr
154
- const json = data?.data || data; // axios vs fetch/xhr
155
- const target = this.redirect || json?.redirect || '/';
156
- window.location.assign(target);
157
- } catch (err) {
158
- alert(this.translations.error);
159
- // eslint-disable-next-line no-console
160
- console.error('DSLogout error:', err);
161
- restore();
162
- }
163
- }
164
-
165
- _isDisabled(el) {
166
- if (el instanceof HTMLButtonElement) return el.disabled;
167
- return el.getAttribute('aria-disabled') === 'true';
168
- }
169
-
170
- _disableWithLoading(el) {
171
- const isButton = el instanceof HTMLButtonElement;
172
- const prevHTML = el.innerHTML;
173
- const prevTitle = el.getAttribute('title');
174
-
175
- if (isButton) {
176
- el.disabled = true;
177
- } else {
178
- el.setAttribute('aria-disabled', 'true');
179
- this.disabledClasses.forEach(c => el.classList.add(c));
180
- }
181
-
182
- el.setAttribute('title', this.translations.loading);
183
- el.innerHTML = this.translations.loading;
184
-
185
- return () => {
186
- if (isButton) {
187
- el.disabled = false;
188
- } else {
189
- el.setAttribute('aria-disabled', 'false');
190
- this.disabledClasses.forEach(c => el.classList.remove(c));
191
- }
192
- if (prevTitle == null) el.removeAttribute('title');
193
- else el.setAttribute('title', prevTitle);
194
- el.innerHTML = prevHTML;
195
- };
196
- }
197
-
198
- _chooseLib() {
199
- const want = this.requestLibrary?.toLowerCase?.() || 'auto';
200
- if (want === 'axios') return (window.axios ? 'axios' : (window.fetch ? 'fetch' : 'xhr'));
201
- if (want === 'fetch') return (window.fetch ? 'fetch' : (window.axios ? 'axios' : 'xhr'));
202
- if (want === 'xhr') return 'xhr';
203
- if (window.axios) return 'axios';
204
- if (window.fetch) return 'fetch';
205
- return 'xhr';
206
- }
207
-
208
- async _post() {
209
- const lib = this._chooseLib();
210
- const headers = {
211
- 'Accept': 'application/json',
212
- 'X-Requested-With': 'XMLHttpRequest',
213
- 'X-CSRF-TOKEN': this.csrf
214
- };
215
-
216
- if (lib === 'axios') {
217
- return window.axios.post(this.url, {}, { headers });
218
- }
219
-
220
- if (lib === 'fetch') {
221
- const res = await fetch(this.url, {
222
- method: 'POST',
223
- headers: { ...headers, 'Content-Type': 'application/json' },
224
- credentials: 'same-origin',
225
- body: '{}'
226
- });
227
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
228
- return res.json();
229
- }
230
-
231
- // xhr fallback
232
- return new Promise((resolve, reject) => {
233
- const xhr = new XMLHttpRequest();
234
- xhr.open('POST', this.url, true);
235
- xhr.withCredentials = true;
236
- xhr.setRequestHeader('Accept', 'application/json');
237
- xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
238
- xhr.setRequestHeader('X-CSRF-TOKEN', this.csrf);
239
- xhr.setRequestHeader('Content-Type', 'application/json');
240
- xhr.onload = () => {
241
- try {
242
- if (xhr.status >= 200 && xhr.status < 300) {
243
- resolve(JSON.parse(xhr.responseText || '{}'));
244
- } else {
245
- reject(new Error(`HTTP ${xhr.status}`));
246
- }
247
- } catch (e) { reject(e); }
248
- };
249
- xhr.onerror = () => reject(new Error('Network error'));
250
- xhr.send('{}');
251
- });
252
- }
253
-
254
- // -------------------------------------------------------------------------
255
- // CSRF utilities
256
- _resolveCsrfToken() {
257
- // meta
258
- const meta = document.querySelector('meta[name="csrf-token"]');
259
- if (meta?.content) return meta.content;
260
-
261
- // from any bound element (data-csrf)
262
- for (const [el] of this._entries(this._bound)) {
263
- const t = el?.dataset?.csrf;
264
- if (t) return t;
265
- }
266
-
267
- // window exposed
268
- if (window.Laravel?.csrfToken) return window.Laravel.csrfToken;
269
-
270
- // cookie (Sanctum style)
271
- const xsrf = this._readCookie('XSRF-TOKEN');
272
- if (xsrf) return decodeURIComponent(xsrf);
273
-
274
- throw new Error(
275
- 'DSLogout: CSRF token not found. Add <meta name="csrf-token" content="{{ csrf_token() }}"> ' +
276
- 'or pass { csrfToken } when constructing DSLogout.'
277
- );
278
- }
279
-
280
- _readCookie(name) {
281
- const m = document.cookie.match(new RegExp('(?:^|; )' + name.replace(/([$?*|{}\]\\^])/g, '\\$1') + '=([^;]*)'));
282
- return m ? m[1] : null;
283
- }
284
-
285
- // helper to iterate WeakMap safely
286
- *_entries(weakMap) {
287
- // NOTE: WeakMap isn’t iterable in general; we only use this in places
288
- // where we’ve just added entries and still hold the references via the DOM.
289
- // As a safe no-op, return empty iterator if not supported.
290
- // (We avoid relying on this for core logic.)
291
- return;
292
- }
293
- }
1
+ /**
2
+ * DSLogout
3
+ *
4
+ * JS-only, multi-element Laravel logout helper.
5
+ *
6
+ * Features:
7
+ * - Bind to one or many elements (selector/NodeList/Array/Element)
8
+ * - Optional event delegation: { root, match }
9
+ * - Prevent multi-click per element with logical + visual disable
10
+ * - Axios → fetch → XHR fallback; JSON redirect support
11
+ * - CSRF auto-resolution from <meta>, data attribute, window, cookie
12
+ * - i18n messages via `translations`
13
+ *
14
+ * @example
15
+ * // Blade <head>:
16
+ * // <meta name="csrf-token" content="{{ csrf_token() }}">
17
+ *
18
+ * // HTML:
19
+ * // <a class="js-logout" href="#">Logout</a>
20
+ * // <button class="js-logout">Logout 2</button>
21
+ *
22
+ * // JS:
23
+ * import { DSLogout } from '/js/modules/DSLogout.js';
24
+ * new DSLogout({
25
+ * elements: '.js-logout', // one or many elements
26
+ * // or: element: '#header-logout'
27
+ * // or delegation:
28
+ * // delegate: { root: document, match: '.js-logout' },
29
+ * url: '/logout', // or "{{ route('logout') }}"
30
+ * requestLibrary: 'axios', // 'auto'|'axios'|'fetch'|'xhr'
31
+ * translations: { loading: 'Çıkış yapılıyor...', error: 'Çıkış yapılamadı' },
32
+ * disabledClasses: ['pointer-events-none','opacity-60','cursor-not-allowed']
33
+ * });
34
+ */
35
+ export class DSLogout {
36
+ constructor({
37
+ element = null, // Element | string
38
+ elements = null, // Element[] | NodeList | string
39
+ delegate = null, // { root: Element|string, match: string }
40
+ url = '/logout',
41
+ redirect = null,
42
+ requestLibrary = 'auto',
43
+ csrfToken = null,
44
+ translations = {},
45
+ disabledClasses = ['pointer-events-none','opacity-60','cursor-not-allowed'],
46
+ eventType = 'click'
47
+ } = {}) {
48
+ this.url = url;
49
+ this.redirect = redirect;
50
+ this.requestLibrary = requestLibrary;
51
+ this.translations = {
52
+ loading: 'Logging out...',
53
+ error: 'Error logging out',
54
+ ...translations
55
+ };
56
+ this.disabledClasses = disabledClasses;
57
+ this.eventType = eventType;
58
+
59
+ // Resolve CSRF once (can be overridden)
60
+ this.csrf = csrfToken || this._resolveCsrfToken();
61
+
62
+ // Per-element state & unbind storage
63
+ this._bound = new WeakMap(); // el -> handler
64
+ this._isDelegated = !!delegate;
65
+ this._delegated = null;
66
+
67
+ // Bind either direct elements or delegation
68
+ if (this._isDelegated) {
69
+ const root = typeof delegate.root === 'string'
70
+ ? document.querySelector(delegate.root)
71
+ : (delegate.root || document);
72
+ if (!root) throw new Error('DSLogout: delegate.root not found');
73
+ if (!delegate.match) throw new Error('DSLogout: delegate.match selector required');
74
+ this._delegated = { root, match: delegate.match };
75
+ this._bindDelegated();
76
+ } else {
77
+ const list = this._normalizeElements(element, elements);
78
+ if (!list.length) throw new Error('DSLogout: no elements to bind');
79
+ list.forEach((el) => this._bindElement(el));
80
+ }
81
+ }
82
+
83
+ // Public: cleanup
84
+ destroy() {
85
+ if (this._isDelegated && this._delegated) {
86
+ this._delegated.root.removeEventListener(this.eventType, this._delegated._handler);
87
+ this._delegated = null;
88
+ return;
89
+ }
90
+ // Unbind direct handlers
91
+ for (const [el, handler] of this._entries(this._bound)) {
92
+ el.removeEventListener(this.eventType, handler);
93
+ }
94
+ this._bound = new WeakMap();
95
+ }
96
+
97
+ // -------------------------------------------------------------------------
98
+ // Internal binding helpers
99
+ _normalizeElements(element, elements) {
100
+ const out = [];
101
+ const pushEl = (el) => { if (el && el.nodeType === 1) out.push(el); };
102
+
103
+ if (element) {
104
+ if (typeof element === 'string') {
105
+ document.querySelectorAll(element).forEach(pushEl);
106
+ } else {
107
+ pushEl(element);
108
+ }
109
+ }
110
+
111
+ if (elements) {
112
+ if (typeof elements === 'string') {
113
+ document.querySelectorAll(elements).forEach(pushEl);
114
+ } else if (elements instanceof NodeList || Array.isArray(elements)) {
115
+ elements.forEach(pushEl);
116
+ } else {
117
+ pushEl(elements);
118
+ }
119
+ }
120
+ return out;
121
+ }
122
+
123
+ _bindElement(el) {
124
+ if (!el) return;
125
+ const handler = (e) => {
126
+ e.preventDefault();
127
+ this._handleClick(el);
128
+ };
129
+ el.addEventListener(this.eventType, handler);
130
+ this._bound.set(el, handler);
131
+ }
132
+
133
+ _bindDelegated() {
134
+ const { root, match } = this._delegated;
135
+ const handler = (e) => {
136
+ const el = e.target.closest(match);
137
+ if (!el || !root.contains(el)) return;
138
+ e.preventDefault();
139
+ this._handleClick(el);
140
+ };
141
+ root.addEventListener(this.eventType, handler);
142
+ this._delegated._handler = handler;
143
+ }
144
+
145
+ // -------------------------------------------------------------------------
146
+ // Click + request pipeline
147
+ async _handleClick(el) {
148
+ if (this._isDisabled(el)) return;
149
+
150
+ const restore = this._disableWithLoading(el);
151
+
152
+ try {
153
+ const data = await this._post(); // axios/fetch/xhr
154
+ const json = data?.data || data; // axios vs fetch/xhr
155
+ const target = this.redirect || json?.redirect || '/';
156
+ window.location.assign(target);
157
+ } catch (err) {
158
+ alert(this.translations.error);
159
+ // eslint-disable-next-line no-console
160
+ console.error('DSLogout error:', err);
161
+ restore();
162
+ }
163
+ }
164
+
165
+ _isDisabled(el) {
166
+ if (el instanceof HTMLButtonElement) return el.disabled;
167
+ return el.getAttribute('aria-disabled') === 'true';
168
+ }
169
+
170
+ _disableWithLoading(el) {
171
+ const isButton = el instanceof HTMLButtonElement;
172
+ const prevHTML = el.innerHTML;
173
+ const prevTitle = el.getAttribute('title');
174
+
175
+ if (isButton) {
176
+ el.disabled = true;
177
+ } else {
178
+ el.setAttribute('aria-disabled', 'true');
179
+ this.disabledClasses.forEach(c => el.classList.add(c));
180
+ }
181
+
182
+ el.setAttribute('title', this.translations.loading);
183
+ el.innerHTML = this.translations.loading;
184
+
185
+ return () => {
186
+ if (isButton) {
187
+ el.disabled = false;
188
+ } else {
189
+ el.setAttribute('aria-disabled', 'false');
190
+ this.disabledClasses.forEach(c => el.classList.remove(c));
191
+ }
192
+ if (prevTitle == null) el.removeAttribute('title');
193
+ else el.setAttribute('title', prevTitle);
194
+ el.innerHTML = prevHTML;
195
+ };
196
+ }
197
+
198
+ _chooseLib() {
199
+ const want = this.requestLibrary?.toLowerCase?.() || 'auto';
200
+ if (want === 'axios') return (window.axios ? 'axios' : (window.fetch ? 'fetch' : 'xhr'));
201
+ if (want === 'fetch') return (window.fetch ? 'fetch' : (window.axios ? 'axios' : 'xhr'));
202
+ if (want === 'xhr') return 'xhr';
203
+ if (window.axios) return 'axios';
204
+ if (window.fetch) return 'fetch';
205
+ return 'xhr';
206
+ }
207
+
208
+ async _post() {
209
+ const lib = this._chooseLib();
210
+ const headers = {
211
+ 'Accept': 'application/json',
212
+ 'X-Requested-With': 'XMLHttpRequest',
213
+ 'X-CSRF-TOKEN': this.csrf
214
+ };
215
+
216
+ if (lib === 'axios') {
217
+ return window.axios.post(this.url, {}, { headers });
218
+ }
219
+
220
+ if (lib === 'fetch') {
221
+ const res = await fetch(this.url, {
222
+ method: 'POST',
223
+ headers: { ...headers, 'Content-Type': 'application/json' },
224
+ credentials: 'same-origin',
225
+ body: '{}'
226
+ });
227
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
228
+ return res.json();
229
+ }
230
+
231
+ // xhr fallback
232
+ return new Promise((resolve, reject) => {
233
+ const xhr = new XMLHttpRequest();
234
+ xhr.open('POST', this.url, true);
235
+ xhr.withCredentials = true;
236
+ xhr.setRequestHeader('Accept', 'application/json');
237
+ xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
238
+ xhr.setRequestHeader('X-CSRF-TOKEN', this.csrf);
239
+ xhr.setRequestHeader('Content-Type', 'application/json');
240
+ xhr.onload = () => {
241
+ try {
242
+ if (xhr.status >= 200 && xhr.status < 300) {
243
+ resolve(JSON.parse(xhr.responseText || '{}'));
244
+ } else {
245
+ reject(new Error(`HTTP ${xhr.status}`));
246
+ }
247
+ } catch (e) { reject(e); }
248
+ };
249
+ xhr.onerror = () => reject(new Error('Network error'));
250
+ xhr.send('{}');
251
+ });
252
+ }
253
+
254
+ // -------------------------------------------------------------------------
255
+ // CSRF utilities
256
+ _resolveCsrfToken() {
257
+ // meta
258
+ const meta = document.querySelector('meta[name="csrf-token"]');
259
+ if (meta?.content) return meta.content;
260
+
261
+ // from any bound element (data-csrf)
262
+ for (const [el] of this._entries(this._bound)) {
263
+ const t = el?.dataset?.csrf;
264
+ if (t) return t;
265
+ }
266
+
267
+ // window exposed
268
+ if (window.Laravel?.csrfToken) return window.Laravel.csrfToken;
269
+
270
+ // cookie (Sanctum style)
271
+ const xsrf = this._readCookie('XSRF-TOKEN');
272
+ if (xsrf) return decodeURIComponent(xsrf);
273
+
274
+ throw new Error(
275
+ 'DSLogout: CSRF token not found. Add <meta name="csrf-token" content="{{ csrf_token() }}"> ' +
276
+ 'or pass { csrfToken } when constructing DSLogout.'
277
+ );
278
+ }
279
+
280
+ _readCookie(name) {
281
+ const m = document.cookie.match(new RegExp('(?:^|; )' + name.replace(/([$?*|{}\]\\^])/g, '\\$1') + '=([^;]*)'));
282
+ return m ? m[1] : null;
283
+ }
284
+
285
+ // helper to iterate WeakMap safely
286
+ *_entries(weakMap) {
287
+ // NOTE: WeakMap isn’t iterable in general; we only use this in places
288
+ // where we’ve just added entries and still hold the references via the DOM.
289
+ // As a safe no-op, return empty iterator if not supported.
290
+ // (We avoid relying on this for core logic.)
291
+ return;
292
+ }
293
+ }