tina4ruby 3.10.95 → 3.11.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23919261c203134fe24fb1510b3f3cdd069085ad7aed707ad660409711af5591
4
- data.tar.gz: 0ec3e28374babf5356255115ae110d58ccf667dd26afc4dd2eff0de92b43d428
3
+ metadata.gz: 011becb3bf4464d7d5d13cf3146137eb5668d5a9a3ddd77f11bd661f3eaea533
4
+ data.tar.gz: ceb3e5ed0eb3760dc2e1ff04b5d938926b6cf39d0eebab252bb7600369f3f83f
5
5
  SHA512:
6
- metadata.gz: fe0bae50d0e4dd65b4ea2e04339a211523ae2a5a2a2b11884d9d82eacc2f653bd2151ff9e5fe736278358a96a3407252576de7cf44e447f9e0c3bb5792244fc6
7
- data.tar.gz: 1f141ba1ce2d4155cfeb6451219f04069fda98162a0d9cc08805de5b71b62b3fa9bda5033d73fef697809062be6f0d5897ba6d7944dddc85e764caa45f34fd27
6
+ metadata.gz: 4c644d5d7d592e5a0ba473d626e3edb88df50f73a0b1579178a27e0d53cec216dee0ed7fd6677954b24d1bb14423865e439f78ddde5e27e1028cd85b15d52b00
7
+ data.tar.gz: a76ac4b87256ade5abecdf7f407625da31f3b1ea9f81d810a13933c78d2e1a2e9a2d39a2c92157071e8374b79bc7fd61260f5aeddb87b6313ee6febad3583f9a
data/lib/tina4/frond.rb CHANGED
@@ -43,7 +43,7 @@ module Tina4
43
43
  FLOAT_RE = /\A-?\d+\.\d+\z/
44
44
  ARRAY_LIT_RE = /\A\[(.+)\]\z/m
45
45
  HASH_LIT_RE = /\A\{(.+)\}\z/m
