@opengeoweb/taf 2.1.2 → 2.1.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.
package/taf.umd.js CHANGED
@@ -228,20 +228,20 @@
228
228
  * Copyright 2021 - Finnish Meteorological Institute (FMI)
229
229
  * */
230
230
  var FORMAT_DAYS_HOURS = 'DDHH';
231
- var FORMAT_DAYS_HOURS_MINUTS = 'DDHHmm'; // formatters
231
+ var FORMAT_DAYS_HOURS_MINUTES = 'DDHHmm'; // formatters
232
232
 
233
233
  var formatValidToString = function formatValidToString(valid) {
234
234
  // Ensure end of valid period if midnight is shown as 24 and not 00
235
235
  var endHours = moment__default["default"].utc(valid.end).format('kk');
236
236
  var endDate = endHours === '24' ? moment__default["default"].utc(valid.end).add(-1, 'day').format('DD') : moment__default["default"].utc(valid.end).format('DD');
237
- return valid.end ? moment__default["default"].utc(valid.start).format(FORMAT_DAYS_HOURS) + "/" + endDate + endHours : moment__default["default"].utc(valid.start).format(FORMAT_DAYS_HOURS_MINUTS);
237
+ return valid.end ? moment__default["default"].utc(valid.start).format(FORMAT_DAYS_HOURS) + "/" + endDate + endHours : moment__default["default"].utc(valid.start).format(FORMAT_DAYS_HOURS_MINUTES);
238
238
  };
239
239
 
240
240
  var getSections = function getSections(value, format) {
241
241
  return {
242
242
  day: parseInt(value.substring(0, 2), 10),
243
243
  hour: parseInt(value.substring(2, 4), 10),
244
- min: format === FORMAT_DAYS_HOURS_MINUTS ? parseInt(value.substring(4), 10) : 0
244
+ min: format === FORMAT_DAYS_HOURS_MINUTES ? parseInt(value.substring(4), 10) : 0
245
245
  };
246
246
  };
247
247
 
@@ -271,7 +271,7 @@
271
271
  var tafStartMoment = moment__default["default"].utc(tafStart);
272
272
  var tafEndMoment = moment__default["default"].utc(tafEnd);
273
273
  var startEnd = trimmedValue.split('/');
274
- var format = trimmedValue.indexOf('/') === -1 ? FORMAT_DAYS_HOURS_MINUTS : FORMAT_DAYS_HOURS;
274
+ var format = trimmedValue.indexOf('/') === -1 ? FORMAT_DAYS_HOURS_MINUTES : FORMAT_DAYS_HOURS;
275
275
  var startSections = getSections(startEnd[0], format);
276
276
  var start = getValidityTimeStamp('start', startSections, tafStartMoment, tafEndMoment).format('YYYY-MM-DDTHH:mm:ss[Z]'); // If we only have start value - return that otherwise, also get end time
277
277
 
@@ -296,7 +296,8 @@
296
296
  var invalidValidityPeriodMessage = 'Invalid validity period, validity end time has to be after validity start time';
297
297
  var invalidValidityEndTimeMessage = 'Invalid validity end time, change group validity must lie within the TAF validity period';
298
298
  var invalidChangeValidInterFieldMessage = 'Invalid validity period, expected DDHH/DDHH for BECMG and TEMPO or DDHHmm for FM';
299
- var invalidBECMGDurationValidInterFieldMessage = 'Invalid validity period, for BECMG only validity periods with a duration of 1, 2, 3 or 4 hours are allowed'; // Validations
299
+ var invalidBECMGDurationValidInterFieldMessage = 'Invalid validity period, for BECMG only validity periods with a duration of 1, 2, 3 or 4 hours are allowed';
300
+ var invalidChangeGroupOverlapsWithFM = 'The valid time of a change group cannot overlap with the valid time of a following FM group. This change group should end before or at the hour of the start of the FM group'; // Validations
300
301
  // Ensure all individual sections are correct (DD HH mm etc)
301
302
 
302
303
  var validateValidSections = function validateValidSections(sections, isStartEnd) {
@@ -320,7 +321,7 @@
320
321
  var tafStartMoment = moment__default["default"].utc(tafStart);
321
322
  var tafEndMoment = moment__default["default"].utc(tafEnd);
322
323
  var startEnd = value.split('/');
323
- var format = value.indexOf('/') === -1 ? FORMAT_DAYS_HOURS_MINUTS : FORMAT_DAYS_HOURS;
324
+ var format = value.indexOf('/') === -1 ? FORMAT_DAYS_HOURS_MINUTES : FORMAT_DAYS_HOURS;
324
325
  var validateMsg = []; // Validate start time
325
326
 
326
327
  var startSections = getSections(startEnd[0], format); // Validate individual sections for start
@@ -398,7 +399,7 @@
398
399
  var tafStartMoment = moment__default["default"].utc(tafStart);
399
400
  var tafEndMoment = moment__default["default"].utc(tafEnd);
400
401
  var startEnd = trimmedValue.split('/');
401
- var format = trimmedValue.indexOf('/') === -1 ? FORMAT_DAYS_HOURS_MINUTS : FORMAT_DAYS_HOURS; // Validate start time
402
+ var format = trimmedValue.indexOf('/') === -1 ? FORMAT_DAYS_HOURS_MINUTES : FORMAT_DAYS_HOURS; // Validate start time
402
403
 
403
404
  var startSections = getSections(startEnd[0], format);
404
405
  var startTimestamp = getValidityTimeStamp('start', startSections, tafStartMoment, tafEndMoment);
@@ -418,17 +419,17 @@
418
419
  var invalidPreviousChangeGroupIsBeforeMessage = 'Valid time should be after previous valid time';
419
420
  var invalidPreviousChangeGroupOrderMessage = "When valid times are equal, the change/probability order should be: " + validProbChangeOrder.join(', ');
420
421
  var invalidPreviousChangeGroupHasSameValue = "When valid times are equal, probability/change can't have the same value as previous row value";
421
- var validatePreviousChangeGroup = function validatePreviousChangeGroup(currentChangeGroup, nextChangeGroup, tafStart, tafEnd) {
422
+ var validatePreviousChangeGroup = function validatePreviousChangeGroup(currentChangeGroup, previousChangeGroup, tafStart, tafEnd) {
422
423
  // extract the needed values from changeGroups
423
424
  var valid = currentChangeGroup.valid,
424
425
  _a = currentChangeGroup.change,
425
426
  change = _a === void 0 ? '' : _a,
426
427
  _b = currentChangeGroup.probability,
427
428
  probability = _b === void 0 ? '' : _b;
428
- var previousValid = nextChangeGroup.valid,
429
- _c = nextChangeGroup.change,
429
+ var previousValid = previousChangeGroup.valid,
430
+ _c = previousChangeGroup.change,
430
431
  previousChange = _c === void 0 ? '' : _c,
431
- _d = nextChangeGroup.probability,
432
+ _d = previousChangeGroup.probability,
432
433
  previousProbability = _d === void 0 ? '' : _d; // get the parsed values
433
434
 
434
435
  var validValue = parseValidToObject(valid, tafStart, tafEnd);
@@ -462,6 +463,27 @@
462
463
  }
463
464
  }
464
465
 
466
+ return true;
467
+ }; // validate overlapping validity when one of the following changegroups is FM
468
+
469
+ var validateOverlapWithFM = function validateOverlapWithFM(currentChangeGroup, nextChangeGroups, tafStart, tafEnd) {
470
+ var valid = currentChangeGroup.valid;
471
+ if (!valid || !nextChangeGroups.length) return true;
472
+ var validValue = parseValidToObject(valid, tafStart, tafEnd);
473
+ var currentRowEndTime = moment__default["default"](validValue.end);
474
+ var firstFMChangeGroup = nextChangeGroups.find(function (nextChangeGroup) {
475
+ return nextChangeGroup.change.trim() === 'FM';
476
+ });
477
+
478
+ if (firstFMChangeGroup && firstFMChangeGroup.valid) {
479
+ var FMValidValue = parseValidToObject(firstFMChangeGroup.valid, tafStart, tafEnd);
480
+ var FMStartTime = moment__default["default"](FMValidValue.start);
481
+
482
+ if (currentRowEndTime.isAfter(FMStartTime)) {
483
+ return invalidChangeGroupOverlapsWithFM;
484
+ }
485
+ }
486
+
465
487
  return true;
466
488
  };
467
489
 
@@ -1399,7 +1421,8 @@
1399
1421
  var invalidWindGustSpeedBelow15Message = 'Invalid wind gusts, gusts are only allowed for a minimum wind speed of 15 knots';
1400
1422
  var invalidGustForWindP99Message = 'Invalid wind speed and gust combination, if wind is P99, gusts can only be empty or P99';
1401
1423
  var invalidWindSpeedForDirectionZeroMessage = 'Invalid wind speed, if direction is 000, speed can only be 00';
1402
- var invalidWindSpeedForDirectionNonZeroMessage = 'Invalid wind speed, if direction is not 000, speed can not be 00'; // validations
1424
+ var invalidWindSpeedForDirectionNonZeroMessage = 'Invalid wind speed, if direction is not 000, speed can not be 00';
1425
+ var invalidWindVRBWindDirectionMessage = 'Invalid wind direction, VRB can only be used in combination with windspeeds smaller than 4 or in combination with gusts and showers'; // validations
1403
1426
 
1404
1427
  var validateDirection = function validateDirection(value) {
1405
1428
  // if not a number
@@ -1447,7 +1470,7 @@
1447
1470
  return value === 'KT' || value === '' || invalidWindMessage;
1448
1471
  };
1449
1472
 
1450
- var validateWindInterSection = function validateWindInterSection(speed, gust, direction) {
1473
+ var validateWindInterSection = function validateWindInterSection(speed, gust, direction, weather1Value, weather2Value, weather3Value) {
1451
1474
  if (direction === '000' && speed !== '00') {
1452
1475
  return invalidWindSpeedForDirectionZeroMessage;
1453
1476
  }
@@ -1456,6 +1479,19 @@
1456
1479
  return invalidWindSpeedForDirectionNonZeroMessage;
1457
1480
  }
1458
1481
 
1482
+ if (direction === 'VRB' && (speed === 'P99' || parseInt(speed, 10) > 3)) {
1483
+ // Only allow VRB for speeds smaller than 4 or in combination with gusts and showers (SH, TS, SQ)
1484
+ var allowedWeatherPhenom = ['SH', 'TS', 'SQ'];
1485
+ if (gust === '' || !allowedWeatherPhenom.some(function (allowedPhenom) {
1486
+ return weather1Value.includes(allowedPhenom);
1487
+ }) && !allowedWeatherPhenom.some(function (allowedPhenom) {
1488
+ return weather2Value.includes(allowedPhenom);
1489
+ }) && !allowedWeatherPhenom.some(function (allowedPhenom) {
1490
+ return weather3Value.includes(allowedPhenom);
1491
+ })) return invalidWindVRBWindDirectionMessage;
1492
+ } // Validate gust
1493
+
1494
+
1459
1495
  if (gust === '') {
1460
1496
  return true;
1461
1497
  }
@@ -1476,20 +1512,23 @@
1476
1512
  }; // Main validation function
1477
1513
 
1478
1514
 
1479
- var validateWindField = function validateWindField(value) {
1480
- var trimmedValue = value.trim();
1515
+ var validateWindField = function validateWindField(value, weatherfield1, weatherfield2, weatherfield3) {
1516
+ var trimmedWindValue = value.trim();
1517
+ var trimmedWeather1Value = weatherfield1.trim();
1518
+ var trimmedWeather2Value = weatherfield2.trim();
1519
+ var trimmedWeather3Value = weatherfield3.trim();
1481
1520
 
1482
- if (!trimmedValue.length) {
1521
+ if (!trimmedWindValue.length) {
1483
1522
  return true;
1484
1523
  } // Validate length and ensure it doesn't contain any other characters than numbers and VRB P KT and G
1485
1524
 
1486
1525
 
1487
- if (trimmedValue.length < 5 || trimmedValue.length > 12 || !trimmedValue.match(/^[0-9VRBPGKT]+$/)) {
1526
+ if (trimmedWindValue.length < 5 || trimmedWindValue.length > 12 || !trimmedWindValue.match(/^[0-9VRBPGKT]+$/)) {
1488
1527
  return invalidWindMessage;
1489
1528
  } // First validate individual sections
1490
1529
 
1491
1530
 
1492
- var windValidationIndv = extractValidateWindSections(trimmedValue, true);
1531
+ var windValidationIndv = extractValidateWindSections(trimmedWindValue, true);
1493
1532
 
1494
1533
  if (windValidationIndv.validations.length !== 0) {
1495
1534
  return windValidationIndv.validations.find(function (error) {
@@ -1498,7 +1537,7 @@
1498
1537
  } // Then do any intersection validation
1499
1538
 
1500
1539
 
1501
- return validateWindInterSection(windValidationIndv.speed, windValidationIndv.gust, windValidationIndv.direction);
1540
+ return validateWindInterSection(windValidationIndv.speed, windValidationIndv.gust, windValidationIndv.direction, trimmedWeather1Value, trimmedWeather2Value, trimmedWeather3Value);
1502
1541
  };
1503
1542
 
1504
1543
  var formatSingleCloud = function formatSingleCloud(value) {
@@ -1547,10 +1586,10 @@
1547
1586
  var parseSingleCloud = function parseSingleCloud(value) {
1548
1587
  if (!value) return null;
1549
1588
  var trimmedCloud = value.trim();
1550
- var cloudType = trimmedCloud.substr(6, 3);
1589
+ var cloudType = trimmedCloud.substring(6, 9);
1551
1590
  return __assign({
1552
- coverage: trimmedCloud.substr(0, 3),
1553
- height: parseInt(trimmedCloud.substr(3, 3), 10)
1591
+ coverage: trimmedCloud.substring(0, 3),
1592
+ height: parseInt(trimmedCloud.substring(3, 6), 10)
1554
1593
  }, cloudType && {
1555
1594
  type: cloudType
1556
1595
  });
@@ -1568,20 +1607,22 @@
1568
1607
  switch (key) {
1569
1608
  case 'cloud1':
1570
1609
  {
1571
- if (isVerticalVisibility(clouds[key])) {
1610
+ var trimmedValue = clouds[key].trim();
1611
+
1612
+ if (isVerticalVisibility(trimmedValue)) {
1572
1613
  // Parse Vertical Visibility
1573
1614
  return __assign(__assign({}, list), (_a = {}, _a[key] = {
1574
- verticalVisibility: parseInt(clouds[key].substr(2, 3), 10)
1615
+ verticalVisibility: parseInt(trimmedValue.substring(2, 5), 10)
1575
1616
  }, _a));
1576
1617
  }
1577
1618
 
1578
- if (clouds[key] === 'NSC') {
1619
+ if (trimmedValue === 'NSC') {
1579
1620
  // Parse No Significant Clouds
1580
- return __assign(__assign({}, list), (_b = {}, _b[key] = clouds[key], _b));
1621
+ return __assign(__assign({}, list), (_b = {}, _b[key] = trimmedValue, _b));
1581
1622
  } // Parse Cloud
1582
1623
 
1583
1624
 
1584
- return __assign(__assign({}, list), (_c = {}, _c[key] = parseSingleCloud(clouds[key]), _c));
1625
+ return __assign(__assign({}, list), (_c = {}, _c[key] = parseSingleCloud(trimmedValue), _c));
1585
1626
  }
1586
1627
 
1587
1628
  default:
@@ -1609,16 +1650,19 @@
1609
1650
  var invalidCloudHeightStepsMessage = 'Invalid cloud height, a height above 050 must be rounded to the nearest 1000ft';
1610
1651
  var invalidCLoudTypeMessage = 'Invalid cloud type, expected CB or TCU';
1611
1652
  var invalidHeightTypeCombinationMessage = 'Invalid cloud input, when cloud height is 050 or higher, CB or TCU is required';
1612
- var invalidVerticalVisibilityMessage = 'Invalid vertical visibility, expected VV<value> with value consisting of 3 digits between 000 and 010';
1653
+ var invalidVerticalVisibilityMessage = 'Invalid vertical visibility, expected VV<value> with value consisting of 3 digits';
1654
+ var invalidVerticalVisibilityHeightMessage = 'Invalid vertical visibility, visibility should lie between 001 and 010';
1655
+ var invalidVerticalVisibilityHeightFGMessage = 'Invalid vertical visibility, in case of fog visibility should lie between 001 and 005';
1613
1656
  var invalidCloudHeightComparedToPreviousMessage = 'Invalid cloud height, height must be greater than previous cloud group height';
1614
- var invalidCloud2CoverageTypeCombinationMessage = 'Invalid cloud input, cloud type is required when coverage is FEW';
1657
+ var invalidCloud2CoverageTypeCombinationMessage = 'Invalid cloud input, for the second cloud group (excluding any significant convective cloud groups with CB/TCU) a coverage of FEW is not allowed';
1615
1658
  var invalidCloud2WithVerticalVisibilityMessage = 'Invalid cloud input, cloud type is required when Vertical Visibility is given';
1616
- var invalidCloud3CoverageTypeCombinationMessage = 'Invalid cloud input, cloud type is required when coverage is FEW or SCT';
1659
+ var invalidCloud3CoverageTypeCombinationMessage = 'Invalid cloud input, for the third cloud group (excluding any significant convective cloud groups with CB/TCU) a coverage of FEW or SCT is not allowed';
1617
1660
  var invalidCloudWithVerticalVisibilityMessage = 'Invalid cloud input, third or fourth cloud group is not allowed when Vertical Visibility is given';
1618
1661
  var invalidCloudWithPreviousEmptyCloud = 'Invalid cloud input, value is not allowed when previous cloud group is empty';
1619
1662
  var invalidCloudWithNSCMessage = 'Invalid cloud input, other cloud groups are not allowed when No Significant Clouds is given';
1620
- var invalidCloud4WithPreviousType = 'Invalid cloud input, value is not allowed when previous cloud group has a type';
1621
- var invalidCloudCAVOKMessage = 'Invalid cloud input, when CAVOK is used, cloud groups must be empty'; // validations
1663
+ var invalidCloudWithPreviousType = 'Invalid cloud input, only one significant convective cloud group with CB/TCU is allowed';
1664
+ var invalidCloudCAVOKMessage = 'Invalid cloud input, when CAVOK is used, cloud groups must be empty';
1665
+ var invalidCloud4TypeMissingMessage = 'Invalid cloud input for fourth group, CB or TCU required if no other significant convective cloud group present'; // validations
1622
1666
 
1623
1667
  var isLengthValid = function isLengthValid(value) {
1624
1668
  var onlyDigitsOrSpecificLetters = RegExp(/^[0-9FEWSCTBKNOVU]+$/);
@@ -1665,25 +1709,32 @@
1665
1709
  };
1666
1710
 
1667
1711
  var isVerticalVisibility = function isVerticalVisibility(value) {
1668
- return value.substr(0, 2) === 'VV';
1712
+ return value.substring(0, 2) === 'VV';
1669
1713
  };
1670
1714
 
1671
- var isVerticalVisibilityValid = function isVerticalVisibilityValid(value) {
1715
+ var isVerticalVisibilityValid = function isVerticalVisibilityValid(value, weather1, weather2, weather3) {
1672
1716
  // validate if its a number
1673
1717
  if (!value.match(/^[0-9]+$/)) {
1674
- return false;
1718
+ return invalidVerticalVisibilityMessage;
1675
1719
  } // check if its 3 digits
1676
1720
 
1677
1721
 
1678
1722
  if (value.length !== 3) {
1679
- return false;
1680
- } // value should not be above 10
1681
-
1723
+ return invalidVerticalVisibilityMessage;
1724
+ }
1682
1725
 
1683
1726
  var intValue = parseInt(value, 10);
1684
1727
 
1685
- if (intValue > 10) {
1686
- return false;
1728
+ if (weather1 !== 'FG' && weather2 !== 'FG' && weather3 !== 'FG') {
1729
+ // value should not be above 10
1730
+ if (intValue < 1 || intValue > 10) {
1731
+ return invalidVerticalVisibilityHeightMessage;
1732
+ }
1733
+ } else if (intValue < 1 || intValue > 5) {
1734
+ // Apply Dutch rule as national exception
1735
+ // In case of fog (FG) the vertical visibility can only be between VV001 up to and including VV005
1736
+ // HEIGHT_VV is: (001-(1)-005). Deze maxima zijn officiële Nederlandse afwijkingen van de WMO/ICAO regelgeving
1737
+ return invalidVerticalVisibilityHeightFGMessage;
1687
1738
  }
1688
1739
 
1689
1740
  return true;
@@ -1700,14 +1751,14 @@
1700
1751
  } // Validate coverage
1701
1752
 
1702
1753
 
1703
- var coverage = value.substr(0, 3);
1754
+ var coverage = value.substring(0, 3);
1704
1755
 
1705
1756
  if (!isCoverageValid(coverage)) {
1706
1757
  errorMessages.push(invalidCloudCoverageMessage);
1707
1758
  } // Validate height
1708
1759
 
1709
1760
 
1710
- var height = value.substr(3, 3);
1761
+ var height = value.substring(3, 6);
1711
1762
  var isValidHeight = isHeightValid(height);
1712
1763
 
1713
1764
  if (isValidHeight !== true) {
@@ -1716,7 +1767,7 @@
1716
1767
  } // Validate type
1717
1768
 
1718
1769
 
1719
- var type = value.substr(6, 3);
1770
+ var type = value.substring(6, 9);
1720
1771
 
1721
1772
  if (!isTypeValid(type)) {
1722
1773
  errorMessages.push(invalidCLoudTypeMessage);
@@ -1735,56 +1786,66 @@
1735
1786
  };
1736
1787
  };
1737
1788
 
1738
- var validateFirstCloud = function validateFirstCloud(value, visibilityValue) {
1739
- var trimmedValue = value.trim(); // Field can be left empty
1789
+ var validateFirstCloud = function validateFirstCloud(value, visibilityValue, weatherfield1, weatherfield2, weatherfield3) {
1790
+ var trimmedValueFirstCloud = value.trim();
1791
+ var trimmedValueVisibility = visibilityValue.trim();
1792
+ var trimmedWeather1Value = weatherfield1.trim();
1793
+ var trimmedWeather2Value = weatherfield2.trim();
1794
+ var trimmedWeather3Value = weatherfield3.trim(); // Field can be left empty
1740
1795
 
1741
- if (!trimmedValue.length) {
1796
+ if (!trimmedValueFirstCloud.length) {
1742
1797
  return true;
1743
1798
  }
1744
1799
 
1745
- if (trimmedValue && visibilityValue === 'CAVOK') {
1800
+ if (trimmedValueFirstCloud && trimmedValueVisibility === 'CAVOK') {
1746
1801
  return invalidCloudCAVOKMessage;
1747
1802
  } // Check for No Signicant Clouds
1748
1803
 
1749
1804
 
1750
- if (trimmedValue === 'NSC') {
1805
+ if (trimmedValueFirstCloud === 'NSC') {
1751
1806
  return true;
1752
1807
  } // Validate Vertical Visibility
1753
1808
 
1754
1809
 
1755
- if (isVerticalVisibility(trimmedValue)) {
1810
+ if (isVerticalVisibility(trimmedValueFirstCloud)) {
1756
1811
  if ( // Validate length of 5
1757
- trimmedValue.length !== 5 || // Ensure it only contains digits or VV
1758
- !trimmedValue.match(/^[0-9V]+$/)) {
1812
+ trimmedValueFirstCloud.length !== 5 || // Ensure it only contains digits or VV
1813
+ !trimmedValueFirstCloud.match(/^[0-9V]+$/)) {
1759
1814
  return invalidVerticalVisibilityMessage;
1760
1815
  }
1761
1816
 
1762
- var verticalVisibilityValue = trimmedValue.substr(2, 3);
1763
-
1764
- if (!isVerticalVisibilityValid(verticalVisibilityValue)) {
1765
- return invalidVerticalVisibilityMessage;
1766
- }
1767
-
1768
- return true;
1817
+ var verticalVisibilityValue = trimmedValueFirstCloud.substring(2, 5);
1818
+ return isVerticalVisibilityValid(verticalVisibilityValue, trimmedWeather1Value, trimmedWeather2Value, trimmedWeather3Value);
1769
1819
  }
1770
1820
 
1771
1821
  var errorMessages = sharedCloudValidations({
1772
- value: trimmedValue,
1822
+ value: trimmedValueFirstCloud,
1773
1823
  canBeNSC: true
1774
1824
  }).errorMessages; // Return first error if any
1775
1825
 
1776
1826
  return errorMessages.length ? errorMessages[0] : true;
1777
1827
  };
1778
1828
  var validateSecondCloud = function validateSecondCloud(firstCloudValue, secondCloudValue, visibilityValue) {
1779
- var trimmedValue = secondCloudValue.trim(); // Field can be left empty
1829
+ var trimmedValueFirstCloud = firstCloudValue.trim();
1830
+ var trimmedValueSecondCloud = secondCloudValue.trim(); // Field can be left empty
1780
1831
 
1781
- if (!trimmedValue.length) {
1832
+ if (!trimmedValueSecondCloud.length) {
1782
1833
  return true;
1834
+ } // If Cloud1 is NSC, Cloud2 has to be empty
1835
+
1836
+
1837
+ if (trimmedValueFirstCloud === 'NSC' && trimmedValueSecondCloud !== '') {
1838
+ return invalidCloudWithNSCMessage;
1839
+ } // If Cloud1 is empty Cloud2 should be empty
1840
+
1841
+
1842
+ if (trimmedValueFirstCloud === '') {
1843
+ return invalidCloudWithPreviousEmptyCloud;
1783
1844
  } // Generic validations
1784
1845
 
1785
1846
 
1786
1847
  var _a = sharedCloudValidations({
1787
- value: trimmedValue
1848
+ value: trimmedValueSecondCloud
1788
1849
  }),
1789
1850
  coverage = _a.coverage,
1790
1851
  height = _a.height,
@@ -1794,32 +1855,32 @@
1794
1855
 
1795
1856
  if (errorMessages.length) {
1796
1857
  return errorMessages[0];
1797
- } // Cloud2 specific validations
1798
- // Without type, coverage can not be FEW
1858
+ } // Cloud2 specific validations - only to be applied when cloud1 does not contain type CB/TCU
1799
1859
 
1800
1860
 
1801
- if (type === '' && coverage === 'FEW') {
1802
- return invalidCloud2CoverageTypeCombinationMessage;
1861
+ var typeCloud1 = trimmedValueFirstCloud.substring(6, 9);
1862
+
1863
+ if (typeCloud1 !== 'CB' && typeCloud1 !== 'TCU') {
1864
+ // Without a type specified in cloud group 1, this group counts as #2 and coverage can not be FEW
1865
+ if (type === '' && coverage === 'FEW') {
1866
+ return invalidCloud2CoverageTypeCombinationMessage;
1867
+ }
1803
1868
  } // If Cloud1 is not valid, skip further validation of Cloud2
1869
+ // No need to pass the weatherfields as their content does not influence what happens below
1804
1870
 
1805
1871
 
1806
- if (validateFirstCloud(firstCloudValue, visibilityValue) !== true) {
1872
+ if (validateFirstCloud(trimmedValueFirstCloud, visibilityValue, '', '', '') !== true) {
1807
1873
  return true;
1808
- } // If Cloud1 is NSC, Cloud2 has to be empty
1809
-
1810
-
1811
- if (firstCloudValue === 'NSC' && trimmedValue !== '') {
1812
- return invalidCloudWithNSCMessage;
1813
- } // If Cloud1 is empty Cloud2 should be empty
1874
+ } // If Cloud1 contains a type, Cloud2 cannot contain a type
1814
1875
 
1815
1876
 
1816
- if (firstCloudValue === '') {
1817
- return invalidCloudWithPreviousEmptyCloud;
1877
+ if (type !== '' && typeCloud1 !== '') {
1878
+ return invalidCloudWithPreviousType;
1818
1879
  } // Cloud2 height has to be greater than Cloud1 height
1819
1880
 
1820
1881
 
1821
- if (!isVerticalVisibility(firstCloudValue)) {
1822
- var cloud1Height = firstCloudValue.substr(3, 3);
1882
+ if (!isVerticalVisibility(trimmedValueFirstCloud)) {
1883
+ var cloud1Height = trimmedValueFirstCloud.substring(3, 6);
1823
1884
 
1824
1885
  if (height <= cloud1Height) {
1825
1886
  return invalidCloudHeightComparedToPreviousMessage;
@@ -1827,22 +1888,34 @@
1827
1888
  } // If Cloud1 contains Vertical Visibility then Cloud2 type is required
1828
1889
 
1829
1890
 
1830
- if (isVerticalVisibility(firstCloudValue) && type === '') {
1891
+ if (isVerticalVisibility(trimmedValueFirstCloud) && type === '') {
1831
1892
  return invalidCloud2WithVerticalVisibilityMessage;
1832
1893
  }
1833
1894
 
1834
1895
  return true;
1835
1896
  };
1836
1897
  var validateThirdCloud = function validateThirdCloud(firstCloudValue, secondCloudValue, thirdCloudValue, visibilityValue) {
1837
- var trimmedValue = thirdCloudValue.trim(); // Field can be left empty
1898
+ var trimmedValueFirstCloud = firstCloudValue.trim();
1899
+ var trimmedValueSecondCloud = secondCloudValue.trim();
1900
+ var trimmedValueThirdCloud = thirdCloudValue.trim(); // Field can be left empty
1838
1901
 
1839
- if (!trimmedValue.length) {
1902
+ if (!trimmedValueThirdCloud.length) {
1840
1903
  return true;
1904
+ } // If Cloud1 is NSC, Cloud3 has to be empty
1905
+
1906
+
1907
+ if (trimmedValueFirstCloud === 'NSC' && trimmedValueThirdCloud !== '') {
1908
+ return invalidCloudWithNSCMessage;
1909
+ } // If Cloud1 or Cloud2 is empty Cloud3 should be empty
1910
+
1911
+
1912
+ if (trimmedValueFirstCloud === '' || trimmedValueSecondCloud === '') {
1913
+ return invalidCloudWithPreviousEmptyCloud;
1841
1914
  } // Generic validations
1842
1915
 
1843
1916
 
1844
1917
  var _a = sharedCloudValidations({
1845
- value: trimmedValue
1918
+ value: trimmedValueThirdCloud
1846
1919
  }),
1847
1920
  coverage = _a.coverage,
1848
1921
  height = _a.height,
@@ -1853,45 +1926,45 @@
1853
1926
  if (errorMessages.length) {
1854
1927
  return errorMessages[0];
1855
1928
  } // Cloud3 specific validations
1856
- // Without type, coverage can not be FEW or SCT
1929
+ // Without a type specified in cloud group 1 or 2, this group counts as #3 and coverage can not be FEW or SCT
1857
1930
 
1858
1931
 
1859
- if (type === '' && (coverage === 'FEW' || coverage === 'SCT')) {
1860
- return invalidCloud3CoverageTypeCombinationMessage;
1932
+ var typeCloud1 = trimmedValueFirstCloud.substring(6, 9);
1933
+ var typeCloud2 = trimmedValueSecondCloud.substring(6, 9);
1934
+
1935
+ if (typeCloud1 !== 'CB' && typeCloud1 !== 'TCU' && typeCloud2 !== 'CB' && typeCloud2 !== 'TCU') {
1936
+ if (type === '' && (coverage === 'FEW' || coverage === 'SCT')) {
1937
+ return invalidCloud3CoverageTypeCombinationMessage;
1938
+ }
1939
+ } else if (type === '' && coverage === 'FEW') {
1940
+ // In case we do have a CB/TCU group in one of the previous clouf groups. This cloudgroup should be evaluated as if it was the second group
1941
+ return invalidCloud2CoverageTypeCombinationMessage;
1861
1942
  } // If Cloud1 is not valid, skip further validation of Cloud3
1943
+ // No need to pass the weatherfields as their content does not influence what happens below
1944
+ // Since in case of VV the third field should be empty anyways
1862
1945
 
1863
1946
 
1864
- if (validateFirstCloud(firstCloudValue, visibilityValue) !== true) {
1947
+ if (validateFirstCloud(trimmedValueFirstCloud, visibilityValue, '', '', '') !== true) {
1865
1948
  return true;
1866
- } // If Cloud1 is empty Cloud3 should be empty
1867
-
1868
-
1869
- if (firstCloudValue === '') {
1870
- return invalidCloudWithPreviousEmptyCloud;
1871
1949
  } // If Cloud1 contains Vertical Visibility then Cloud3 should be empty
1872
1950
 
1873
1951
 
1874
- if (isVerticalVisibility(firstCloudValue)) {
1952
+ if (isVerticalVisibility(trimmedValueFirstCloud)) {
1875
1953
  return invalidCloudWithVerticalVisibilityMessage;
1876
- } // If Cloud1 is NSC, Cloud3 has to be empty
1877
-
1878
-
1879
- if (firstCloudValue === 'NSC' && trimmedValue !== '') {
1880
- return invalidCloudWithNSCMessage;
1881
1954
  } // If Cloud2 is not valid, skip further validation of Cloud3
1882
1955
 
1883
1956
 
1884
- if (validateSecondCloud(firstCloudValue, secondCloudValue, visibilityValue) !== true) {
1957
+ if (validateSecondCloud(trimmedValueFirstCloud, trimmedValueSecondCloud, visibilityValue) !== true) {
1885
1958
  return true;
1886
- } // If Cloud2 is empty Cloud3 should be empty
1959
+ } // If one of the previous groups contains a type, Cloud3 cannot contain a type
1887
1960
 
1888
1961
 
1889
- if (secondCloudValue === '') {
1890
- return invalidCloudWithPreviousEmptyCloud;
1962
+ if (type !== '' && (typeCloud1 !== '' || typeCloud2 !== '')) {
1963
+ return invalidCloudWithPreviousType;
1891
1964
  } // Cloud3 height has to be greater than Cloud2 height
1892
1965
 
1893
1966
 
1894
- var cloud2Height = secondCloudValue.substr(3, 3);
1967
+ var cloud2Height = trimmedValueSecondCloud.substring(3, 6);
1895
1968
 
1896
1969
  if (height <= cloud2Height) {
1897
1970
  return invalidCloudHeightComparedToPreviousMessage;
@@ -1900,17 +1973,31 @@
1900
1973
  return true;
1901
1974
  };
1902
1975
  var validateFourthCloud = function validateFourthCloud(firstCloudValue, secondCloudValue, thirdCloudValue, fourthCloudValue, visibilityValue) {
1903
- var trimmedValue = fourthCloudValue.trim(); // Field can be left empty
1976
+ var trimmedValueFirstCloud = firstCloudValue.trim();
1977
+ var trimmedValueSecondCloud = secondCloudValue.trim();
1978
+ var trimmedValueThirdCloud = thirdCloudValue.trim();
1979
+ var trimmedValueFourthCloud = fourthCloudValue.trim(); // Field can be left empty
1904
1980
 
1905
- if (!trimmedValue.length) {
1981
+ if (!trimmedValueFourthCloud.length) {
1906
1982
  return true;
1983
+ } // If Cloud1 is NSC, Cloud4 has to be empty
1984
+
1985
+
1986
+ if (trimmedValueFirstCloud === 'NSC' && trimmedValueFourthCloud !== '') {
1987
+ return invalidCloudWithNSCMessage;
1988
+ } // If Cloud 1, 2 or 3 is empty Cloud4 should be empty
1989
+
1990
+
1991
+ if (trimmedValueFirstCloud === '' || trimmedValueSecondCloud === '' || trimmedValueThirdCloud === '') {
1992
+ return invalidCloudWithPreviousEmptyCloud;
1907
1993
  } // Generic validations
1908
1994
 
1909
1995
 
1910
1996
  var _a = sharedCloudValidations({
1911
- value: trimmedValue
1997
+ value: trimmedValueFourthCloud
1912
1998
  }),
1913
1999
  height = _a.height,
2000
+ coverage = _a.coverage,
1914
2001
  type = _a.type,
1915
2002
  errorMessages = _a.errorMessages; // Return generic errors if any
1916
2003
 
@@ -1918,50 +2005,48 @@
1918
2005
  if (errorMessages.length) {
1919
2006
  return errorMessages[0];
1920
2007
  } // Cloud4 specific validations
1921
- // Type is required
2008
+ // If none of the previous cloud groups contains a type, the fourth group must contain a type if not empty
1922
2009
 
1923
2010
 
1924
- if (type === '') {
1925
- return invalidCLoudTypeMessage;
2011
+ var typeCloud1 = trimmedValueFirstCloud.substring(6, 9);
2012
+ var typeCloud2 = trimmedValueSecondCloud.substring(6, 9);
2013
+ var typeCloud3 = trimmedValueThirdCloud.substring(6, 9);
2014
+
2015
+ if (typeCloud1 !== 'CB' && typeCloud1 !== 'TCU' && typeCloud2 !== 'CB' && typeCloud2 !== 'TCU' && typeCloud3 !== 'CB' && typeCloud3 !== 'TCU') {
2016
+ if (type === '') {
2017
+ return invalidCloud4TypeMissingMessage;
2018
+ }
2019
+ } else if (coverage === 'FEW' || coverage === 'SCT') {
2020
+ // We have a CB/TCU group in one of the previous cloud groups. This group should be evaluated as if it was the third group
2021
+ return invalidCloud3CoverageTypeCombinationMessage;
1926
2022
  } // If Cloud1 is not valid, skip further validation of Cloud4
2023
+ // No need to pass the weatherfields as their content does not influence what happens below
2024
+ // Since in case of VV the fourth field should be empty anyways
1927
2025
 
1928
2026
 
1929
- if (validateFirstCloud(firstCloudValue, visibilityValue) !== true) {
2027
+ if (validateFirstCloud(trimmedValueFirstCloud, visibilityValue, '', '', '') !== true) {
1930
2028
  return true;
1931
2029
  } // If Cloud1 contains Vertical Visibility then Cloud4 should be empty
1932
2030
 
1933
2031
 
1934
- if (isVerticalVisibility(firstCloudValue)) {
2032
+ if (isVerticalVisibility(trimmedValueFirstCloud)) {
1935
2033
  return invalidCloudWithVerticalVisibilityMessage;
1936
- } // If Cloud1 is NSC, Cloud4 has to be empty
1937
-
1938
-
1939
- if (firstCloudValue === 'NSC' && trimmedValue !== '') {
1940
- return invalidCloudWithNSCMessage;
1941
- } // If Cloud 1, 2 or 3 is empty Cloud4 should be empty
1942
-
1943
-
1944
- if (firstCloudValue === '' || secondCloudValue === '' || thirdCloudValue === '') {
1945
- return invalidCloudWithPreviousEmptyCloud;
1946
2034
  } // If Cloud3 is not valid, skip further validation of Cloud4
1947
2035
 
1948
2036
 
1949
- if (validateThirdCloud(firstCloudValue, secondCloudValue, thirdCloudValue, visibilityValue) !== true) {
2037
+ if (validateThirdCloud(trimmedValueFirstCloud, trimmedValueSecondCloud, trimmedValueThirdCloud, visibilityValue) !== true) {
1950
2038
  return true;
1951
- } // If Cloud3 contains a type, Cloud4 should be empty
1952
-
2039
+ } // If one of the previous groups contains a type, Cloud4 cannot contain a type
1953
2040
 
1954
- var cloud3Type = thirdCloudValue.substr(6, 3);
1955
2041
 
1956
- if (cloud3Type !== '') {
1957
- return invalidCloud4WithPreviousType;
2042
+ if (type !== '' && (typeCloud1 !== '' || typeCloud2 !== '' || typeCloud3 !== '')) {
2043
+ return invalidCloudWithPreviousType;
1958
2044
  } // Cloud4 height has to be greater than Cloud3 height
1959
2045
 
1960
2046
 
1961
- var cloud3Height = thirdCloudValue.substr(3, 3);
2047
+ var cloud3Height = trimmedValueThirdCloud.substring(3, 6);
1962
2048
 
1963
2049
  if (height <= cloud3Height) {
1964
- // Because of the type validations this is automatically enforced for Cloud4, so impossible to end up here but leaving the check in just in case
1965
2050
  return invalidCloudHeightComparedToPreviousMessage;
1966
2051
  }
1967
2052
 
@@ -2806,14 +2891,38 @@
2806
2891
  return true;
2807
2892
  }, [index, isChangeGroup, getValues]);
2808
2893
 
2809
- var getFieldFromNextRow = function getFieldFromNextRow(fieldname) {
2810
- if (fieldname === void 0) {
2811
- fieldname = 'valid';
2894
+ var getFieldFromRow = function getFieldFromRow(fieldname, rowIndex) {
2895
+ var indexedFieldName = "changeGroups[" + rowIndex + "]." + fieldname;
2896
+ var rowValue = getValues(indexedFieldName);
2897
+ return rowValue ? [indexedFieldName] : [];
2898
+ };
2899
+
2900
+ var getValidFieldFromNextRow = function getValidFieldFromNextRow() {
2901
+ return getFieldFromRow('valid', index + 1);
2902
+ };
2903
+
2904
+ var validateNextRows = React__default["default"].useCallback(function () {
2905
+ if (isChangeGroup) {
2906
+ var _a = getValues(),
2907
+ validDateEnd = _a.validDateEnd,
2908
+ validDateStart = _a.validDateStart,
2909
+ changeGroups = _a.changeGroups;
2910
+
2911
+ var currentChangeGroup = changeGroups[index];
2912
+ var nextChangeGroups = changeGroups.slice(index + 1);
2913
+ return validateOverlapWithFM(currentChangeGroup, nextChangeGroups, validDateStart, validDateEnd);
2812
2914
  }
2813
2915
 
2814
- var nextValidFieldName = "changeGroups[" + (index + 1) + "]." + fieldname;
2815
- var nextRowValidValue = getValues(nextValidFieldName);
2816
- return nextRowValidValue ? [nextValidFieldName] : [];
2916
+ return true;
2917
+ }, [index, isChangeGroup, getValues]);
2918
+
2919
+ var getPreviousValidFields = function getPreviousValidFields(rowIndex) {
2920
+ var changeGroups = getValues().changeGroups;
2921
+ var previousChangeGroups = changeGroups.slice(0, rowIndex);
2922
+ var validFields = previousChangeGroups.map(function (_element, index) {
2923
+ return getFieldFromRow('valid', index)[0];
2924
+ });
2925
+ return validFields;
2817
2926
  };
2818
2927
 
2819
2928
  var addChangeGroup = React__default["default"].useCallback(function () {
@@ -2862,7 +2971,7 @@
2862
2971
  }],
2863
2972
  onBlur: function onBlur() {
2864
2973
  if (isChangeGroup) {
2865
- var nextValidFieldName = getFieldFromNextRow();
2974
+ var nextValidFieldName = getValidFieldFromNextRow();
2866
2975
  trigger(__spreadArray(__spreadArray(__spreadArray(__spreadArray([], __read(subFields)), __read(mainOptionalFields)), __read(mainFields)), __read(nextValidFieldName)));
2867
2976
  }
2868
2977
  }
@@ -2892,8 +3001,9 @@
2892
3001
  }],
2893
3002
  onBlur: function onBlur() {
2894
3003
  if (isChangeGroup) {
2895
- var nextValidFieldName = getFieldFromNextRow();
2896
- trigger(__spreadArray(__spreadArray(__spreadArray(__spreadArray([], __read(subFields)), __read(mainOptionalFields)), __read(mainFields)), __read(nextValidFieldName)));
3004
+ var nextValidFieldName = getValidFieldFromNextRow();
3005
+ var previousValidFieldNames = getPreviousValidFields(index);
3006
+ trigger(__spreadArray(__spreadArray(__spreadArray(__spreadArray(__spreadArray([], __read(subFields)), __read(mainOptionalFields)), __read(mainFields)), __read(nextValidFieldName)), __read(previousValidFieldNames)));
2897
3007
  }
2898
3008
  }
2899
3009
  })), /*#__PURE__*/React__default["default"].createElement(TafFormTextField, {
@@ -2911,13 +3021,15 @@
2911
3021
  validateValidChangeGroupInterField: function validateValidChangeGroupInterField$1(value) {
2912
3022
  return isChangeGroup ? validateValidChangeGroupInterField(value, getValues(fieldNames.change), getValues('validDateStart'), getValues('validDateEnd')) : true;
2913
3023
  },
2914
- validatePreviousRow: validatePreviousRow
3024
+ validatePreviousRow: validatePreviousRow,
3025
+ validateNextRows: validateNextRows
2915
3026
  }
2916
3027
  },
2917
3028
  onBlur: function onBlur() {
2918
3029
  if (isChangeGroup) {
2919
- var nextValidFieldName = getFieldFromNextRow();
2920
- trigger(__spreadArray(__spreadArray(__spreadArray(__spreadArray([], __read(subFields)), __read(mainOptionalFields)), __read(mainFields)), __read(nextValidFieldName)));
3030
+ var nextValidFieldName = getValidFieldFromNextRow();
3031
+ var previousValidFieldNames = getPreviousValidFields(index);
3032
+ trigger(__spreadArray(__spreadArray(__spreadArray(__spreadArray(__spreadArray([], __read(subFields)), __read(mainOptionalFields)), __read(mainFields)), __read(nextValidFieldName)), __read(previousValidFieldNames)));
2921
3033
  }
2922
3034
  }
2923
3035
  }), /*#__PURE__*/React__default["default"].createElement(TafFormTextField, {
@@ -2931,7 +3043,9 @@
2931
3043
  return _validateRequiredMainFields(value, 'Wind');
2932
3044
  },
2933
3045
  validateRequiredSub: validateRequiredSub,
2934
- validateWindField: validateWindField
3046
+ validateWindField: function validateWindField$1(value) {
3047
+ return validateWindField(value, getValues(fieldNames.weather1), getValues(fieldNames.weather2), getValues(fieldNames.weather3));
3048
+ }
2935
3049
  }
2936
3050
  },
2937
3051
  onBlur: function onBlur() {
@@ -2980,7 +3094,7 @@
2980
3094
  },
2981
3095
  onBlur: function onBlur() {
2982
3096
  var fields = isChangeGroup ? __spreadArray(__spreadArray([], __read(subFields)), __read(mainFields)) : [];
2983
- return trigger(__spreadArray(__spreadArray([], __read(fields)), [fieldNames.weather2, fieldNames.weather3]));
3097
+ return trigger(__spreadArray(__spreadArray([], __read(fields)), [fieldNames.weather2, fieldNames.weather3, fieldNames.wind, fieldNames.cloud1]));
2984
3098
  }
2985
3099
  }), /*#__PURE__*/React__default["default"].createElement(TafFormTextField, {
2986
3100
  label: "Weather",
@@ -2996,7 +3110,7 @@
2996
3110
  },
2997
3111
  onBlur: function onBlur() {
2998
3112
  var fields = isChangeGroup ? __spreadArray(__spreadArray([], __read(subFields)), __read(mainFields)) : [];
2999
- return trigger(__spreadArray(__spreadArray([], __read(fields)), [fieldNames.weather3]));
3113
+ return trigger(__spreadArray(__spreadArray([], __read(fields)), [fieldNames.weather3, fieldNames.wind, fieldNames.cloud1]));
3000
3114
  }
3001
3115
  }), /*#__PURE__*/React__default["default"].createElement(TafFormTextField, {
3002
3116
  label: "Weather",
@@ -3009,6 +3123,9 @@
3009
3123
  return validateThirdWeatherField(getValues(fieldNames.weather1), getValues(fieldNames.weather2), value, getValues(fieldNames.visibility));
3010
3124
  }
3011
3125
  }
3126
+ },
3127
+ onBlur: function onBlur() {
3128
+ return trigger([fieldNames.wind, fieldNames.cloud1]);
3012
3129
  }
3013
3130
  }), /*#__PURE__*/React__default["default"].createElement(TafFormTextField, {
3014
3131
  label: "Cloud",
@@ -3022,7 +3139,7 @@
3022
3139
  },
3023
3140
  validateRequiredSub: validateRequiredSub,
3024
3141
  validateFirstCloud: function validateFirstCloud$1(value) {
3025
- return validateFirstCloud(value, getValues(fieldNames.visibility));
3142
+ return validateFirstCloud(value, getValues(fieldNames.visibility), getValues(fieldNames.weather1), getValues(fieldNames.weather2), getValues(fieldNames.weather3));
3026
3143
  }
3027
3144
  }
3028
3145
  },
@@ -3093,7 +3210,7 @@
3093
3210
  return onRemoveChangeGroup(index);
3094
3211
  }
3095
3212
  }, lastRow ? {
3096
- tabIndex: '-1'
3213
+ tabIndex: -1
3097
3214
  } : {}), /*#__PURE__*/React__default["default"].createElement(icons.Delete, null))));
3098
3215
  });
3099
3216