tina4ruby 3.11.35 → 3.12.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.
@@ -0,0 +1,600 @@
1
+ var _frondModule = (() => {
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/js/frond.ts
21
+ var frond_exports = {};
22
+ __export(frond_exports, {
23
+ frond: () => frond
24
+ });
25
+ var _token = null;
26
+ function request(url, options) {
27
+ let opts;
28
+ if (typeof options === "function") {
29
+ opts = { onSuccess: options };
30
+ } else {
31
+ opts = options || {};
32
+ }
33
+ const method = (opts.method || "GET").toUpperCase();
34
+ const xhr = new XMLHttpRequest();
35
+ xhr.open(method, url, true);
36
+ if (_token !== null) {
37
+ xhr.setRequestHeader("Authorization", "Bearer " + _token);
38
+ }
39
+ if (opts.headers) {
40
+ for (const key in opts.headers) {
41
+ if (Object.prototype.hasOwnProperty.call(opts.headers, key)) {
42
+ xhr.setRequestHeader(key, opts.headers[key]);
43
+ }
44
+ }
45
+ }
46
+ let body = null;
47
+ if (opts.body !== void 0 && opts.body !== null) {
48
+ if (opts.body instanceof FormData) {
49
+ body = opts.body;
50
+ } else if (typeof opts.body === "object") {
51
+ body = JSON.stringify(opts.body);
52
+ xhr.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
53
+ } else if (typeof opts.body === "string") {
54
+ body = opts.body;
55
+ xhr.setRequestHeader("Content-Type", "text/plain; charset=UTF-8");
56
+ }
57
+ }
58
+ xhr.onload = function() {
59
+ const freshToken = xhr.getResponseHeader("FreshToken");
60
+ if (freshToken && freshToken !== "") {
61
+ _token = freshToken;
62
+ }
63
+ let content = xhr.response;
64
+ try {
65
+ content = JSON.parse(content);
66
+ } catch {
67
+ }
68
+ if (xhr.responseURL) {
69
+ const requested = new URL(url, window.location.href).href;
70
+ if (xhr.responseURL !== requested) {
71
+ window.location.href = xhr.responseURL;
72
+ return;
73
+ }
74
+ }
75
+ if (xhr.status >= 200 && xhr.status < 400) {
76
+ if (opts.onSuccess) opts.onSuccess(content, xhr.status, xhr);
77
+ } else {
78
+ if (opts.onError) opts.onError(xhr.status, xhr);
79
+ }
80
+ };
81
+ xhr.onerror = function() {
82
+ if (opts.onError) opts.onError(xhr.status, xhr);
83
+ };
84
+ xhr.send(body);
85
+ }
86
+ function inject(html, target) {
87
+ if (!html) return "";
88
+ const parser = new DOMParser();
89
+ const wrapped = html.includes("<html>") ? html : "<body>" + html + "</body></html>";
90
+ const doc = parser.parseFromString(wrapped, "text/html");
91
+ const body = doc.querySelector("body");
92
+ const scripts = body.querySelectorAll("script");
93
+ scripts.forEach(function(s) {
94
+ s.remove();
95
+ });
96
+ if (target !== null) {
97
+ const el = document.getElementById(target);
98
+ if (!el) return "";
99
+ if (body.children.length > 0) {
100
+ el.replaceChildren.apply(el, Array.from(body.children));
101
+ } else {
102
+ el.innerHTML = body.innerHTML;
103
+ }
104
+ scripts.forEach(function(script) {
105
+ const ns = document.createElement("script");
106
+ ns.type = "text/javascript";
107
+ ns.async = true;
108
+ if (script.src) {
109
+ ns.src = script.src;
110
+ } else {
111
+ ns.textContent = script.textContent;
112
+ }
113
+ el.appendChild(ns);
114
+ });
115
+ return "";
116
+ }
117
+ scripts.forEach(function(script) {
118
+ const ns = document.createElement("script");
119
+ ns.type = "text/javascript";
120
+ ns.async = true;
121
+ ns.textContent = script.textContent;
122
+ document.body.appendChild(ns);
123
+ });
124
+ return body.innerHTML;
125
+ }
126
+ function load(url, target, callback) {
127
+ const targetId = target || "content";
128
+ request(url, {
129
+ method: "GET",
130
+ onSuccess: function(data, _status) {
131
+ if (document.getElementById(targetId)) {
132
+ const html = inject(data, targetId);
133
+ if (callback) callback(html, data);
134
+ } else {
135
+ if (callback) callback(data);
136
+ }
137
+ }
138
+ });
139
+ }
140
+ function post(url, data, target, callback) {
141
+ const targetId = target || "content";
142
+ request(url, {
143
+ method: "POST",
144
+ body: data,
145
+ onSuccess: function(responseData) {
146
+ let html = "";
147
+ if (responseData && responseData.message !== void 0) {
148
+ html = inject(responseData.message, targetId);
149
+ } else if (document.getElementById(targetId)) {
150
+ html = inject(responseData, targetId);
151
+ } else {
152
+ if (callback) callback(responseData);
153
+ return;
154
+ }
155
+ if (callback) callback(html, responseData);
156
+ }
157
+ });
158
+ }
159
+ var form = {
160
+ /**
161
+ * Collect all form field values into a FormData object.
162
+ *
163
+ * Handles inputs, selects, textareas, file uploads (including
164
+ * multi-file), checkboxes, and radio buttons. Updates formToken
165
+ * hidden fields automatically.
166
+ *
167
+ * @param formId - DOM id of the form (without '#').
168
+ * @returns Populated FormData instance.
169
+ */
170
+ collect: function(formId) {
171
+ const fd = new FormData();
172
+ const elements = document.querySelectorAll("#" + formId + " select, #" + formId + " input, #" + formId + " textarea");
173
+ for (let i = 0; i < elements.length; i++) {
174
+ const el = elements[i];
175
+ if (el.name === "formToken" && _token !== null) {
176
+ el.value = _token;
177
+ }
178
+ if (!el.name) continue;
179
+ if (el.type === "file") {
180
+ const files = el.files;
181
+ if (files) {
182
+ for (let f = 0; f < files.length; f++) {
183
+ const file = files[f];
184
+ if (file !== void 0) {
185
+ let name = el.name;
186
+ if (files.length > 1 && !name.includes("[")) {
187
+ name = name + "[]";
188
+ }
189
+ fd.append(name, file, file.name);
190
+ }
191
+ }
192
+ }
193
+ } else if (el.type === "checkbox" || el.type === "radio") {
194
+ if (el.checked) {
195
+ fd.append(el.name, el.value);
196
+ } else if (el.type !== "radio") {
197
+ fd.append(el.name, "0");
198
+ }
199
+ } else {
200
+ fd.append(el.name, el.value === "" ? "" : el.value);
201
+ }
202
+ }
203
+ return fd;
204
+ },
205
+ /**
206
+ * Collect form data and POST it to a URL. Inject response into target.
207
+ *
208
+ * @param formId - DOM id of the form.
209
+ * @param url - URL to POST to.
210
+ * @param target - DOM id to inject response into (default: "message").
211
+ * @param callback - Optional callback.
212
+ */
213
+ submit: function(formId, url, target, callback) {
214
+ const data = form.collect(formId);
215
+ post(url, data, target || "message", callback);
216
+ },
217
+ /**
218
+ * Load a form via the given action and inject response HTML.
219
+ *
220
+ * Accepts friendly names: "create", "edit" map to GET; "delete" maps
221
+ * to DELETE.
222
+ *
223
+ * @param action - HTTP method or friendly name.
224
+ * @param url - URL to fetch.
225
+ * @param target - DOM id to inject into (default: "form").
226
+ * @param callback - Optional callback.
227
+ */
228
+ show: function(action, url, target, callback) {
229
+ let method = action.toUpperCase();
230
+ if (action === "create" || action === "edit") method = "GET";
231
+ if (action === "delete") method = "DELETE";
232
+ const targetId = target || "form";
233
+ request(url, {
234
+ method,
235
+ onSuccess: function(data) {
236
+ let html = "";
237
+ if (data && data.message !== void 0) {
238
+ html = inject(data.message, targetId);
239
+ } else if (document.getElementById(targetId)) {
240
+ html = inject(data, targetId);
241
+ } else {
242
+ if (callback) callback(data);
243
+ return;
244
+ }
245
+ if (callback) callback(html);
246
+ }
247
+ });
248
+ }
249
+ };
250
+ function wsConnect(url, options) {
251
+ const opts = {
252
+ reconnect: true,
253
+ reconnectDelay: 1e3,
254
+ maxReconnectDelay: 3e4,
255
+ maxReconnectAttempts: Infinity,
256
+ protocols: [],
257
+ onOpen: function() {
258
+ },
259
+ onClose: function() {
260
+ },
261
+ onError: function() {
262
+ },
263
+ ...options || {}
264
+ };
265
+ let socket = null;
266
+ let intentionalClose = false;
267
+ let currentDelay = opts.reconnectDelay;
268
+ let attempts = 0;
269
+ let reconnectTimer = null;
270
+ const listeners = {
271
+ message: [],
272
+ open: [],
273
+ close: [],
274
+ error: []
275
+ };
276
+ const managed = {
277
+ status: "connecting",
278
+ send: function(data) {
279
+ if (!socket || socket.readyState !== WebSocket.OPEN) {
280
+ throw new Error("[frond] WebSocket is not connected");
281
+ }
282
+ socket.send(typeof data === "string" ? data : JSON.stringify(data));
283
+ },
284
+ on: function(event, handler) {
285
+ if (!listeners[event]) listeners[event] = [];
286
+ listeners[event].push(handler);
287
+ return function() {
288
+ const arr = listeners[event];
289
+ const idx = arr.indexOf(handler);
290
+ if (idx >= 0) arr.splice(idx, 1);
291
+ };
292
+ },
293
+ close: function(code, reason) {
294
+ intentionalClose = true;
295
+ if (reconnectTimer) {
296
+ clearTimeout(reconnectTimer);
297
+ reconnectTimer = null;
298
+ }
299
+ if (socket) {
300
+ socket.close(code || 1e3, reason || "");
301
+ }
302
+ managed.status = "closed";
303
+ }
304
+ };
305
+ function parseMessage(data) {
306
+ if (typeof data !== "string") return data;
307
+ try {
308
+ return JSON.parse(data);
309
+ } catch {
310
+ return data;
311
+ }
312
+ }
313
+ function scheduleReconnect() {
314
+ if (!opts.reconnect || attempts >= opts.maxReconnectAttempts) return;
315
+ attempts++;
316
+ managed.status = "reconnecting";
317
+ reconnectTimer = setTimeout(function() {
318
+ reconnectTimer = null;
319
+ connect();
320
+ }, currentDelay);
321
+ currentDelay = Math.min(currentDelay * 2, opts.maxReconnectDelay);
322
+ }
323
+ function connect() {
324
+ managed.status = attempts > 0 ? "reconnecting" : "connecting";
325
+ try {
326
+ socket = new WebSocket(url, opts.protocols);
327
+ } catch {
328
+ managed.status = "closed";
329
+ return;
330
+ }
331
+ socket.onopen = function() {
332
+ managed.status = "open";
333
+ attempts = 0;
334
+ currentDelay = opts.reconnectDelay;
335
+ opts.onOpen();
336
+ for (const fn of listeners.open) fn();
337
+ };
338
+ socket.onmessage = function(event) {
339
+ const parsed = parseMessage(event.data);
340
+ for (const fn of listeners.message) fn(parsed);
341
+ };
342
+ socket.onclose = function(event) {
343
+ managed.status = "closed";
344
+ opts.onClose(event.code, event.reason);
345
+ for (const fn of listeners.close) fn(event.code, event.reason);
346
+ if (!intentionalClose) {
347
+ scheduleReconnect();
348
+ }
349
+ };
350
+ socket.onerror = function(event) {
351
+ opts.onError(event);
352
+ for (const fn of listeners.error) fn(event);
353
+ };
354
+ }
355
+ connect();
356
+ return managed;
357
+ }
358
+ function sseConnect(url, options) {
359
+ const opts = {
360
+ reconnect: true,
361
+ reconnectDelay: 1e3,
362
+ maxReconnectDelay: 3e4,
363
+ maxReconnectAttempts: Infinity,
364
+ events: [],
365
+ json: true,
366
+ onOpen: function() {
367
+ },
368
+ onClose: function() {
369
+ },
370
+ onError: function() {
371
+ },
372
+ ...options || {}
373
+ };
374
+ let source = null;
375
+ let intentionalClose = false;
376
+ let currentDelay = opts.reconnectDelay;
377
+ let attempts = 0;
378
+ let reconnectTimer = null;
379
+ const listeners = {
380
+ message: [],
381
+ open: [],
382
+ close: [],
383
+ error: []
384
+ };
385
+ const managed = {
386
+ status: "connecting",
387
+ on: function(event, handler) {
388
+ if (!listeners[event]) listeners[event] = [];
389
+ listeners[event].push(handler);
390
+ return function() {
391
+ const arr = listeners[event];
392
+ const idx = arr.indexOf(handler);
393
+ if (idx >= 0) arr.splice(idx, 1);
394
+ };
395
+ },
396
+ close: function() {
397
+ intentionalClose = true;
398
+ if (reconnectTimer) {
399
+ clearTimeout(reconnectTimer);
400
+ reconnectTimer = null;
401
+ }
402
+ if (source) {
403
+ source.close();
404
+ source = null;
405
+ }
406
+ managed.status = "closed";
407
+ }
408
+ };
409
+ function parseData(raw) {
410
+ if (!opts.json) return raw;
411
+ try {
412
+ return JSON.parse(raw);
413
+ } catch {
414
+ return raw;
415
+ }
416
+ }
417
+ function dispatch(data, eventName) {
418
+ for (const fn of listeners.message) fn(data, eventName || void 0);
419
+ }
420
+ function scheduleReconnect() {
421
+ if (!opts.reconnect || attempts >= opts.maxReconnectAttempts) return;
422
+ attempts++;
423
+ managed.status = "reconnecting";
424
+ reconnectTimer = setTimeout(function() {
425
+ reconnectTimer = null;
426
+ connect();
427
+ }, currentDelay);
428
+ currentDelay = Math.min(currentDelay * 2, opts.maxReconnectDelay);
429
+ }
430
+ function connect() {
431
+ managed.status = attempts > 0 ? "reconnecting" : "connecting";
432
+ try {
433
+ source = new EventSource(url);
434
+ } catch {
435
+ managed.status = "closed";
436
+ return;
437
+ }
438
+ source.onopen = function() {
439
+ managed.status = "open";
440
+ attempts = 0;
441
+ currentDelay = opts.reconnectDelay;
442
+ opts.onOpen();
443
+ for (const fn of listeners.open) fn(null);
444
+ };
445
+ source.onmessage = function(event) {
446
+ dispatch(parseData(event.data), null);
447
+ };
448
+ for (const name of opts.events) {
449
+ source.addEventListener(name, function(e) {
450
+ dispatch(parseData(e.data), name);
451
+ });
452
+ }
453
+ source.onerror = function(event) {
454
+ opts.onError(event);
455
+ for (const fn of listeners.error) fn(event);
456
+ if (source && source.readyState === 2) {
457
+ source = null;
458
+ managed.status = "closed";
459
+ opts.onClose();
460
+ for (const fn of listeners.close) fn(null);
461
+ if (!intentionalClose) {
462
+ scheduleReconnect();
463
+ }
464
+ }
465
+ };
466
+ }
467
+ connect();
468
+ return managed;
469
+ }
470
+ var cookie = {
471
+ /**
472
+ * Set a browser cookie.
473
+ *
474
+ * @param name - Cookie name.
475
+ * @param value - Cookie value.
476
+ * @param days - Optional lifetime in days.
477
+ */
478
+ set: function(name, value, days) {
479
+ let expires = "";
480
+ if (days) {
481
+ const d = /* @__PURE__ */ new Date();
482
+ d.setTime(d.getTime() + days * 24 * 60 * 60 * 1e3);
483
+ expires = "; expires=" + d.toUTCString();
484
+ }
485
+ document.cookie = name + "=" + (value || "") + expires + "; path=/";
486
+ },
487
+ /**
488
+ * Retrieve a cookie value by name.
489
+ *
490
+ * @param name - Cookie name.
491
+ * @returns Cookie value, or null if not found.
492
+ */
493
+ get: function(name) {
494
+ const nameEQ = name + "=";
495
+ const parts = document.cookie.split(";");
496
+ for (let i = 0; i < parts.length; i++) {
497
+ let c = parts[i];
498
+ while (c.charAt(0) === " ") c = c.substring(1);
499
+ if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length);
500
+ }
501
+ return null;
502
+ },
503
+ /**
504
+ * Delete a cookie by name.
505
+ *
506
+ * @param name - Cookie name.
507
+ */
508
+ remove: function(name) {
509
+ document.cookie = name + "=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/";
510
+ }
511
+ };
512
+ function message(text, type) {
513
+ const el = document.getElementById("message");
514
+ if (!el) return;
515
+ const alertType = type || "info";
516
+ el.innerHTML = '<div class="alert alert-' + alertType + ' alert-dismissible">' + text + '<button type="button" class="btn-close" data-t4-dismiss="alert">&times;</button></div>';
517
+ }
518
+ function popup(url, title, w, h) {
519
+ const dualLeft = window.screenLeft !== void 0 ? window.screenLeft : window.screenX;
520
+ const dualTop = window.screenTop !== void 0 ? window.screenTop : window.screenY;
521
+ const width = window.innerWidth || document.documentElement.clientWidth || screen.width;
522
+ const height = window.innerHeight || document.documentElement.clientHeight || screen.height;
523
+ const zoom = width / window.screen.availWidth;
524
+ const left = (width - w) / 2 / zoom + dualLeft;
525
+ const top = (height - h) / 2 / zoom + dualTop;
526
+ const win = window.open(
527
+ url,
528
+ title,
529
+ "directories=no,toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=" + w / zoom + ",height=" + h / zoom + ",top=" + top + ",left=" + left
530
+ );
531
+ if (window.focus && win) win.focus();
532
+ return win;
533
+ }
534
+ function report(url) {
535
+ if (url.indexOf("No data available") >= 0) {
536
+ window.alert("No data available for this report.");
537
+ return;
538
+ }
539
+ window.open(
540
+ url,
541
+ "_blank",
542
+ "toolbar=no,scrollbars=yes,resizable=yes,width=800,height=600,top=0,left=0"
543
+ );
544
+ }
545
+ function graphql(url, query, variables, callback) {
546
+ request(url, {
547
+ method: "POST",
548
+ body: { query, variables: variables || {} },
549
+ onSuccess: function(response) {
550
+ if (callback) {
551
+ callback(response.data || null, response.errors || void 0);
552
+ }
553
+ },
554
+ onError: function(status) {
555
+ if (callback) {
556
+ callback(null, [{ message: "GraphQL request failed with status " + status }]);
557
+ }
558
+ }
559
+ });
560
+ }
561
+ var frond = {
562
+ /** Core HTTP request. */
563
+ request,
564
+ /** GET + inject HTML into target element. */
565
+ load,
566
+ /** POST + inject HTML into target element. */
567
+ post,
568
+ /** Parse HTML string, inject into element, execute scripts. */
569
+ inject,
570
+ /** Form helpers: collect, submit, show. */
571
+ form,
572
+ /** WebSocket with auto-reconnect. */
573
+ ws: wsConnect,
574
+ /** Server-Sent Events with auto-reconnect. */
575
+ sse: sseConnect,
576
+ /** Cookie helpers: get, set, remove. */
577
+ cookie,
578
+ /** Display alert message in #message element. */
579
+ message,
580
+ /** Open centred popup window. */
581
+ popup,
582
+ /** Open PDF report in new window. */
583
+ report,
584
+ /** Execute a GraphQL query/mutation. */
585
+ graphql,
586
+ /** Current bearer token (read/write). */
587
+ get token() {
588
+ return _token;
589
+ },
590
+ set token(value) {
591
+ _token = value;
592
+ }
593
+ };
594
+ if (typeof window !== "undefined") {
595
+ window.frond = frond;
596
+ }
597
+ return __toCommonJS(frond_exports);
598
+ })();
599
+ /* Frond v2.1.3 — tina4.com */
600
+ //# sourceMappingURL=frond.js.map
@@ -1,2 +1,2 @@
1
1
  var _frondModule=(()=>{var b=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var O=(o,s)=>{for(var e in s)b(o,e,{get:s[e],enumerable:!0})},M=(o,s,e,t)=>{if(s&&typeof s=="object"||typeof s=="function")for(let n of x(s))!C.call(o,n)&&n!==e&&b(o,n,{get:()=>s[n],enumerable:!(t=k(s,n))||t.enumerable});return o};var q=o=>M(b({},"__esModule",{value:!0}),o);var j={};O(j,{frond:()=>R});var g=null;function w(o,s){let e;typeof s=="function"?e={onSuccess:s}:e=s||{};let t=(e.method||"GET").toUpperCase(),n=new XMLHttpRequest;if(n.open(t,o,!0),g!==null&&n.setRequestHeader("Authorization","Bearer "+g),e.headers)for(let r in e.headers)Object.prototype.hasOwnProperty.call(e.headers,r)&&n.setRequestHeader(r,e.headers[r]);let i=null;e.body!==void 0&&e.body!==null&&(e.body instanceof FormData?i=e.body:typeof e.body=="object"?(i=JSON.stringify(e.body),n.setRequestHeader("Content-Type","application/json; charset=UTF-8")):typeof e.body=="string"&&(i=e.body,n.setRequestHeader("Content-Type","text/plain; charset=UTF-8"))),n.onload=function(){let r=n.getResponseHeader("FreshToken");r&&r!==""&&(g=r);let u=n.response;try{u=JSON.parse(u)}catch{}if(n.responseURL){let c=new URL(o,window.location.href).href;if(n.responseURL!==c){window.location.href=n.responseURL;return}}n.status>=200&&n.status<400?e.onSuccess&&e.onSuccess(u,n.status,n):e.onError&&e.onError(n.status,n)},n.onerror=function(){e.onError&&e.onError(n.status,n)},n.send(i)}function h(o,s){if(!o)return"";let e=new DOMParser,t=o.includes("<html>")?o:"<body>"+o+"</body></html>",i=e.parseFromString(t,"text/html").querySelector("body"),r=i.querySelectorAll("script");if(r.forEach(function(u){u.remove()}),s!==null){let u=document.getElementById(s);return u&&(i.children.length>0?u.replaceChildren.apply(u,Array.from(i.children)):u.innerHTML=i.innerHTML,r.forEach(function(c){let d=document.createElement("script");d.type="text/javascript",d.async=!0,c.src?d.src=c.src:d.textContent=c.textContent,u.appendChild(d)})),""}return r.forEach(function(u){let c=document.createElement("script");c.type="text/javascript",c.async=!0,c.textContent=u.textContent,document.body.appendChild(c)}),i.innerHTML}function H(o,s,e){let t=s||"content";w(o,{method:"GET",onSuccess:function(n,i){if(document.getElementById(t)){let r=h(n,t);e&&e(r,n)}else e&&e(n)}})}function S(o,s,e,t){let n=e||"content";w(o,{method:"POST",body:s,onSuccess:function(i){let r="";if(i&&i.message!==void 0)r=h(i.message,n);else if(document.getElementById(n))r=h(i,n);else{t&&t(i);return}t&&t(r,i)}})}var T={collect:function(o){let s=new FormData,e=document.querySelectorAll("#"+o+" select, #"+o+" input, #"+o+" textarea");for(let t=0;t<e.length;t++){let n=e[t];if(n.name==="formToken"&&g!==null&&(n.value=g),!!n.name)if(n.type==="file"){let i=n.files;if(i)for(let r=0;r<i.length;r++){let u=i[r];if(u!==void 0){let c=n.name;i.length>1&&!c.includes("[")&&(c=c+"[]"),s.append(c,u,u.name)}}}else n.type==="checkbox"||n.type==="radio"?n.checked?s.append(n.name,n.value):n.type!=="radio"&&s.append(n.name,"0"):s.append(n.name,n.value===""?"":n.value)}return s},submit:function(o,s,e,t){let n=T.collect(o);S(s,n,e||"message",t)},show:function(o,s,e,t){let n=o.toUpperCase();(o==="create"||o==="edit")&&(n="GET"),o==="delete"&&(n="DELETE");let i=e||"form";w(s,{method:n,onSuccess:function(r){let u="";if(r&&r.message!==void 0)u=h(r.message,i);else if(document.getElementById(i))u=h(r,i);else{t&&t(r);return}t&&t(u)}})}};function L(o,s){let e={reconnect:!0,reconnectDelay:1e3,maxReconnectDelay:3e4,maxReconnectAttempts:1/0,protocols:[],onOpen:function(){},onClose:function(){},onError:function(){},...s||{}},t=null,n=!1,i=e.reconnectDelay,r=0,u=null,c={message:[],open:[],close:[],error:[]},d={status:"connecting",send:function(l){if(!t||t.readyState!==WebSocket.OPEN)throw new Error("[frond] WebSocket is not connected");t.send(typeof l=="string"?l:JSON.stringify(l))},on:function(l,a){return c[l]||(c[l]=[]),c[l].push(a),function(){let f=c[l],m=f.indexOf(a);m>=0&&f.splice(m,1)}},close:function(l,a){n=!0,u&&(clearTimeout(u),u=null),t&&t.close(l||1e3,a||""),d.status="closed"}};function y(l){if(typeof l!="string")return l;try{return JSON.parse(l)}catch{return l}}function p(){!e.reconnect||r>=e.maxReconnectAttempts||(r++,d.status="reconnecting",u=setTimeout(function(){u=null,v()},i),i=Math.min(i*2,e.maxReconnectDelay))}function v(){d.status=r>0?"reconnecting":"connecting";try{t=new WebSocket(o,e.protocols)}catch{d.status="closed";return}t.onopen=function(){d.status="open",r=0,i=e.reconnectDelay,e.onOpen();for(let l of c.open)l()},t.onmessage=function(l){let a=y(l.data);for(let f of c.message)f(a)},t.onclose=function(l){d.status="closed",e.onClose(l.code,l.reason);for(let a of c.close)a(l.code,l.reason);n||p()},t.onerror=function(l){e.onError(l);for(let a of c.error)a(l)}}return v(),d}function D(o,s){let e={reconnect:!0,reconnectDelay:1e3,maxReconnectDelay:3e4,maxReconnectAttempts:1/0,events:[],json:!0,onOpen:function(){},onClose:function(){},onError:function(){},...s||{}},t=null,n=!1,i=e.reconnectDelay,r=0,u=null,c={message:[],open:[],close:[],error:[]},d={status:"connecting",on:function(a,f){return c[a]||(c[a]=[]),c[a].push(f),function(){let m=c[a],E=m.indexOf(f);E>=0&&m.splice(E,1)}},close:function(){n=!0,u&&(clearTimeout(u),u=null),t&&(t.close(),t=null),d.status="closed"}};function y(a){if(!e.json)return a;try{return JSON.parse(a)}catch{return a}}function p(a,f){for(let m of c.message)m(a,f||void 0)}function v(){!e.reconnect||r>=e.maxReconnectAttempts||(r++,d.status="reconnecting",u=setTimeout(function(){u=null,l()},i),i=Math.min(i*2,e.maxReconnectDelay))}function l(){d.status=r>0?"reconnecting":"connecting";try{t=new EventSource(o)}catch{d.status="closed";return}t.onopen=function(){d.status="open",r=0,i=e.reconnectDelay,e.onOpen();for(let a of c.open)a(null)},t.onmessage=function(a){p(y(a.data),null)};for(let a of e.events)t.addEventListener(a,function(f){p(y(f.data),a)});t.onerror=function(a){e.onError(a);for(let f of c.error)f(a);if(t&&t.readyState===2){t=null,d.status="closed",e.onClose();for(let f of c.close)f(null);n||v()}}}return l(),d}var W={set:function(o,s,e){let t="";if(e){let n=new Date;n.setTime(n.getTime()+e*24*60*60*1e3),t="; expires="+n.toUTCString()}document.cookie=o+"="+(s||"")+t+"; path=/"},get:function(o){let s=o+"=",e=document.cookie.split(";");for(let t=0;t<e.length;t++){let n=e[t];for(;n.charAt(0)===" ";)n=n.substring(1);if(n.indexOf(s)===0)return n.substring(s.length)}return null},remove:function(o){document.cookie=o+"=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/"}};function A(o,s){let e=document.getElementById("message");if(!e)return;let t=s||"info";e.innerHTML='<div class="alert alert-'+t+' alert-dismissible">'+o+'<button type="button" class="btn-close" data-t4-dismiss="alert">&times;</button></div>'}function I(o,s,e,t){let n=window.screenLeft!==void 0?window.screenLeft:window.screenX,i=window.screenTop!==void 0?window.screenTop:window.screenY,r=window.innerWidth||document.documentElement.clientWidth||screen.width,u=window.innerHeight||document.documentElement.clientHeight||screen.height,c=r/window.screen.availWidth,d=(r-e)/2/c+n,y=(u-t)/2/c+i,p=window.open(o,s,"directories=no,toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width="+e/c+",height="+t/c+",top="+y+",left="+d);return window.focus&&p&&p.focus(),p}function N(o){if(o.indexOf("No data available")>=0){window.alert("No data available for this report.");return}window.open(o,"_blank","toolbar=no,scrollbars=yes,resizable=yes,width=800,height=600,top=0,left=0")}function U(o,s,e,t){w(o,{method:"POST",body:{query:s,variables:e||{}},onSuccess:function(n){t&&t(n.data||null,n.errors||void 0)},onError:function(n){t&&t(null,[{message:"GraphQL request failed with status "+n}])}})}var R={request:w,load:H,post:S,inject:h,form:T,ws:L,sse:D,cookie:W,message:A,popup:I,report:N,graphql:U,get token(){return g},set token(o){g=o}};typeof window<"u"&&(window.frond=R);return q(j);})();
2
- /* Frond v2 — tina4.com */
2
+ /* Frond v2.1.3 — tina4.com */