46
- HASH_PAIR_RE = /\A\s*["']?(\w+)["']?\s*:\s*(.+)\z/
46
+ HASH_PAIR_RE = /\A\s*(?:["']([^"']+)["']|(\w+))\s*:\s*(.+)\z/
47
47
  RANGE_LIT_RE = /\A(\d+)\.\.(\d+)\z/
48
48
  ARITHMETIC_OPS = [" + ", " - ", " * ", " // ", " / ", " % ", " ** "].freeze
49
49
  FUNC_CALL_RE = /\A(\w+)\s*\((.*)\)\z/m
@@ -827,10 +827,10 @@ module Tina4
827
827
  elsif ch == '"' || ch == "'"
828
828
  in_quote = ch
829
829
  current << ch
830
- elsif ch == "("
830
+ elsif ch == "(" || ch == "{" || ch == "["
831
831
  depth += 1
832
832
  current << ch
833
- elsif ch == ")"
833
+ elsif ch == ")" || ch == "}" || ch == "]"
834
834
  depth -= 1
835
835
  current << ch
836
836
  elsif ch == "," && depth == 0
@@ -915,7 +915,8 @@ module Tina4
915
915
  hash = {}
916
916
  split_args_toplevel(inner).each do |pair|
917
917
  if pair =~ HASH_PAIR_RE
918
- hash[Regexp.last_match(1)] = eval_expr(Regexp.last_match(2).strip, context)
918
+ key = Regexp.last_match(1) || Regexp.last_match(2)
919
+ hash[key] = eval_expr(Regexp.last_match(3).strip, context)
919
920
  end
920
921
  end
921
922
  return hash
@@ -1728,7 +1729,17 @@ module Tina4
1728
1729
  "trim" => ->(v, *_a) { v.to_s.strip },
1729
1730
  "ltrim" => ->(v, *_a) { v.to_s.lstrip },
1730
1731
  "rtrim" => ->(v, *_a) { v.to_s.rstrip },
1731
- "replace" => ->(v, *a) { a.length >= 2 ? v.to_s.gsub(a[0].to_s, a[1].to_s) : v.to_s },
1732
+ "replace" => ->(v, *a) {
1733
+ if a.length == 1 && a[0].is_a?(Hash)
1734
+ result = v.to_s
1735
+ a[0].each { |old, new_val| result = result.gsub(old.to_s, new_val.to_s) }
1736
+ result
1737
+ elsif a.length >= 2
1738
+ v.to_s.gsub(a[0].to_s, a[1].to_s)
1739
+ else
1740
+ v.to_s
1741
+ end
1742
+ },
1732
1743
  "striptags" => ->(v, *_a) { v.to_s.gsub(STRIPTAGS_RE, "") },
1733
1744
 
1734
1745
  # -- Encoding --
data/lib/tina4/orm.rb CHANGED
@@ -54,7 +54,7 @@ module Tina4
54
54
 
55
55
  # Auto-map flag (no-op in Ruby since snake_case is native)
56
56
  def auto_map
57
- @auto_map || false
57
+ @auto_map.nil? ? true : @auto_map
58
58
  end
59
59
 
60
60
  def auto_map=(val)
@@ -599,7 +599,8 @@ module Tina4
599
599
 
600
600
  # Convert to hash using Ruby attribute names.
601
601
  # Optionally include relationships via the include keyword.
602
- def to_h(include: nil) # -> dict
602
+ # case: 'snake' (default) returns snake_case keys, 'camel' returns camelCase keys.
603
+ def to_h(include: nil, case: 'snake') # -> dict
603
604
  hash = {}
604
605
  self.class.field_definitions.each_key do |name|
605
606
  hash[name] = __send__(name)
@@ -621,20 +622,41 @@ module Tina4
621
622
  if related.nil?
622
623
  hash[rel_name] = nil
623
624
  elsif related.is_a?(Array)
624
- hash[rel_name] = related.map { |r| r.to_h(include: nested.empty? ? nil : nested) }
625
+ hash[rel_name] = related.map { |r| r.to_h(include: nested.empty? ? nil : nested, case: binding.local_variable_get(:case)) }
625
626
  else
626
- hash[rel_name] = related.to_h(include: nested.empty? ? nil : nested)
627
+ hash[rel_name] = related.to_h(include: nested.empty? ? nil : nested, case: binding.local_variable_get(:case))
627
628
  end
628
629
  end
629
630
  end
630
631
 
632
+ case_mode = binding.local_variable_get(:case)
633
+ if case_mode == 'camel'
634
+ camel_hash = {}
635
+ hash.each do |key, value|
636
+ camel_key = Tina4.snake_to_camel(key.to_s).to_sym
637
+ camel_hash[camel_key] = value
638
+ end
639
+ return camel_hash
640
+ end
641
+
631
642
  hash
632
643
  end
633
644
 
634
- alias to_hash to_h
635
- alias to_dict to_h
636
- alias to_assoc to_h
637
- alias to_object to_h
645
+ def to_hash(include: nil, case: 'snake')
646
+ to_h(include: include, case: binding.local_variable_get(:case))
647
+ end
648
+
649
+ def to_dict(include: nil, case: 'snake')
650
+ to_h(include: include, case: binding.local_variable_get(:case))
651
+ end
652
+
653
+ def to_assoc(include: nil, case: 'snake')
654
+ to_h(include: include, case: binding.local_variable_get(:case))
655
+ end
656
+
657
+ def to_object(include: nil, case: 'snake')
658
+ to_h(include: include, case: binding.local_variable_get(:case))
659
+ end
638
660
 
639
661
  def to_array # -> list
640
662
  to_h.values
@@ -1,420 +1,2 @@
1
- (function (global, factory) {
2
- if (typeof define === 'function' && define.amd) {
3
- define([], factory);
4
- } else if (typeof module !== 'undefined' && module.exports){
5
- module.exports = factory();
6
- } else {
7
- global.ReconnectingWebSocket = factory();
8
- }
9
- })(this, function () {
10
- if (!('WebSocket' in window)) {
11
- return;
12
- }
13
- function ReconnectingWebSocket(url, protocols, options) {
14
- var settings = {
15
- debug: false,
16
- automaticOpen: true,
17
- reconnectInterval: 1000,
18
- maxReconnectInterval: 30000,
19
- reconnectDecay: 1.5,
20
- timeoutInterval: 2000,
21
- maxReconnectAttempts: null,
22
- binaryType: 'blob'
23
- }
24
- if (!options) { options = {}; }
25
- for (var key in settings) {
26
- if (typeof options[key] !== 'undefined') {
27
- this[key] = options[key];
28
- } else {
29
- this[key] = settings[key];
30
- }
31
- }
32
- this.url = url;
33
- this.reconnectAttempts = 0;
34
- this.readyState = WebSocket.CONNECTING;
35
- this.protocol = null;
36
- var self = this;
37
- var ws;
38
- var forcedClose = false;
39
- var timedOut = false;
40
- var eventTarget = document.createElement('div');
41
- eventTarget.addEventListener('open', function(event) { self.onopen(event); });
42
- eventTarget.addEventListener('close', function(event) { self.onclose(event); });
43
- eventTarget.addEventListener('connecting', function(event) { self.onconnecting(event); });
44
- eventTarget.addEventListener('message', function(event) { self.onmessage(event); });
45
- eventTarget.addEventListener('error', function(event) { self.onerror(event); });
46
- this.addEventListener = eventTarget.addEventListener.bind(eventTarget);
47
- this.removeEventListener = eventTarget.removeEventListener.bind(eventTarget);
48
- this.dispatchEvent = eventTarget.dispatchEvent.bind(eventTarget);
49
- function generateEvent(s, args) {
50
- var evt = document.createEvent("CustomEvent");
51
- evt.initCustomEvent(s, false, false, args);
52
- return evt;
53
- };
54
- this.open = function (reconnectAttempt) {
55
- ws = new WebSocket(self.url, protocols || []);
56
- ws.binaryType = this.binaryType;
57
- if (reconnectAttempt) {
58
- if (this.maxReconnectAttempts && this.reconnectAttempts > this.maxReconnectAttempts) {
59
- return;
60
- }
61
- } else {
62
- eventTarget.dispatchEvent(generateEvent('connecting'));
63
- this.reconnectAttempts = 0;
64
- }
65
- if (self.debug || ReconnectingWebSocket.debugAll) {
66
- console.debug('ReconnectingWebSocket', 'attempt-connect', self.url);
67
- }
68
- var localWs = ws;
69
- var timeout = setTimeout(function() {
70
- if (self.debug || ReconnectingWebSocket.debugAll) {
71
- console.debug('ReconnectingWebSocket', 'connection-timeout', self.url);
72
- }
73
- timedOut = true;
74
- localWs.close();
75
- timedOut = false;
76
- }, self.timeoutInterval);
77
- ws.onopen = function(event) {
78
- clearTimeout(timeout);
79
- if (self.debug || ReconnectingWebSocket.debugAll) {
80
- console.debug('ReconnectingWebSocket', 'onopen', self.url);
81
- }
82
- self.protocol = ws.protocol;
83
- self.readyState = WebSocket.OPEN;
84
- self.reconnectAttempts = 0;
85
- var e = generateEvent('open');
86
- e.isReconnect = reconnectAttempt;
87
- reconnectAttempt = false;
88
- eventTarget.dispatchEvent(e);
89
- };
90
- ws.onclose = function(event) {
91
- clearTimeout(timeout);
92
- ws = null;
93
- if (forcedClose) {
94
- self.readyState = WebSocket.CLOSED;
95
- eventTarget.dispatchEvent(generateEvent('close'));
96
- } else {
97
- self.readyState = WebSocket.CONNECTING;
98
- var e = generateEvent('connecting');
99
- e.code = event.code;
100
- e.reason = event.reason;
101
- e.wasClean = event.wasClean;
102
- eventTarget.dispatchEvent(e);
103
- if (!reconnectAttempt && !timedOut) {
104
- if (self.debug || ReconnectingWebSocket.debugAll) {
105
- console.debug('ReconnectingWebSocket', 'onclose', self.url);
106
- }
107
- eventTarget.dispatchEvent(generateEvent('close'));
108
- }
109
- var timeout = self.reconnectInterval * Math.pow(self.reconnectDecay, self.reconnectAttempts);
110
- setTimeout(function() {
111
- self.reconnectAttempts++;
112
- self.open(true);
113
- }, timeout > self.maxReconnectInterval ? self.maxReconnectInterval : timeout);
114
- }
115
- };
116
- ws.onmessage = function(event) {
117
- if (self.debug || ReconnectingWebSocket.debugAll) {
118
- console.debug('ReconnectingWebSocket', 'onmessage', self.url, event.data);
119
- }
120
- var e = generateEvent('message');
121
- e.data = event.data;
122
- eventTarget.dispatchEvent(e);
123
- };
124
- ws.onerror = function(event) {
125
- if (self.debug || ReconnectingWebSocket.debugAll) {
126
- console.debug('ReconnectingWebSocket', 'onerror', self.url, event);
127
- }
128
- eventTarget.dispatchEvent(generateEvent('error'));
129
- };
130
- }
131
- if (this.automaticOpen == true) {
132
- this.open(false);
133
- }
134
- this.send = function(data) {
135
- if (ws) {
136
- if (self.debug || ReconnectingWebSocket.debugAll) {
137
- console.debug('ReconnectingWebSocket', 'send', self.url, data);
138
- }
139
- return ws.send(data);
140
- } else {
141
- throw 'INVALID_STATE_ERR : Pausing to reconnect websocket';
142
- }
143
- };
144
- this.close = function(code, reason) {
145
- if (typeof code == 'undefined') {
146
- code = 1000;
147
- }
148
- forcedClose = true;
149
- if (ws) {
150
- ws.close(code, reason);
151
- }
152
- };
153
- this.refresh = function() {
154
- if (ws) {
155
- ws.close();
156
- }
157
- };
158
- }
159
- ReconnectingWebSocket.prototype.onopen = function(event) {};
160
- ReconnectingWebSocket.prototype.onclose = function(event) {};
161
- ReconnectingWebSocket.prototype.onconnecting = function(event) {};
162
- ReconnectingWebSocket.prototype.onmessage = function(event) {};
163
- ReconnectingWebSocket.prototype.onerror = function(event) {};
164
- ReconnectingWebSocket.debugAll = false;
165
- ReconnectingWebSocket.CONNECTING = WebSocket.CONNECTING;
166
- ReconnectingWebSocket.OPEN = WebSocket.OPEN;
167
- ReconnectingWebSocket.CLOSING = WebSocket.CLOSING;
168
- ReconnectingWebSocket.CLOSED = WebSocket.CLOSED;
169
- return ReconnectingWebSocket;
170
- });
171
- var formToken = null;
172
- function sendRequest(url, request, method, callback) {
173
- if (url === undefined) url = "";
174
- if (request === undefined) request = null;
175
- if (method === undefined) method = 'GET';
176
- const xhr = new XMLHttpRequest();
177
- xhr.open(method, url, true);
178
- if (formToken !== null) {
179
- xhr.setRequestHeader('Authorization', 'Bearer ' + formToken);
180
- }
181
- let isFormData = request instanceof FormData;
182
- if (method.toUpperCase() === 'POST' || method.toUpperCase() === 'PUT' || method.toUpperCase() === 'PATCH') {
183
- if (isFormData) {
184
- } else if (typeof request === 'object' && request !== null) {
185
- request = JSON.stringify(request);
186
- xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
187
- } else if (typeof request === 'string') {
188
- xhr.setRequestHeader('Content-Type', 'text/plain; charset=UTF-8');
189
- }
190
- }
191
- xhr.onload = function () {
192
- let content = xhr.response;
193
- const freshToken = xhr.getResponseHeader('FreshToken');
194
- if (freshToken && freshToken !== '') {
195
- formToken = freshToken;
196
- }
197
- try {
198
- content = JSON.parse(content);
199
- } catch (e) {
200
- }
201
- if (typeof callback === 'function') {
202
- callback(content, xhr.status, xhr);
203
- }
204
- };
205
- xhr.onerror = function () {
206
- if (typeof callback === 'function') {
207
- callback(null, xhr.status, xhr);
208
- }
209
- };
210
- xhr.send(request);
211
- }
212
- function getFormData(formId) {
213
- let data = new FormData();
214
- let elements = document.querySelectorAll("#" + formId + " select, #" + formId + " input, #" + formId + " textarea");
215
- for (let ie = 0; ie < elements.length; ie++ )
216
- {
217
- let element = elements[ie];
218
- if (element.name === 'formToken' && formToken !== null) {
219
- element.value = formToken;
220
- }
221
- if (element.name) {
222
- if (element.type === 'file') {
223
- for (let i = 0; i < element.files.length; i++) {
224
- let fileData = element.files[i];
225
- let elementName = element.name;
226
- if (fileData !== undefined) {
227
- if (element.files.length > 1 && !elementName.includes('[')) {
228
- elementName = elementName + '[]';
229
- }
230
- data.append(elementName, fileData, fileData.name);
231
- }
232
- }
233
- } else if (element.type === 'checkbox' || element.type === 'radio') {
234
- if (element.checked) {
235
- data.append(element.name, element.value)
236
- } else {
237
- if (element.type !== 'radio') {
238
- data.append(element.name, "0")
239
- }
240
- }
241
- } else {
242
- if (element.value === '') {
243
- element.value = null;
244
- }
245
- data.append(element.name, element.value);
246
- }
247
- }
248
- }
249
- return data;
250
- }
251
- function handleHtmlData(data, targetElement) {
252
- if (data === "") return '';
253
- const parser = new DOMParser();
254
- const htmlData = parser.parseFromString(data.includes !== undefined && data.includes('<html>') ? data : '<body>'+data+'</body></html>', 'text/html');
255
- const body = htmlData.querySelector('body');
256
- const scripts = body.querySelectorAll('script');
257
- body.querySelectorAll('script').forEach(script => script.remove());
258
- if (targetElement !== null) {
259
- if (body.children.length > 0) {
260
- document.getElementById(targetElement).replaceChildren(...body.children);
261
- } else {
262
- document.getElementById(targetElement).replaceChildren(body.innerHTML);
263
- }
264
- if (scripts) {
265
- scripts.forEach(script => {
266
- const newScript = document.createElement("script");
267
- newScript.type = 'text/javascript';
268
- newScript.async = true;
269
- newScript.textContent = script.innerText;
270
- document.getElementById(targetElement).append(newScript);
271
- });
272
- }
273
- } else {
274
- if (scripts) {
275
- scripts.forEach(script => {
276
- const newScript = document.createElement("script");
277
- newScript.type = 'text/javascript';
278
- newScript.async = true;
279
- newScript.textContent = script.innerText;
280
- document.body.append(newScript);
281
- console.log(newScript);
282
- });
283
- }
284
- return body.innerHTML;
285
- }
286
- return '';
287
- }
288
- function loadPage(loadURL, targetElement, callback = null) {
289
- if (targetElement === undefined) targetElement = 'content';
290
- sendRequest(loadURL, null, "GET", function(data) {
291
- let processedHTML = '';
292
- if (document.getElementById(targetElement) !== null) {
293
- processedHTML = handleHtmlData(data, targetElement);
294
- } else {
295
- if (callback) {
296
- callback(data);
297
- } else {
298
- console.log('TINA4 - define targetElement or callback for loadPage', data);
299
- }
300
- return;
301
- }
302
- if (callback) {
303
- callback(processedHTML, data);
304
- }
305
- });
306
- }
307
- function showForm(action, loadURL, targetElement, callback = null) {
308
- if (targetElement === undefined) targetElement = 'form';
309
- if (action === 'create') action = 'GET';
310
- if (action === 'edit') action = 'GET';
311
- if (action === 'delete') action = 'DELETE';
312
- sendRequest(loadURL, null, action, function(data) {
313
- let processedHTML = '';
314
- if (data.message !== undefined) {
315
- processedHTML = handleHtmlData ((data.message), targetElement);
316
- } else {
317
- if (document.getElementById(targetElement) !== null) {
318
- processedHTML = handleHtmlData (data, targetElement);
319
- } else {
320
- if (callback) {
321
- callback(data);
322
- } else {
323
- console.log('TINA4 - define targetElement or callback for showForm', data);
324
- }
325
- return;
326
- }
327
- }
328
- if (callback) {
329
- callback(processedHTML);
330
- }
331
- });
332
- }
333
- function postUrl(url, data, targetElement, callback= null) {
334
- sendRequest(url, data, 'POST', function(data) {
335
- let processedHTML = '';
336
- if (data.message !== undefined) {
337
- processedHTML = handleHtmlData ((data.message), targetElement);
338
- } else {
339
- if (document.getElementById(targetElement) !== null) {
340
- processedHTML = handleHtmlData (data, targetElement);
341
- } else {
342
- if (callback) {
343
- callback(data);
344
- } else {
345
- console.log('TINA4 - define targetElement or callback for postUrl', data);
346
- }
347
- return;
348
- }
349
- }
350
- if (callback) {
351
- callback(processedHTML,data)
352
- }
353
- });
354
- }
355
- function saveForm(formId, targetURL, targetElement, callback = null) {
356
- if (targetElement === undefined) targetElement = 'message';
357
- let data = getFormData(formId);
358
- postUrl(targetURL, data, targetElement, callback);
359
- }
360
- function postForm(formId, targetURL, targetElement, callback = null){
361
- saveForm(formId, targetURL, targetElement, callback)
362
- }
363
- function submitForm(formId, targetURL, targetElement, callback = null){
364
- saveForm(formId, targetURL, targetElement, callback)
365
- }
366
- function showMessage(message) {
367
- document.getElementById('message').innerHTML = '<div class="alert alert-info alert-dismissible fade show"><strong>Info</strong> ' + message + '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
368
- }
369
- function setCookie(name, value, days) {
370
- let expires = "";
371
- if (days) {
372
- let date = new Date();
373
- date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
374
- expires = "; expires=" + date.toUTCString();
375
- }
376
- document.cookie = name + "=" + (value || "") + expires + "; path=/";
377
- }
378
- function getCookie(name) {
379
- let nameEQ = name + "=";
380
- let ca = document.cookie.split(';');
381
- for (let i = 0; i < ca.length; i++) {
382
- var c = ca[i];
383
- while (c.charAt(0) == ' ') c = c.substring(1, c.length);
384
- if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
385
- }
386
- return null;
387
- }
388
- const popupCenter = ({url, title, w, h}) => {
389
- const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : window.screenX;
390
- const dualScreenTop = window.screenTop !== undefined ? window.screenTop : window.screenY;
391
- const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
392
- const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height;
393
- const systemZoom = width / window.screen.availWidth;
394
- const left = (width - w) / 2 / systemZoom + dualScreenLeft
395
- const top = (height - h) / 2 / systemZoom + dualScreenTop
396
- const newWindow = window.open(url, title,
397
- `
398
- directories=no,toolbar=no,location=no,status=no,menubar=no,scrollbars=no,resizable=no,
399
- width=${w / systemZoom},
400
- height=${h / systemZoom},
401
- top=${top},
402
- left=${left}
403
- `
404
- )
405
- if (window.focus) newWindow.focus();
406
- return newWindow;
407
- }
408
- function openReport(pdfReportPath){
409
- if (pdfReportPath.indexOf("No data available") < 0){
410
- open(pdfReportPath, "content", "target=_blank, toolbar=no, scrollbars=yes, resizable=yes, width=800, height=600, top=0, left=0");
411
- }
412
- else {
413
- window.alert("Sorry , unable to print a report according to your selection!");
414
- }
415
- }
416
- function getRoute(loadURL, callback) {
417
- sendRequest(loadURL, null, 'GET', function(data) {
418
- callback(handleHtmlData (data, null));
419
- });
420
- }
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 */
@@ -63,8 +63,11 @@ module Tina4
63
63
  relative = scss_file.sub(base_dir, "").sub(/\.scss$/, ".css")
64
64
  css_file = File.join(output_dir, relative)
65
65
  FileUtils.mkdir_p(File.dirname(css_file))
66
- File.write(css_file, css_content)
67
- Tina4::Log.debug("Compiled SCSS: #{scss_file} -> #{css_file}")
66
+ existing = File.exist?(css_file) ? File.read(css_file) : nil
67
+ if existing != css_content
68
+ File.write(css_file, css_content)
69
+ Tina4::Log.debug("Compiled SCSS: #{scss_file} -> #{css_file}")
70
+ end
68
71
  end
69
72
 
70
73
  css_content
data/lib/tina4/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tina4
4
- VERSION = "3.10.95"
4
+ VERSION = "3.11.0"
5
5
  end
@@ -54,7 +54,8 @@ module Tina4
54
54
  end
55
55
 
56
56
  def start
57
- unless ENV['TINA4_CLI'] == 'true' || ENV['TINA4_OVERRIDE_CLIENT'] == 'true'
57
+ is_managed = ARGV.include?('--managed')
58
+ unless is_managed || ENV['TINA4_OVERRIDE_CLIENT'] == 'true'
58
59
  puts
59
60
  puts '=' * 60
60
61
  puts
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tina4ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.10.95
4
+ version: 3.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tina4 Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-04-11 00:00:00.000000000 Z
11
+ date: 2026-04-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack