@magic-xpa/engine 4.1300.0-dev4130.184 → 4.1300.0-dev4130.186
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StringBuilder, Int32, NString, ApplicationException, NNumber, List, Debug, Char, RefParam, NumberStyles, HashUtils, DateTime, Dictionary, Stack, NChar, isNullOrUndefined, WebException, Thread, Encoding, Exception, isUndefined, Hashtable, NotImplementedException, Array_Enumerator, ISO_8859_1_Encoding, Int64 } from '@magic-xpa/mscorelib';
|
|
2
2
|
import { XMLConstants, StorageAttribute, ViewRefreshMode, InternalInterface, Logger, StorageAttributeCheck, StrUtil, SubformType, TableBehaviour, MgControlType, ScrollBarThumbType, ForceExit, XmlParser, Misc, Base64, Priority, SyncExecutionHelper, Queue, Constants, DateTimeUtils, Logger_LogLevels, Logger_MessageDirection, MsgInterface, RequestInfo, OSEnvironment, JSON_Utils, UtilDateJpn, UtilStrByteMode, PICInterface, WindowType, BrkScope, RaiseAt, CtrlButtonTypeGui } from '@magic-xpa/utils';
|
|
3
|
-
import { RecordUtils, GuiFieldBase, ExpVal, BlobType, FieldDef, GuiTaskBase, MgControlBase, PropInterface, GuiDataCollection, CommandType, Commands, HtmlProperties, ControlTable, Modifiers, KeyboardItem, TaskDefinitionIdTableSaxHandler, DisplayConvertor, VectorType, PIC, MgTimer, GuiConstants, RuntimeContextBase, UsernamePasswordCredentials, Styles, Manager, NUM_TYPE, GuiExpressionEvaluator, ExpressionInterface, DataModificationTypes, GuiDataViewBase, ObjectReferencesCollection, EMPTY_DCREF, ObjectReferenceBase, PropTable, FieldsTable as FieldsTable$1, DcValuesBuilderBase, MgFormBase, GuiEnvironment, TaskDefinitionId, Events, Helps, FocusManager, EventsProcessor, UIBridge } from '@magic-xpa/gui';
|
|
3
|
+
import { RecordUtils, GuiFieldBase, ExpVal, BlobType, FieldDef, GuiTaskBase, MgControlBase, PropInterface, GuiDataCollection, CommandType, Commands, HtmlProperties, ControlTable, Modifiers, KeyboardItem, TaskDefinitionIdTableSaxHandler, DisplayConvertor, VectorType, PIC, MgTimer, GuiConstants, RuntimeContextBase, UsernamePasswordCredentials, Styles, Manager, NUM_TYPE, GuiExpressionEvaluator, ExpressionInterface, DateBreakParams, DataModificationTypes, GuiDataViewBase, ObjectReferencesCollection, EMPTY_DCREF, ObjectReferenceBase, PropTable, FieldsTable as FieldsTable$1, DcValuesBuilderBase, MgFormBase, GuiEnvironment, TaskDefinitionId, Events, Helps, FocusManager, EventsProcessor, UIBridge } from '@magic-xpa/gui';
|
|
4
4
|
import { HttpHeaders, HttpErrorResponse } from '@angular/common/http';
|
|
5
5
|
import { timer, Subject } from 'rxjs';
|
|
6
6
|
import { io } from 'socket.io-client';
|
|
@@ -9784,6 +9784,10 @@ class ExpressionDict {
|
|
|
9784
9784
|
null,
|
|
9785
9785
|
new ExpDesc('U', 0, -1, -1, 'B', false), /* 719- Subscribe */
|
|
9786
9786
|
new ExpDesc('U', 0, -1, -1, 'B', false), /* 720- Unsubscribe */
|
|
9787
|
+
null,
|
|
9788
|
+
null,
|
|
9789
|
+
new ExpDesc('B', 0, 4, 4, 'TDVV', false), /* 723- LocalToUTC */
|
|
9790
|
+
new ExpDesc('B', 0, 4, 4, 'TDVV', false), /* 724- UTCToLocal */
|
|
9787
9791
|
];
|
|
9788
9792
|
}
|
|
9789
9793
|
|
|
@@ -15642,6 +15646,20 @@ class ExpressionEvaluator extends GuiExpressionEvaluator {
|
|
|
15642
15646
|
resVal.Attr = StorageAttribute.BOOLEAN;
|
|
15643
15647
|
resVal.BoolVal = this.eval_op_UnSubscribe(valStack);
|
|
15644
15648
|
break;
|
|
15649
|
+
case ExpressionInterface.EXP_OP_LOCALTOUTC:
|
|
15650
|
+
val4 = valStack.pop(); // UTC date output variable
|
|
15651
|
+
val3 = valStack.pop(); // UTC time output variable
|
|
15652
|
+
val2 = valStack.pop(); // local date
|
|
15653
|
+
val1 = valStack.pop(); // local time
|
|
15654
|
+
await this.eval_op_localToUTC(resVal, val1, val2, val3, val4);
|
|
15655
|
+
break;
|
|
15656
|
+
case ExpressionInterface.EXP_OP_UTCTOLOCAL:
|
|
15657
|
+
val4 = valStack.pop(); // local date output variable
|
|
15658
|
+
val3 = valStack.pop(); // local time output variable
|
|
15659
|
+
val2 = valStack.pop(); // UTC date
|
|
15660
|
+
val1 = valStack.pop(); // UTC time
|
|
15661
|
+
await this.eval_op_utcToLocal(resVal, val1, val2, val3, val4);
|
|
15662
|
+
break;
|
|
15645
15663
|
default:
|
|
15646
15664
|
return;
|
|
15647
15665
|
}
|
|
@@ -16556,6 +16574,217 @@ class ExpressionEvaluator extends GuiExpressionEvaluator {
|
|
|
16556
16574
|
resVal.Attr = StorageAttribute.BOOLEAN;
|
|
16557
16575
|
resVal.BoolVal = true;
|
|
16558
16576
|
}
|
|
16577
|
+
/**
|
|
16578
|
+
* Safely extracts date/time parts from a JS Date using Intl.DateTimeFormat.
|
|
16579
|
+
*
|
|
16580
|
+
* WHY: Native JS Date has known pitfalls — century year bug (year < 100 mapped to 1900+year),
|
|
16581
|
+
* silent leap year rollovers, DST gaps, and 0-based months. Intl.DateTimeFormat avoids all
|
|
16582
|
+
* of these by using the browser's IANA timezone database internally.
|
|
16583
|
+
*
|
|
16584
|
+
* Used by both eval_op_localToUTC and eval_op_utcToLocal as a single safe extraction point.
|
|
16585
|
+
*
|
|
16586
|
+
* @param date - A valid JS Date object
|
|
16587
|
+
* @param timeZone - IANA timezone string (e.g. 'Asia/Kolkata', 'UTC').
|
|
16588
|
+
* Defaults to browser's local timezone — correct for any user, any country.
|
|
16589
|
+
* @returns Parsed parts (month is 1-based) or null if date is invalid
|
|
16590
|
+
*/
|
|
16591
|
+
convertDateTimeParts(date, timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone) {
|
|
16592
|
+
if (!date || isNaN(date.getTime()))
|
|
16593
|
+
return null;
|
|
16594
|
+
// 'en-CA' locale formats date as YYYY-MM-DD — unambiguous and consistent across
|
|
16595
|
+
// all browsers. Other locales like en-US (MM/DD/YYYY) or en-GB (DD/MM/YYYY) can
|
|
16596
|
+
// behave differently across Chrome, Firefox and Safari. Nothing to do with Canada.
|
|
16597
|
+
const formatter = new Intl.DateTimeFormat('en-CA', {
|
|
16598
|
+
timeZone,
|
|
16599
|
+
year: 'numeric',
|
|
16600
|
+
month: '2-digit',
|
|
16601
|
+
day: '2-digit',
|
|
16602
|
+
hour: '2-digit',
|
|
16603
|
+
minute: '2-digit',
|
|
16604
|
+
second: '2-digit',
|
|
16605
|
+
hour12: false
|
|
16606
|
+
});
|
|
16607
|
+
const parts = formatter.formatToParts(date);
|
|
16608
|
+
const get = (type) => {
|
|
16609
|
+
const part = parts.find(p => p.type === type);
|
|
16610
|
+
if (!part)
|
|
16611
|
+
throw new Error(`Missing date part: ${type}`);
|
|
16612
|
+
return parseInt(part.value, 10);
|
|
16613
|
+
};
|
|
16614
|
+
try {
|
|
16615
|
+
const hour = get('hour');
|
|
16616
|
+
return {
|
|
16617
|
+
year: get('year'),
|
|
16618
|
+
month: get('month'), // 1-based because Intl returns display month (1–12), unlike JS Date.getMonth() which is 0-based
|
|
16619
|
+
day: get('day'),
|
|
16620
|
+
hour: hour === 24 ? 0 : hour, // normalize midnight — some browsers return 24 instead of 0
|
|
16621
|
+
minute: get('minute'),
|
|
16622
|
+
second: get('second')
|
|
16623
|
+
};
|
|
16624
|
+
}
|
|
16625
|
+
catch {
|
|
16626
|
+
return null;
|
|
16627
|
+
}
|
|
16628
|
+
}
|
|
16629
|
+
/**
|
|
16630
|
+
* Validates that a time value (in total seconds) is within a valid day range
|
|
16631
|
+
*/
|
|
16632
|
+
isValidTimeSec(timeSec) {
|
|
16633
|
+
return Number.isInteger(timeSec) && timeSec >= 0 && timeSec <= 86399;
|
|
16634
|
+
}
|
|
16635
|
+
/**
|
|
16636
|
+
* Splits total seconds since midnight into hours, minutes, seconds.
|
|
16637
|
+
* e.g. 3661 → { hours: 1, minutes: 1, seconds: 1 }
|
|
16638
|
+
*/
|
|
16639
|
+
splitTimeSec(timeSec) {
|
|
16640
|
+
return {
|
|
16641
|
+
hours: Math.floor(timeSec / 3600),
|
|
16642
|
+
minutes: Math.floor((timeSec % 3600) / 60),
|
|
16643
|
+
seconds: timeSec % 60
|
|
16644
|
+
};
|
|
16645
|
+
}
|
|
16646
|
+
/**
|
|
16647
|
+
* Writes a time (seconds) and date (Magic numeric) into their respective output fields.
|
|
16648
|
+
*/
|
|
16649
|
+
async writeDateTimeToFields(timeFld, dateTimeSec, dateFld, dateVal) {
|
|
16650
|
+
const tmpVal = new ExpVal();
|
|
16651
|
+
tmpVal.MgNumVal = new NUM_TYPE();
|
|
16652
|
+
tmpVal.Attr = StorageAttribute.TIME;
|
|
16653
|
+
tmpVal.MgNumVal.NUM_4_LONG(dateTimeSec);
|
|
16654
|
+
await timeFld.setValueAndStartRecompute(tmpVal.ToMgVal(), false, true, false, false);
|
|
16655
|
+
await timeFld.updateDisplay();
|
|
16656
|
+
tmpVal.Attr = StorageAttribute.DATE;
|
|
16657
|
+
tmpVal.MgNumVal.NUM_4_LONG(dateVal);
|
|
16658
|
+
await dateFld.setValueAndStartRecompute(tmpVal.ToMgVal(), false, true, false, false);
|
|
16659
|
+
await dateFld.updateDisplay();
|
|
16660
|
+
}
|
|
16661
|
+
/**
|
|
16662
|
+
* Converts local date/time to UTC and writes the result into output fields.
|
|
16663
|
+
*
|
|
16664
|
+
* Edge cases handled:
|
|
16665
|
+
* - Century years — setFullYear() avoids JS year < 100 bug
|
|
16666
|
+
* - Leap years — round-trip validation catches silent date rollovers
|
|
16667
|
+
* - DST gaps — Intl.DateTimeFormat resolves correctly per IANA tz rules
|
|
16668
|
+
* - DST ambiguity — Intl uses first occurrence consistently
|
|
16669
|
+
* - Cross-midnight — JS Date handles automatically
|
|
16670
|
+
* - Out-of-range — time < 0 or > 86399 explicitly rejected
|
|
16671
|
+
*
|
|
16672
|
+
* @param resVal - Output: Boolean result (true = success)
|
|
16673
|
+
* @param localTimeVal - Input: Local time as total seconds since midnight (Magic numeric)
|
|
16674
|
+
* @param localDateVal - Input: Local date as Magic numeric
|
|
16675
|
+
* @param utcTimeVar - Input: Field index for the UTC time output field
|
|
16676
|
+
* @param utcDateVar - Input: Field index for the UTC date output field
|
|
16677
|
+
*/
|
|
16678
|
+
async eval_op_localToUTC(resVal, localTimeVal, localDateVal, utcTimeVar, utcDateVar) {
|
|
16679
|
+
resVal.Attr = StorageAttribute.BOOLEAN;
|
|
16680
|
+
resVal.BoolVal = false;
|
|
16681
|
+
// Guard: all four numeric values must be present
|
|
16682
|
+
if (localTimeVal.MgNumVal === null || localDateVal.MgNumVal === null ||
|
|
16683
|
+
utcTimeVar.MgNumVal === null || utcDateVar.MgNumVal === null) {
|
|
16684
|
+
return;
|
|
16685
|
+
}
|
|
16686
|
+
// Fetch output fields
|
|
16687
|
+
const utcDateFld = this.GetFieldOfContextTask(utcDateVar.MgNumVal.NUM_2_LONG());
|
|
16688
|
+
const utcTimeFld = this.GetFieldOfContextTask(utcTimeVar.MgNumVal.NUM_2_LONG());
|
|
16689
|
+
if (utcTimeFld === null || utcDateFld === null)
|
|
16690
|
+
return;
|
|
16691
|
+
// Break the input date into parts
|
|
16692
|
+
const dateParts = new DateBreakParams();
|
|
16693
|
+
DisplayConvertor.Instance.date_break_datemode(dateParts, localDateVal.MgNumVal.NUM_2_LONG(), true, this.ExpTask.getCompIdx());
|
|
16694
|
+
// Reject zero/uninitialized Magic date parts
|
|
16695
|
+
if (dateParts.year === 0 || dateParts.month === 0 || dateParts.day === 0)
|
|
16696
|
+
return;
|
|
16697
|
+
// Guard: reject out-of-range time value
|
|
16698
|
+
const localTimeSec = localTimeVal.MgNumVal.NUM_2_LONG();
|
|
16699
|
+
if (!this.isValidTimeSec(localTimeSec))
|
|
16700
|
+
return;
|
|
16701
|
+
const { hours, minutes, seconds } = this.splitTimeSec(localTimeSec);
|
|
16702
|
+
// Use setFullYear to avoid JS bug where new Date(year < 100, ...) maps to 1900+year
|
|
16703
|
+
const localDateTime = new Date(0);
|
|
16704
|
+
localDateTime.setFullYear(dateParts.year, dateParts.month - 1, dateParts.day);
|
|
16705
|
+
localDateTime.setHours(hours, minutes, seconds, 0);
|
|
16706
|
+
// Guard: round-trip check catches silent rollovers (e.g. Feb 29 on non-leap year)
|
|
16707
|
+
if (localDateTime.getFullYear() !== dateParts.year ||
|
|
16708
|
+
localDateTime.getMonth() !== dateParts.month - 1 ||
|
|
16709
|
+
localDateTime.getDate() !== dateParts.day) {
|
|
16710
|
+
return;
|
|
16711
|
+
}
|
|
16712
|
+
if (isNaN(localDateTime.getTime()))
|
|
16713
|
+
return;
|
|
16714
|
+
// Extract UTC parts via Intl — DST, leap year, century year all handled automatically
|
|
16715
|
+
const utcParts = this.convertDateTimeParts(localDateTime, 'UTC');
|
|
16716
|
+
if (utcParts === null)
|
|
16717
|
+
return;
|
|
16718
|
+
const utcTime = utcParts.hour * 3600 + utcParts.minute * 60 + utcParts.second;
|
|
16719
|
+
const utcDate = DisplayConvertor.Instance.date_4_calender(utcParts.year, utcParts.month, utcParts.day, 1, false // month already 1-based
|
|
16720
|
+
);
|
|
16721
|
+
await this.writeDateTimeToFields(utcTimeFld, utcTime, utcDateFld, utcDate);
|
|
16722
|
+
resVal.BoolVal = true;
|
|
16723
|
+
}
|
|
16724
|
+
/**
|
|
16725
|
+
* Converts UTC date/time to local and writes the result into output fields.
|
|
16726
|
+
*
|
|
16727
|
+
* Edge cases handled:
|
|
16728
|
+
* - Century years — Date.UTC() does not have the year < 100 bug
|
|
16729
|
+
* - Leap years — round-trip validation catches silent date rollovers
|
|
16730
|
+
* - DST gaps — Intl.DateTimeFormat resolves to correct local time automatically
|
|
16731
|
+
* - DST ambiguity — Intl uses first occurrence consistently
|
|
16732
|
+
* - Cross-midnight — JS Date handles automatically
|
|
16733
|
+
* - Out-of-range — time < 0 or > 86399 explicitly rejected
|
|
16734
|
+
*
|
|
16735
|
+
* @param resVal - Output: Boolean result (true = success)
|
|
16736
|
+
* @param utcTimeVal - Input: UTC time as total seconds since midnight (Magic numeric)
|
|
16737
|
+
* @param utcDateVal - Input: UTC date as Magic numeric
|
|
16738
|
+
* @param localTimeVar - Input: Field index for the local time output field
|
|
16739
|
+
* @param localDateVar - Input: Field index for the local date output field
|
|
16740
|
+
*/
|
|
16741
|
+
async eval_op_utcToLocal(resVal, utcTimeVal, utcDateVal, localTimeVar, localDateVar) {
|
|
16742
|
+
resVal.Attr = StorageAttribute.BOOLEAN;
|
|
16743
|
+
resVal.BoolVal = false;
|
|
16744
|
+
// Guard: all four numeric values must be present
|
|
16745
|
+
if (utcTimeVal.MgNumVal === null || utcDateVal.MgNumVal === null ||
|
|
16746
|
+
localTimeVar.MgNumVal === null || localDateVar.MgNumVal === null) {
|
|
16747
|
+
return;
|
|
16748
|
+
}
|
|
16749
|
+
// Fetch output fields
|
|
16750
|
+
const localDateFld = this.GetFieldOfContextTask(localDateVar.MgNumVal.NUM_2_LONG());
|
|
16751
|
+
const localTimeFld = this.GetFieldOfContextTask(localTimeVar.MgNumVal.NUM_2_LONG());
|
|
16752
|
+
if (localTimeFld === null || localDateFld === null)
|
|
16753
|
+
return;
|
|
16754
|
+
// Break the input date into parts
|
|
16755
|
+
const dateParts = new DateBreakParams();
|
|
16756
|
+
DisplayConvertor.Instance.date_break_datemode(dateParts, utcDateVal.MgNumVal.NUM_2_LONG(), true, this.ExpTask.getCompIdx());
|
|
16757
|
+
// Reject zero/uninitialized Magic date parts
|
|
16758
|
+
if (dateParts.year === 0 || dateParts.month === 0 || dateParts.day === 0)
|
|
16759
|
+
return;
|
|
16760
|
+
// Guard: reject out-of-range time value
|
|
16761
|
+
const utcTimeSec = utcTimeVal.MgNumVal.NUM_2_LONG();
|
|
16762
|
+
if (!this.isValidTimeSec(utcTimeSec))
|
|
16763
|
+
return;
|
|
16764
|
+
const { hours, minutes, seconds } = this.splitTimeSec(utcTimeSec);
|
|
16765
|
+
// Why Date.UTC() instead of new Date(year, month, day, ...)?
|
|
16766
|
+
// JS has a bug: new Date(99, 0, 1) does not give year 99 — it gives 1999.
|
|
16767
|
+
// Date.UTC() always uses the exact year provided — safe for century years like 1900, 2000, 2100
|
|
16768
|
+
const utcDateTime = new Date(Date.UTC(dateParts.year, dateParts.month - 1, dateParts.day, hours, minutes, seconds, 0));
|
|
16769
|
+
// Guard: round-trip check catches silent rollovers (e.g. Feb 29 on non-leap year)
|
|
16770
|
+
if (utcDateTime.getUTCFullYear() !== dateParts.year ||
|
|
16771
|
+
utcDateTime.getUTCMonth() !== dateParts.month - 1 ||
|
|
16772
|
+
utcDateTime.getUTCDate() !== dateParts.day) {
|
|
16773
|
+
return;
|
|
16774
|
+
}
|
|
16775
|
+
if (isNaN(utcDateTime.getTime()))
|
|
16776
|
+
return;
|
|
16777
|
+
// Extract local datetime parts via Intl — no timeZone arg = browser's local timezone
|
|
16778
|
+
// DST, leap year, century year all handled automatically
|
|
16779
|
+
const localParts = this.convertDateTimeParts(utcDateTime);
|
|
16780
|
+
if (localParts === null)
|
|
16781
|
+
return;
|
|
16782
|
+
const localTime = localParts.hour * 3600 + localParts.minute * 60 + localParts.second;
|
|
16783
|
+
const localDate = DisplayConvertor.Instance.date_4_calender(localParts.year, localParts.month, localParts.day, 1, false // month already 1-based
|
|
16784
|
+
);
|
|
16785
|
+
await this.writeDateTimeToFields(localTimeFld, localTime, localDateFld, localDate);
|
|
16786
|
+
resVal.BoolVal = true;
|
|
16787
|
+
}
|
|
16559
16788
|
eval_op_ndow(resVal, val1, displayConvertor) {
|
|
16560
16789
|
if (val1.MgNumVal === null) {
|
|
16561
16790
|
super.SetNULL(resVal, StorageAttribute.NUMERIC);
|
|
@@ -39877,7 +40106,7 @@ class CommandsTable {
|
|
|
39877
40106
|
}
|
|
39878
40107
|
}
|
|
39879
40108
|
|
|
39880
|
-
let CurrentClientVersion = '4.1300.0-dev4130.
|
|
40109
|
+
let CurrentClientVersion = '4.1300.0-dev4130.186';
|
|
39881
40110
|
|
|
39882
40111
|
// @dynamic
|
|
39883
40112
|
class ClientManager {
|