histropediajs 1.5.0 → 1.6.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.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- HistropediaJS v1.5.0 | Histropedia Ltd. (c) 2026 | https://js.histropedia.com
2
+ HistropediaJS v1.6.0 | Histropedia Ltd. (c) 2026 | https://js.histropedia.com
3
3
  Use of this software is subject to our End User Licence Agreement: https://js.histropedia.com/licence.html
4
4
  */
5
5
  var __defProp = Object.defineProperty;
@@ -180,8 +180,17 @@ const LEGACY_PRECISIONS = {
180
180
  8: "decade",
181
181
  9: "year",
182
182
  10: "month",
183
- 11: "day"
184
- };
183
+ 11: "day",
184
+ 12: "hour",
185
+ 13: "minute",
186
+ 14: "second",
187
+ 15: "millisecond"
188
+ };
189
+ const DMY_STRING_PATTERN = /^(-?\d+)(?:-(\d{1,2}))?(?:-(\d{1,2}))?(?:[T ](\d{1,2})(?::(\d{1,2}))?(?::(\d{1,2})(?:\.(\d{1,3}))?)?)?$/;
190
+ const MILLISECONDS_PER_SECOND = 1e3;
191
+ const MILLISECONDS_PER_MINUTE = 60 * MILLISECONDS_PER_SECOND;
192
+ const MILLISECONDS_PER_HOUR = 60 * MILLISECONDS_PER_MINUTE;
193
+ const MILLISECONDS_PER_DAY = 24 * MILLISECONDS_PER_HOUR;
185
194
  function normalizePrecision(precision) {
186
195
  return Object.prototype.hasOwnProperty.call(LEGACY_PRECISIONS, precision) ? LEGACY_PRECISIONS[precision] : precision || "day";
187
196
  }
@@ -194,16 +203,60 @@ function getHistoricalPeriodBounds(year, size) {
194
203
  };
195
204
  }
196
205
  __name(getHistoricalPeriodBounds, "getHistoricalPeriodBounds");
197
- function Dmy(year, month, day) {
206
+ function clampInteger(value, fallback, minimum, maximum) {
207
+ const parsed = Number(value);
208
+ if (!Number.isFinite(parsed)) {
209
+ return fallback;
210
+ }
211
+ return Math.min(maximum, Math.max(minimum, Math.trunc(parsed)));
212
+ }
213
+ __name(clampInteger, "clampInteger");
214
+ function setTimeParts(target, hour, minute, second, millisecond) {
215
+ const values = {
216
+ hour: clampInteger(hour, 0, 0, 23),
217
+ minute: clampInteger(minute, 0, 0, 59),
218
+ second: clampInteger(second, 0, 0, 59),
219
+ millisecond: clampInteger(millisecond, 0, 0, 999)
220
+ };
221
+ for (const [key, value] of Object.entries(values)) {
222
+ Object.defineProperty(target, key, {
223
+ value,
224
+ enumerable: value !== 0,
225
+ configurable: true,
226
+ writable: true
227
+ });
228
+ }
229
+ }
230
+ __name(setTimeParts, "setTimeParts");
231
+ function setPrecision(target, precision) {
232
+ if (precision === void 0) {
233
+ return;
234
+ }
235
+ Object.defineProperty(target, "precision", {
236
+ value: precision,
237
+ enumerable: false,
238
+ configurable: true,
239
+ writable: true
240
+ });
241
+ }
242
+ __name(setPrecision, "setPrecision");
243
+ function createAdjustedDmy(source, values) {
244
+ return new Dmy({ ...values, precision: source.precision });
245
+ }
246
+ __name(createAdjustedDmy, "createAdjustedDmy");
247
+ function Dmy(year, month, day, hour, minute, second, millisecond) {
198
248
  if (typeof year === "object") {
199
249
  const options = year;
200
250
  this.year = options.year;
201
251
  this.month = options.month;
202
252
  this.day = getCorrectDay(options.year, options.month, options.day);
253
+ setTimeParts(this, options.hour, options.minute, options.second, options.millisecond);
254
+ setPrecision(this, options.precision);
203
255
  } else {
204
256
  this.year = year;
205
257
  this.month = month;
206
258
  this.day = getCorrectDay(year, month, day);
259
+ setTimeParts(this, hour, minute, second, millisecond);
207
260
  }
208
261
  }
209
262
  __name(Dmy, "Dmy");
@@ -230,14 +283,19 @@ function digitGroupedNumber(number, separator) {
230
283
  }
231
284
  __name(digitGroupedNumber, "digitGroupedNumber");
232
285
  Dmy.prototype.getDaysTo = function(other) {
233
- return other.getDaysSinceYearZero() - this.getDaysSinceYearZero();
286
+ const dayDifference = other.getDaysSinceYearZero() - this.getDaysSinceYearZero();
287
+ const timeDifference = other.getMillisecondsOfDay() - this.getMillisecondsOfDay();
288
+ return dayDifference + timeDifference / MILLISECONDS_PER_DAY;
289
+ };
290
+ Dmy.prototype.getMillisecondsOfDay = function() {
291
+ return this.hour * MILLISECONDS_PER_HOUR + this.minute * MILLISECONDS_PER_MINUTE + this.second * MILLISECONDS_PER_SECOND + this.millisecond;
234
292
  };
235
293
  Dmy.prototype.getMonthsTo = function(other) {
236
294
  return this.getYearsTo(other) * 12;
237
295
  };
238
296
  Dmy.prototype.getYearsTo = function(other) {
239
297
  let yearDiff = other.year - this.year;
240
- yearDiff += (other.month - this.month) / 12 + (other.day - this.day) / 365.2422;
298
+ yearDiff += (other.month - this.month) / 12 + (other.day - this.day) / 365.2422 + (other.getMillisecondsOfDay() - this.getMillisecondsOfDay()) / (MILLISECONDS_PER_DAY * 365.2422);
241
299
  return yearDiff;
242
300
  };
243
301
  Dmy.prototype.getLeapYearsSinceYearZero = function() {
@@ -257,14 +315,11 @@ Dmy.prototype.getDaysSinceYearZero = function() {
257
315
  return days;
258
316
  };
259
317
  Dmy.prototype.getDayOfYear = function() {
260
- if (isUndefined(this._dayOfYear)) {
261
- this._dayOfYear = 0;
262
- for (let m = 1; m < this.month; m++) {
263
- this._dayOfYear += daysInMonth(this.year, m);
264
- }
265
- this._dayOfYear += this.day;
318
+ let dayOfYear = this.day;
319
+ for (let m = 1; m < this.month; m++) {
320
+ dayOfYear += daysInMonth(this.year, m);
266
321
  }
267
- return this._dayOfYear;
322
+ return dayOfYear;
268
323
  };
269
324
  Dmy.getByDayOfYear = function(year, dayOfYear) {
270
325
  const dmy = new Dmy(year, 1, 1);
@@ -290,18 +345,77 @@ Dmy.prototype.addDays = function(amount) {
290
345
  let day = this.day;
291
346
  let month = this.month;
292
347
  let year = this.year;
293
- for (let i = 0; i < amount; i++) {
294
- day++;
295
- if (day > daysInMonth(year, month)) {
296
- day = 1;
297
- month++;
298
- if (month > 12) {
299
- month = 1;
300
- year++;
348
+ const direction = amount < 0 ? -1 : 1;
349
+ let remaining = Math.abs(Math.trunc(amount));
350
+ while (remaining > 0) {
351
+ if (direction > 0) {
352
+ day++;
353
+ if (day > daysInMonth(year, month)) {
354
+ day = 1;
355
+ month++;
356
+ if (month > 12) {
357
+ month = 1;
358
+ year++;
359
+ }
360
+ }
361
+ } else {
362
+ day--;
363
+ if (day < 1) {
364
+ month--;
365
+ if (month < 1) {
366
+ month = 12;
367
+ year--;
368
+ }
369
+ day = daysInMonth(year, month);
301
370
  }
302
371
  }
372
+ remaining--;
303
373
  }
304
- return new Dmy(year, month, day);
374
+ return createAdjustedDmy(this, {
375
+ year,
376
+ month,
377
+ day,
378
+ hour: this.hour,
379
+ minute: this.minute,
380
+ second: this.second,
381
+ millisecond: this.millisecond
382
+ });
383
+ };
384
+ Dmy.prototype.addMilliseconds = function(amount) {
385
+ let date = this;
386
+ let milliseconds = this.getMillisecondsOfDay() + Math.trunc(amount);
387
+ while (milliseconds < 0) {
388
+ date = date.addDays(-1);
389
+ milliseconds += MILLISECONDS_PER_DAY;
390
+ }
391
+ while (milliseconds >= MILLISECONDS_PER_DAY) {
392
+ date = date.addDays(1);
393
+ milliseconds -= MILLISECONDS_PER_DAY;
394
+ }
395
+ const hour = Math.floor(milliseconds / MILLISECONDS_PER_HOUR);
396
+ milliseconds -= hour * MILLISECONDS_PER_HOUR;
397
+ const minute = Math.floor(milliseconds / MILLISECONDS_PER_MINUTE);
398
+ milliseconds -= minute * MILLISECONDS_PER_MINUTE;
399
+ const second = Math.floor(milliseconds / MILLISECONDS_PER_SECOND);
400
+ const millisecond = milliseconds - second * MILLISECONDS_PER_SECOND;
401
+ return createAdjustedDmy(this, {
402
+ year: date.year,
403
+ month: date.month,
404
+ day: date.day,
405
+ hour,
406
+ minute,
407
+ second,
408
+ millisecond
409
+ });
410
+ };
411
+ Dmy.prototype.addSeconds = function(amount) {
412
+ return this.addMilliseconds(amount * MILLISECONDS_PER_SECOND);
413
+ };
414
+ Dmy.prototype.addMinutes = function(amount) {
415
+ return this.addMilliseconds(amount * MILLISECONDS_PER_MINUTE);
416
+ };
417
+ Dmy.prototype.addHours = function(amount) {
418
+ return this.addMilliseconds(amount * MILLISECONDS_PER_HOUR);
305
419
  };
306
420
  Dmy.prototype.addMonths = function(amount) {
307
421
  let month = this.month;
@@ -313,22 +427,10 @@ Dmy.prototype.addMonths = function(amount) {
313
427
  year++;
314
428
  }
315
429
  }
316
- return new Dmy(year, month, 1);
430
+ return createAdjustedDmy(this, { year, month, day: 1 });
317
431
  };
318
432
  Dmy.prototype.getPreviousDay = function() {
319
- let day = this.day;
320
- let month = this.month;
321
- let year = this.year;
322
- day--;
323
- if (day < 1) {
324
- month--;
325
- if (month < 1) {
326
- month = 12;
327
- year--;
328
- }
329
- day = daysInMonth(year, month);
330
- }
331
- return new Dmy(year, month, day);
433
+ return this.addDays(-1);
332
434
  };
333
435
  Dmy.prototype.getNextMonth = function() {
334
436
  let month = this.month;
@@ -338,7 +440,7 @@ Dmy.prototype.getNextMonth = function() {
338
440
  month = 1;
339
441
  year++;
340
442
  }
341
- return new Dmy(year, month, 1);
443
+ return createAdjustedDmy(this, { year, month, day: 1 });
342
444
  };
343
445
  Dmy.prototype.getPreviousMonth = function() {
344
446
  let month = this.month;
@@ -348,11 +450,15 @@ Dmy.prototype.getPreviousMonth = function() {
348
450
  month = 12;
349
451
  year--;
350
452
  }
351
- return new Dmy(year, month, 1);
453
+ return createAdjustedDmy(this, { year, month, day: 1 });
352
454
  };
353
455
  Dmy.prototype.addYears = function(amount) {
354
- return new Dmy(this.year + amount, 1, 1);
456
+ return createAdjustedDmy(this, { year: this.year + amount, month: 1, day: 1 });
355
457
  };
458
+ function pad(value, length) {
459
+ return String(value).padStart(length, "0");
460
+ }
461
+ __name(pad, "pad");
356
462
  Dmy.prototype.format = function(format, options) {
357
463
  options = options || {};
358
464
  format = format || "D MMM YYYY";
@@ -375,24 +481,26 @@ Dmy.prototype.format = function(format, options) {
375
481
  return format.replace(
376
482
  "YYYY",
377
483
  isBc ? digitGroupedNumber(-this.year + 1, thousandsSeparator) + bceText : digitGroupedNumber(this.year, thousandsSeparator)
378
- ).replace("D", this.day).replace("MMM", MONTH_NAMES[this.month - 1]);
484
+ ).replace("D", this.day).replace("MMM", MONTH_NAMES[this.month - 1]).replace("HH", pad(this.hour, 2)).replace("mm", pad(this.minute, 2)).replace("ss", pad(this.second, 2)).replace("SSS", pad(this.millisecond, 3));
379
485
  };
380
486
  Dmy.prototype.isSame = function(other) {
381
- return other.year === this.year && other.month === this.month && other.day === this.day;
487
+ return other.year === this.year && other.month === this.month && other.day === this.day && (other.hour || 0) === this.hour && (other.minute || 0) === this.minute && (other.second || 0) === this.second && (other.millisecond || 0) === this.millisecond;
382
488
  };
383
489
  Dmy.prototype.toKeyString = function() {
384
- return `${this.year}|${this.month}|${this.day}`;
490
+ const dateKey = `${this.year}|${this.month}|${this.day}`;
491
+ if (this.getMillisecondsOfDay() === 0) {
492
+ return dateKey;
493
+ }
494
+ return `${dateKey}|${this.hour}|${this.minute}|${this.second}|${this.millisecond}`;
385
495
  };
386
496
  Dmy.prototype.isAfter = function(other) {
387
- if (this.year > other.year) {
388
- return true;
389
- }
390
- if (this.year === other.year) {
391
- if (this.month > other.month) {
392
- return true;
393
- }
394
- if (this.month === other.month) {
395
- return this.day > other.day;
497
+ const fields = ["year", "month", "day", "hour", "minute", "second", "millisecond"];
498
+ for (let i = 0; i < fields.length; i++) {
499
+ const field = fields[i];
500
+ const thisValue = this[field] || 0;
501
+ const otherValue = other[field] || 0;
502
+ if (thisValue !== otherValue) {
503
+ return thisValue > otherValue;
396
504
  }
397
505
  }
398
506
  return false;
@@ -412,15 +520,24 @@ Dmy.prototype.compare = function(other) {
412
520
  Dmy.prototype.asFloat = function() {
413
521
  const m = this.month - 1;
414
522
  const d = this.day - 1;
415
- if (m === 0 && d === 0) {
523
+ const dayFraction = this.getMillisecondsOfDay() / MILLISECONDS_PER_DAY;
524
+ if (m === 0 && d === 0 && dayFraction === 0) {
416
525
  return this.year;
417
526
  }
418
- return Math.round((this.year + (m * 31 + d) / 371) * 100) / 100;
527
+ return Math.round((this.year + (m * 31 + d + dayFraction) / 371) * 100) / 100;
419
528
  };
420
529
  Dmy.prototype.isInFuture = function() {
421
530
  const date = /* @__PURE__ */ new Date();
422
- const today = new Dmy(date.getFullYear(), date.getMonth() + 1, date.getDate());
423
- return this.isAfter(today);
531
+ const now = new Dmy(
532
+ date.getFullYear(),
533
+ date.getMonth() + 1,
534
+ date.getDate(),
535
+ date.getHours(),
536
+ date.getMinutes(),
537
+ date.getSeconds(),
538
+ date.getMilliseconds()
539
+ );
540
+ return this.isAfter(now);
424
541
  };
425
542
  Dmy.prototype.convertToIso = function() {
426
543
  if (this.year < 0) {
@@ -433,6 +550,18 @@ Dmy.CreateAsStartOfPeriod = function(isoDmy, precision) {
433
550
  const month = isoDmy.month || 1;
434
551
  const day = isoDmy.day || 1;
435
552
  precision = normalizePrecision(precision);
553
+ if (precision === "millisecond") {
554
+ return new Dmy(isoDmy);
555
+ }
556
+ if (precision === "second") {
557
+ return new Dmy(isoYear, month, day, isoDmy.hour, isoDmy.minute, isoDmy.second, 0);
558
+ }
559
+ if (precision === "minute") {
560
+ return new Dmy(isoYear, month, day, isoDmy.hour, isoDmy.minute, 0, 0);
561
+ }
562
+ if (precision === "hour") {
563
+ return new Dmy(isoYear, month, day, isoDmy.hour, 0, 0, 0);
564
+ }
436
565
  if (precision === "day") {
437
566
  return new Dmy(isoYear, month, day);
438
567
  }
@@ -458,6 +587,18 @@ Dmy.CreateAsEndOfPeriod = function(isoDmy, precision) {
458
587
  const month = isoDmy.month || 1;
459
588
  const day = isoDmy.day || 1;
460
589
  precision = normalizePrecision(precision);
590
+ if (precision === "millisecond") {
591
+ return new Dmy(isoDmy);
592
+ }
593
+ if (precision === "second") {
594
+ return new Dmy(isoYear, month, day, isoDmy.hour, isoDmy.minute, isoDmy.second, 999);
595
+ }
596
+ if (precision === "minute") {
597
+ return new Dmy(isoYear, month, day, isoDmy.hour, isoDmy.minute, 59, 999);
598
+ }
599
+ if (precision === "hour") {
600
+ return new Dmy(isoYear, month, day, isoDmy.hour, 59, 59, 999);
601
+ }
461
602
  if (precision === "day") {
462
603
  return new Dmy(isoYear, month, day);
463
604
  }
@@ -485,6 +626,26 @@ Dmy.CreateAsEndOfPeriod = function(isoDmy, precision) {
485
626
  Logger.error("precision is not implemented");
486
627
  return new Dmy(isoYear, month, day);
487
628
  };
629
+ Dmy.CreateAsExclusiveEndOfPeriod = function(isoDmy, precision) {
630
+ precision = normalizePrecision(precision);
631
+ const start = Dmy.CreateAsStartOfPeriod(isoDmy, precision);
632
+ if (precision === "millisecond") {
633
+ return start.addMilliseconds(1);
634
+ }
635
+ if (precision === "second") {
636
+ return start.addSeconds(1);
637
+ }
638
+ if (precision === "minute") {
639
+ return start.addMinutes(1);
640
+ }
641
+ if (precision === "hour") {
642
+ return start.addHours(1);
643
+ }
644
+ if (precision === "day") {
645
+ return start.addDays(1);
646
+ }
647
+ return Dmy.CreateAsEndOfPeriod(isoDmy, precision).addDays(1);
648
+ };
488
649
  Dmy.now = function() {
489
650
  const now = /* @__PURE__ */ new Date();
490
651
  return new Dmy({
@@ -493,21 +654,37 @@ Dmy.now = function() {
493
654
  day: now.getDate()
494
655
  });
495
656
  };
657
+ function parseDmyString(dateString) {
658
+ const match = String(dateString).match(DMY_STRING_PATTERN);
659
+ if (!match) {
660
+ return null;
661
+ }
662
+ const year = parseInt(match[1], 10);
663
+ const month = match[2] == null ? 1 : parseInt(match[2], 10);
664
+ const day = match[3] == null ? 1 : parseInt(match[3], 10);
665
+ const hour = match[4] == null ? 0 : parseInt(match[4], 10);
666
+ const minute = match[5] == null ? 0 : parseInt(match[5], 10);
667
+ const second = match[6] == null ? 0 : parseInt(match[6], 10);
668
+ const millisecond = match[7] ? parseInt(match[7].padEnd(3, "0"), 10) : 0;
669
+ return {
670
+ date: new Dmy({
671
+ year,
672
+ month,
673
+ day,
674
+ hour,
675
+ minute,
676
+ second,
677
+ millisecond
678
+ }),
679
+ hasMonth: match[2] != null,
680
+ hasDay: match[3] != null,
681
+ hasTime: match[4] != null
682
+ };
683
+ }
684
+ __name(parseDmyString, "parseDmyString");
496
685
  Dmy.fromString = function(dateString) {
497
- const dateParts = dateString.split("-");
498
- const isBc = dateParts[0] === "";
499
- if (isBc) {
500
- dateParts.shift();
501
- dateParts[0] = `-${dateParts[0]}`;
502
- }
503
- const year = parseInt(dateParts[0]);
504
- const month = parseInt(dateParts[1]) || 1;
505
- const day = parseInt(dateParts[2]) || 1;
506
- return new Dmy({
507
- year: parseInt(year),
508
- month: parseInt(month),
509
- day: parseInt(day)
510
- });
686
+ const parsed = parseDmyString(dateString);
687
+ return parsed ? parsed.date : new Dmy(NaN, 1, 1);
511
688
  };
512
689
  function merge(target, ...sources) {
513
690
  return Object.assign(target, ...sources);
@@ -595,10 +772,10 @@ function defineValueOrFunctionProperty(obj, propName) {
595
772
  });
596
773
  }
597
774
  __name(defineValueOrFunctionProperty, "defineValueOrFunctionProperty");
598
- function isUndefined$1(value) {
775
+ function isUndefined(value) {
599
776
  return typeof value === "undefined";
600
777
  }
601
- __name(isUndefined$1, "isUndefined$1");
778
+ __name(isUndefined, "isUndefined");
602
779
  const propertyAbbreviations = {
603
780
  id: "i",
604
781
  title: "t",
@@ -607,10 +784,18 @@ const propertyAbbreviations = {
607
784
  "from.year": "fy",
608
785
  "from.month": "fm",
609
786
  "from.day": "fd",
787
+ "from.hour": "fhr",
788
+ "from.minute": "fmi",
789
+ "from.second": "fs",
790
+ "from.millisecond": "fms",
610
791
  "from.precision": "fp",
611
792
  "to.year": "ty",
612
793
  "to.month": "tm",
613
794
  "to.day": "td",
795
+ "to.hour": "thr",
796
+ "to.minute": "tmi",
797
+ "to.second": "ts",
798
+ "to.millisecond": "tms",
614
799
  "to.precision": "tp",
615
800
  isToPresent: "p",
616
801
  isStarred: "v",
@@ -629,7 +814,8 @@ function extractProperty(data, property) {
629
814
  __name(extractProperty, "extractProperty");
630
815
  function extractPropertyFromPath(data, path) {
631
816
  const pathParts = path.split(".");
632
- return pathParts.reduce((curr, next) => curr ? curr[next] : void 0, data);
817
+ const value = pathParts.reduce((curr, next) => curr ? curr[next] : void 0, data);
818
+ return isUndefined(value) ? data[propertyAbbreviations[path]] : value;
633
819
  }
634
820
  __name(extractPropertyFromPath, "extractPropertyFromPath");
635
821
  function isPath(property) {
@@ -645,9 +831,9 @@ function getPropertyFromData(data, property) {
645
831
  __name(getPropertyFromData, "getPropertyFromData");
646
832
  function existsInData(data, property) {
647
833
  if (isPath(property)) {
648
- return !isUndefined$1(extractPropertyFromPath(data, property));
834
+ return !isUndefined(extractPropertyFromPath(data, property));
649
835
  }
650
- return !isUndefined$1(extractProperty(data, property));
836
+ return !isUndefined(extractProperty(data, property));
651
837
  }
652
838
  __name(existsInData, "existsInData");
653
839
  defineValueOrFunctionProperty(Article.prototype, "hidePeriodLine");
@@ -723,12 +909,16 @@ Article.prototype.setupByReceivedData = function(receivedData) {
723
909
  this.isImageInfoLoaded = false;
724
910
  };
725
911
  Article.prototype._setupArticlePeriod = function(data) {
726
- const fromDmy = new Dmy(
912
+ let fromDmy = new Dmy(
727
913
  parseInt(getPropertyFromData(data, "from.year")),
728
914
  parseInt(getPropertyFromData(data, "from.month")),
729
- parseInt(getPropertyFromData(data, "from.day"))
915
+ parseInt(getPropertyFromData(data, "from.day")),
916
+ parseInt(getPropertyFromData(data, "from.hour")),
917
+ parseInt(getPropertyFromData(data, "from.minute")),
918
+ parseInt(getPropertyFromData(data, "from.second")),
919
+ parseInt(getPropertyFromData(data, "from.millisecond"))
730
920
  );
731
- this.owner.getDmyFromInput(fromDmy);
921
+ fromDmy = this.owner.getDmyFromInput(fromDmy);
732
922
  const fromPrecision = getPropertyFromData(data, "from.precision");
733
923
  const isToPresentProp = getPropertyFromData(data, "isToPresent");
734
924
  const isToPresent = isToPresentProp === true || isToPresentProp === 1;
@@ -739,17 +929,21 @@ Article.prototype._setupArticlePeriod = function(data) {
739
929
  toDmy = new Dmy(
740
930
  parseInt(getPropertyFromData(data, "to.year")),
741
931
  parseInt(getPropertyFromData(data, "to.month")),
742
- parseInt(getPropertyFromData(data, "to.day"))
932
+ parseInt(getPropertyFromData(data, "to.day")),
933
+ parseInt(getPropertyFromData(data, "to.hour")),
934
+ parseInt(getPropertyFromData(data, "to.minute")),
935
+ parseInt(getPropertyFromData(data, "to.second")),
936
+ parseInt(getPropertyFromData(data, "to.millisecond"))
743
937
  );
744
- this.owner.getDmyFromInput(toDmy);
938
+ toDmy = this.owner.getDmyFromInput(toDmy);
745
939
  toPrecision = getPropertyFromData(data, "to.precision");
746
940
  } else {
747
941
  toDmy = fromDmy;
748
942
  toPrecision = fromPrecision;
749
943
  }
750
944
  const from = Dmy.CreateAsStartOfPeriod(fromDmy, fromPrecision);
751
- const to = Dmy.CreateAsEndOfPeriod(toDmy, toPrecision);
752
- this.period = { from, to: to.getNextDay(), isToPresent };
945
+ const to = Dmy.CreateAsExclusiveEndOfPeriod(toDmy, toPrecision);
946
+ this.period = { from, to, isToPresent };
753
947
  this.period.years = this.period.to.asFloat() - this.period.from.asFloat();
754
948
  };
755
949
  Article.prototype.setImage = function() {
@@ -864,7 +1058,7 @@ Article.prototype.headerOverlapsWith = function(other) {
864
1058
  return otherRight >= thisLeft && thisRight >= otherLeft;
865
1059
  };
866
1060
  Article.prototype._getWidth = function() {
867
- return this._getCurrentStyle().width;
1061
+ return this.getCurrentStyle().width;
868
1062
  };
869
1063
  Article.prototype.getWidth = function() {
870
1064
  this._ensureSizeCache();
@@ -925,29 +1119,10 @@ Article.prototype.setOption = function(option, value) {
925
1119
  const styleChanged = existsInData(option, "style");
926
1120
  const activeStyleChanged = existsInData(option, "activeStyle");
927
1121
  const hoverStyleChanged = existsInData(option, "hoverStyle");
928
- let activeHoverStyleUpdated = false;
929
- if (styleChanged) {
1122
+ if (styleChanged || hoverStyleChanged || activeStyleChanged) {
930
1123
  this.initialiseStyles(this.data);
931
- this.invalidateCaches();
932
- } else {
933
- if (hoverStyleChanged) {
934
- deepMerge(this.hoverStyle, option.hoverStyle);
935
- this.activeHoverStyle = deepMerge(
936
- {},
937
- this.hoverStyle,
938
- this.owner._getResolvedArticleOptions(this).defaultActiveStyle,
939
- this.data.activeStyle
940
- );
941
- activeHoverStyleUpdated = true;
942
- }
943
- if (activeStyleChanged) {
944
- this.activeStyle = deepMerge(this.activeStyle, option.activeStyle);
945
- if (!activeHoverStyleUpdated) {
946
- deepMerge(this.activeHoverStyle, option.activeStyle);
947
- }
948
- }
949
- this.invalidateCaches();
950
1124
  }
1125
+ this.invalidateCaches();
951
1126
  const cardLayoutChanged = existsInData(option, "cardLayout");
952
1127
  if (cardLayoutChanged) {
953
1128
  this.setCardLayout(option.cardLayout);
@@ -962,35 +1137,27 @@ Article.prototype.setOption = function(option, value) {
962
1137
  }
963
1138
  };
964
1139
  Article.prototype.initialiseStyles = function(data = this.data) {
965
- const articleOptions = this.owner._getResolvedArticleOptions(this);
966
1140
  const resolvedDefaultCardLayout = this.owner._resolveDefaultCardLayoutName(this);
967
1141
  const explicitCardLayout = this.owner._getExplicitArticleCardLayout(this);
968
1142
  const cardLayout = explicitCardLayout || resolvedDefaultCardLayout;
969
1143
  this._resolvedDefaultCardLayout = resolvedDefaultCardLayout;
970
1144
  this._resolvedCardLayoutName = cardLayout;
971
- const layoutStyles = articleOptions.layoutStyles[cardLayout] || {};
972
- const style = deepMerge(
973
- {},
974
- articleOptions.defaultStyle,
975
- layoutStyles.style || {},
976
- getPropertyFromData(data, "style") || {}
977
- );
1145
+ const styleOverrides = this.owner._getResolvedArticleStyleOverrides(this, cardLayout);
1146
+ const style = deepMerge({}, styleOverrides.style, getPropertyFromData(data, "style") || {});
978
1147
  const activeStyle = deepMerge(
979
1148
  {},
980
- articleOptions.defaultActiveStyle,
981
- layoutStyles.activeStyle || {},
1149
+ styleOverrides.activeStyle,
982
1150
  getPropertyFromData(data, "activeStyle") || {}
983
1151
  );
984
1152
  const hoverStyle = deepMerge(
985
1153
  {},
986
- articleOptions.defaultHoverStyle,
987
- layoutStyles.hoverStyle || {},
1154
+ styleOverrides.hoverStyle,
988
1155
  getPropertyFromData(data, "hoverStyle") || {}
989
1156
  );
990
1157
  this.style = style;
991
1158
  this.activeStyle = deepMerge({}, style, activeStyle);
992
1159
  this.hoverStyle = deepMerge({}, style, hoverStyle);
993
- this.activeHoverStyle = deepMerge({}, this.hoverStyle, activeStyle);
1160
+ this.activeHoverStyle = deepMerge({}, style, hoverStyle, activeStyle);
994
1161
  };
995
1162
  Article.prototype.setStyle = function(option, value) {
996
1163
  if (typeof option === "string") {
@@ -1012,13 +1179,7 @@ Article.prototype.setHoverStyle = function(option, value) {
1012
1179
  option = buildObjectFromPath(option, value);
1013
1180
  }
1014
1181
  this.data.hoverStyle = deepMerge(this.data.hoverStyle || {}, option);
1015
- deepMerge(this.hoverStyle, option);
1016
- this.activeHoverStyle = deepMerge(
1017
- {},
1018
- this.hoverStyle,
1019
- this.owner._getResolvedArticleOptions(this).defaultActiveStyle,
1020
- this.data.activeStyle
1021
- );
1182
+ this.initialiseStyles(this.data);
1022
1183
  this.owner.requestRedraw();
1023
1184
  };
1024
1185
  Article.prototype.setActiveStyle = function(option, value) {
@@ -1029,8 +1190,7 @@ Article.prototype.setActiveStyle = function(option, value) {
1029
1190
  option = buildObjectFromPath(option, value);
1030
1191
  }
1031
1192
  this.data.activeStyle = deepMerge(this.data.activeStyle || {}, option);
1032
- deepMerge(this.activeStyle, option);
1033
- deepMerge(this.activeHoverStyle, option);
1193
+ this.initialiseStyles(this.data);
1034
1194
  this.owner.requestRedraw();
1035
1195
  };
1036
1196
  Article.prototype.activated = function() {
@@ -1192,6 +1352,57 @@ Article.prototype.moveToOffsetWithAnim = function(destination) {
1192
1352
  });
1193
1353
  moveAnimation.dummyElement = moveTween;
1194
1354
  };
1355
+ function roundPixel(value) {
1356
+ return Math.round(value);
1357
+ }
1358
+ __name(roundPixel, "roundPixel");
1359
+ function snapPixel(value, dpr = 1) {
1360
+ const ratio = typeof dpr === "number" && isFinite(dpr) && dpr > 0 ? dpr : 1;
1361
+ return Math.round(value * ratio) / ratio;
1362
+ }
1363
+ __name(snapPixel, "snapPixel");
1364
+ function retainPixel(value) {
1365
+ return value;
1366
+ }
1367
+ __name(retainPixel, "retainPixel");
1368
+ function createPixelSnap(dpr = 1, mode = "device") {
1369
+ if (mode === false || mode === "none") {
1370
+ return retainPixel;
1371
+ }
1372
+ if (mode === "css") {
1373
+ return roundPixel;
1374
+ }
1375
+ return /* @__PURE__ */ __name(function snapToDevicePixel(value) {
1376
+ return snapPixel(value, dpr);
1377
+ }, "snapToDevicePixel");
1378
+ }
1379
+ __name(createPixelSnap, "createPixelSnap");
1380
+ function createStrokePixelSnap(dpr = 1, mode = "device", lineWidth = 1) {
1381
+ if (mode === false || mode === "none") {
1382
+ return retainPixel;
1383
+ }
1384
+ const deviceRatio = typeof dpr === "number" && isFinite(dpr) && dpr > 0 ? dpr : 1;
1385
+ const ratio = mode === "css" ? 1 : deviceRatio;
1386
+ const width = typeof lineWidth === "number" && isFinite(lineWidth) && lineWidth > 0 ? lineWidth : 1;
1387
+ const halfPhysicalWidth = width * ratio / 2;
1388
+ return /* @__PURE__ */ __name(function snapStrokeCentre(value) {
1389
+ return (Math.round(value * ratio - halfPhysicalWidth) + halfPhysicalWidth) / ratio;
1390
+ }, "snapStrokeCentre");
1391
+ }
1392
+ __name(createStrokePixelSnap, "createStrokePixelSnap");
1393
+ function createInnerSpace(outerSpace, borderWidth, snap) {
1394
+ if (!snap) {
1395
+ snap = roundPixel;
1396
+ }
1397
+ const halfBorder = borderWidth / 2;
1398
+ return {
1399
+ left: snap(outerSpace.left + halfBorder),
1400
+ top: snap(outerSpace.top + halfBorder),
1401
+ width: snap(outerSpace.width - borderWidth),
1402
+ height: snap(outerSpace.height - borderWidth)
1403
+ };
1404
+ }
1405
+ __name(createInnerSpace, "createInnerSpace");
1195
1406
  Article.prototype.draw = function(ctx) {
1196
1407
  if (!this.isDataLoaded || isNaN(this.position.left)) {
1197
1408
  return;
@@ -1201,12 +1412,15 @@ Article.prototype.draw = function(ctx) {
1201
1412
  const cardLayout = this._getCurrentCardLayout();
1202
1413
  cardLayout.draw.call(this, ctx);
1203
1414
  };
1204
- Article.prototype._getCurrentStyle = function() {
1415
+ Article.prototype.getCurrentStyle = function() {
1205
1416
  if (this.isActive) {
1206
1417
  return this.isMouseover ? this.activeHoverStyle : this.activeStyle;
1207
1418
  }
1208
1419
  return this.isMouseover ? this.hoverStyle : this.style;
1209
1420
  };
1421
+ Article.prototype._getCurrentStyle = function() {
1422
+ return this.getCurrentStyle();
1423
+ };
1210
1424
  Article.prototype._getPeriodLineRenderGeometry = function(axisY) {
1211
1425
  const resolvedArticleOptions = this.owner._getResolvedArticleOptions(this);
1212
1426
  const periodLineOptions = resolvedArticleOptions.periodLine || {};
@@ -1238,7 +1452,7 @@ Article.prototype._getPeriodLineRenderGeometry = function(axisY) {
1238
1452
  };
1239
1453
  Article.prototype.drawPeriodLinesAndConnectors = function(ctx, axisY) {
1240
1454
  ctx.globalAlpha = this.opacity;
1241
- const style = this._getCurrentStyle();
1455
+ const style = this.getCurrentStyle();
1242
1456
  const geometry = this._getPeriodLineRenderGeometry(axisY);
1243
1457
  const y = geometry.y;
1244
1458
  if (!this.hidePeriodLine) {
@@ -1270,16 +1484,26 @@ Article.prototype.drawPeriodLinesAndConnectors = function(ctx, axisY) {
1270
1484
  const y1 = y;
1271
1485
  const x2 = connectorEnd.left;
1272
1486
  const y2 = connectorEnd.top;
1487
+ const isVertical = Math.abs(x1 - x2) < Number.EPSILON;
1488
+ const pixelSnapMode = this.owner?.options?.article?.rendering?.pixelSnap;
1489
+ const snapStroke = createStrokePixelSnap(
1490
+ this.owner?.getCanvasDpr?.() || 1,
1491
+ pixelSnapMode,
1492
+ style.connectorLine.thickness
1493
+ );
1494
+ const connectorX = isVertical ? snapStroke(x2) : null;
1495
+ const drawX1 = connectorX ?? x1;
1496
+ const drawX2 = connectorX ?? x2;
1273
1497
  ctx.strokeStyle = ctx.fillStyle = style.color;
1274
1498
  ctx.lineWidth = style.connectorLine.thickness;
1275
1499
  ctx.beginPath();
1276
- ctx.moveTo(x1, y1);
1277
- ctx.lineTo(x2, y2);
1500
+ ctx.moveTo(drawX1, y1);
1501
+ ctx.lineTo(drawX2, y2);
1278
1502
  ctx.stroke();
1279
- const radians = Math.atan((y2 - y1) / (x2 - x1)) + (x2 >= x1 ? -90 : 90) * Math.PI / 180;
1503
+ const radians = Math.atan((y2 - y1) / (drawX2 - drawX1)) + (drawX2 >= drawX1 ? -90 : 90) * Math.PI / 180;
1280
1504
  ctx.save();
1281
1505
  ctx.beginPath();
1282
- ctx.translate(x2, y2);
1506
+ ctx.translate(drawX2, y2);
1283
1507
  ctx.rotate(radians);
1284
1508
  ctx.moveTo(-style.connectorLine.arrow.width, 0);
1285
1509
  ctx.lineTo(style.connectorLine.arrow.width, 0);
@@ -1351,7 +1575,7 @@ Article.prototype.memo = function(key, factory) {
1351
1575
  return key in bucket ? bucket[key] : bucket[key] = factory.call(this);
1352
1576
  };
1353
1577
  Article.prototype._getSizeCacheKey = function() {
1354
- const style = typeof this._getCurrentStyle === "function" ? this._getCurrentStyle() : null;
1578
+ const style = typeof this.getCurrentStyle === "function" ? this.getCurrentStyle() : null;
1355
1579
  const imageStyle = style && style.image || {};
1356
1580
  const header = style && style.header || {};
1357
1581
  const subheader = style && style.subheader || {};
@@ -1587,6 +1811,10 @@ const sorters = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
1587
1811
  const MINOR_MARKER = 0;
1588
1812
  const MAJOR_MARKER = 1;
1589
1813
  const ERA_MARKER = 2;
1814
+ const ZOOM_MILLISECOND = -4;
1815
+ const ZOOM_SECOND = -3;
1816
+ const ZOOM_MINUTE = -2;
1817
+ const ZOOM_HOUR = -1;
1590
1818
  const ZOOM_DAY = 0;
1591
1819
  const ZOOM_MONTH = 1;
1592
1820
  const ZOOM_YEAR = 2;
@@ -1599,6 +1827,10 @@ const ZOOM_MILLION_YEARS = 8;
1599
1827
  const ZOOM_10_MILLION_YEARS = 9;
1600
1828
  const ZOOM_100_MILLION_YEARS = 10;
1601
1829
  const ZOOM_BILLION_YEARS = 11;
1830
+ const PRECISION_MILLISECOND = "millisecond";
1831
+ const PRECISION_SECOND = "second";
1832
+ const PRECISION_MINUTE = "minute";
1833
+ const PRECISION_HOUR = "hour";
1602
1834
  const PRECISION_DAY = "day";
1603
1835
  const PRECISION_MONTH = "month";
1604
1836
  const PRECISION_YEAR = "year";
@@ -1620,6 +1852,249 @@ const DENSITY_ALL = "all";
1620
1852
  const RANGE_ALL = "all";
1621
1853
  const RANGE_SCREEN = "screen";
1622
1854
  const DENSITY_SETTINGS = [
1855
+ {
1856
+ region: -26,
1857
+ zoom: { from: Number.NEGATIVE_INFINITY, to: -80.86 },
1858
+ low: 1,
1859
+ medium: 2,
1860
+ high: 3,
1861
+ all: 25,
1862
+ step: { months: 0, days: 0, divisionsPerDay: 134217728 }
1863
+ },
1864
+ {
1865
+ region: -25,
1866
+ zoom: { from: -80.86, to: -77.75 },
1867
+ low: 1,
1868
+ medium: 2,
1869
+ high: 3,
1870
+ all: 25,
1871
+ step: { months: 0, days: 0, divisionsPerDay: 67108864 }
1872
+ },
1873
+ {
1874
+ region: -24,
1875
+ zoom: { from: -77.75, to: -74.64 },
1876
+ low: 1,
1877
+ medium: 2,
1878
+ high: 3,
1879
+ all: 25,
1880
+ step: { months: 0, days: 0, divisionsPerDay: 33554432 }
1881
+ },
1882
+ {
1883
+ region: -23,
1884
+ zoom: { from: -74.64, to: -71.53 },
1885
+ low: 1,
1886
+ medium: 2,
1887
+ high: 3,
1888
+ all: 25,
1889
+ step: { months: 0, days: 0, divisionsPerDay: 16777216 }
1890
+ },
1891
+ {
1892
+ region: -22,
1893
+ zoom: { from: -71.53, to: -68.42 },
1894
+ low: 1,
1895
+ medium: 2,
1896
+ high: 3,
1897
+ all: 25,
1898
+ step: { months: 0, days: 0, divisionsPerDay: 8388608 }
1899
+ },
1900
+ {
1901
+ region: -21,
1902
+ zoom: { from: -68.42, to: -65.31 },
1903
+ low: 1,
1904
+ medium: 2,
1905
+ high: 3,
1906
+ all: 25,
1907
+ step: { months: 0, days: 0, divisionsPerDay: 4194304 }
1908
+ },
1909
+ {
1910
+ region: -20,
1911
+ zoom: { from: -65.31, to: -62.2 },
1912
+ low: 1,
1913
+ medium: 2,
1914
+ high: 3,
1915
+ all: 25,
1916
+ step: { months: 0, days: 0, divisionsPerDay: 2097152 }
1917
+ },
1918
+ {
1919
+ region: -19,
1920
+ zoom: { from: -62.2, to: -59.09 },
1921
+ low: 1,
1922
+ medium: 2,
1923
+ high: 3,
1924
+ all: 25,
1925
+ step: { months: 0, days: 0, divisionsPerDay: 1048576 }
1926
+ },
1927
+ {
1928
+ region: -18,
1929
+ zoom: { from: -59.09, to: -55.98 },
1930
+ low: 1,
1931
+ medium: 2,
1932
+ high: 3,
1933
+ all: 25,
1934
+ step: { months: 0, days: 0, divisionsPerDay: 524288 }
1935
+ },
1936
+ {
1937
+ region: -17,
1938
+ zoom: { from: -55.98, to: -52.87 },
1939
+ low: 1,
1940
+ medium: 2,
1941
+ high: 3,
1942
+ all: 25,
1943
+ step: { months: 0, days: 0, divisionsPerDay: 262144 }
1944
+ },
1945
+ {
1946
+ region: -16,
1947
+ zoom: { from: -52.87, to: -49.76 },
1948
+ low: 1,
1949
+ medium: 2,
1950
+ high: 3,
1951
+ all: 25,
1952
+ step: { months: 0, days: 0, divisionsPerDay: 131072 }
1953
+ },
1954
+ {
1955
+ region: -15,
1956
+ zoom: { from: -49.76, to: -46.65 },
1957
+ low: 1,
1958
+ medium: 2,
1959
+ high: 3,
1960
+ all: 25,
1961
+ step: { months: 0, days: 0, divisionsPerDay: 65536 }
1962
+ },
1963
+ {
1964
+ region: -14,
1965
+ zoom: { from: -46.65, to: -43.54 },
1966
+ low: 1,
1967
+ medium: 2,
1968
+ high: 3,
1969
+ all: 25,
1970
+ step: { months: 0, days: 0, divisionsPerDay: 32768 }
1971
+ },
1972
+ {
1973
+ region: -13,
1974
+ zoom: { from: -43.54, to: -40.43 },
1975
+ low: 1,
1976
+ medium: 2,
1977
+ high: 3,
1978
+ all: 25,
1979
+ step: { months: 0, days: 0, divisionsPerDay: 16384 }
1980
+ },
1981
+ {
1982
+ region: -12,
1983
+ zoom: { from: -40.43, to: -37.32 },
1984
+ low: 1,
1985
+ medium: 2,
1986
+ high: 3,
1987
+ all: 25,
1988
+ step: { months: 0, days: 0, divisionsPerDay: 8192 }
1989
+ },
1990
+ {
1991
+ region: -11,
1992
+ zoom: { from: -37.32, to: -34.21 },
1993
+ low: 1,
1994
+ medium: 2,
1995
+ high: 3,
1996
+ all: 25,
1997
+ step: { months: 0, days: 0, divisionsPerDay: 4096 }
1998
+ },
1999
+ {
2000
+ region: -10,
2001
+ zoom: { from: -34.21, to: -31.1 },
2002
+ low: 1,
2003
+ medium: 2,
2004
+ high: 3,
2005
+ all: 25,
2006
+ step: { months: 0, days: 0, divisionsPerDay: 2048 }
2007
+ },
2008
+ {
2009
+ region: -9,
2010
+ zoom: { from: -31.1, to: -27.99 },
2011
+ low: 1,
2012
+ medium: 2,
2013
+ high: 3,
2014
+ all: 25,
2015
+ step: { months: 0, days: 0, divisionsPerDay: 1024 }
2016
+ },
2017
+ {
2018
+ region: -8,
2019
+ zoom: { from: -27.99, to: -24.88 },
2020
+ low: 1,
2021
+ medium: 2,
2022
+ high: 3,
2023
+ all: 25,
2024
+ step: { months: 0, days: 0, divisionsPerDay: 512 }
2025
+ },
2026
+ {
2027
+ region: -7,
2028
+ zoom: { from: -24.88, to: -21.77 },
2029
+ low: 1,
2030
+ medium: 2,
2031
+ high: 3,
2032
+ all: 25,
2033
+ step: { months: 0, days: 0, divisionsPerDay: 256 }
2034
+ },
2035
+ {
2036
+ region: -6,
2037
+ zoom: { from: -21.77, to: -18.66 },
2038
+ low: 1,
2039
+ medium: 2,
2040
+ high: 3,
2041
+ all: 25,
2042
+ step: { months: 0, days: 0, divisionsPerDay: 128 }
2043
+ },
2044
+ {
2045
+ region: -5,
2046
+ zoom: { from: -18.66, to: -15.55 },
2047
+ low: 1,
2048
+ medium: 2,
2049
+ high: 3,
2050
+ all: 25,
2051
+ step: { months: 0, days: 0, divisionsPerDay: 64 }
2052
+ },
2053
+ {
2054
+ region: -4,
2055
+ zoom: { from: -15.55, to: -12.44 },
2056
+ low: 1,
2057
+ medium: 2,
2058
+ high: 3,
2059
+ all: 25,
2060
+ step: { months: 0, days: 0, divisionsPerDay: 32 }
2061
+ },
2062
+ {
2063
+ region: -3,
2064
+ zoom: { from: -12.44, to: -9.33 },
2065
+ low: 1,
2066
+ medium: 2,
2067
+ high: 3,
2068
+ all: 25,
2069
+ step: { months: 0, days: 0, divisionsPerDay: 16 }
2070
+ },
2071
+ {
2072
+ region: -2,
2073
+ zoom: { from: -9.33, to: -6.22 },
2074
+ low: 1,
2075
+ medium: 2,
2076
+ high: 3,
2077
+ all: 25,
2078
+ step: { months: 0, days: 0, divisionsPerDay: 8 }
2079
+ },
2080
+ {
2081
+ region: -1,
2082
+ zoom: { from: -6.22, to: -3.11 },
2083
+ low: 1,
2084
+ medium: 2,
2085
+ high: 3,
2086
+ all: 25,
2087
+ step: { months: 0, days: 0, divisionsPerDay: 4 }
2088
+ },
2089
+ {
2090
+ region: 0,
2091
+ zoom: { from: -3.11, to: 0 },
2092
+ low: 1,
2093
+ medium: 2,
2094
+ high: 3,
2095
+ all: 25,
2096
+ step: { months: 0, days: 0, divisionsPerDay: 2 }
2097
+ },
1623
2098
  {
1624
2099
  region: 1,
1625
2100
  zoom: { from: 0, to: 3.11 },
@@ -1995,9 +2470,13 @@ const constants = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePro
1995
2470
  PRECISION_CENTURY,
1996
2471
  PRECISION_DAY,
1997
2472
  PRECISION_DECADE,
2473
+ PRECISION_HOUR,
1998
2474
  PRECISION_MILLENNIUM,
1999
2475
  PRECISION_MILLION_YEARS,
2476
+ PRECISION_MILLISECOND,
2477
+ PRECISION_MINUTE,
2000
2478
  PRECISION_MONTH,
2479
+ PRECISION_SECOND,
2001
2480
  PRECISION_YEAR,
2002
2481
  RANGE_ALL,
2003
2482
  RANGE_SCREEN,
@@ -2010,9 +2489,13 @@ const constants = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePro
2010
2489
  ZOOM_CENTURY,
2011
2490
  ZOOM_DAY,
2012
2491
  ZOOM_DECADE,
2492
+ ZOOM_HOUR,
2013
2493
  ZOOM_MILLENNIUM,
2014
2494
  ZOOM_MILLION_YEARS,
2495
+ ZOOM_MILLISECOND,
2496
+ ZOOM_MINUTE,
2015
2497
  ZOOM_MONTH,
2498
+ ZOOM_SECOND,
2016
2499
  ZOOM_YEAR
2017
2500
  }, Symbol.toStringTag, { value: "Module" }));
2018
2501
  const CHART_RENDERERS = /* @__PURE__ */ new Map();
@@ -2085,7 +2568,7 @@ const CHART_DEFAULT_STYLE = {
2085
2568
  minor: {
2086
2569
  visible: true,
2087
2570
  color: "rgba(0, 0, 0, 0.035)",
2088
- thickness: 2,
2571
+ thickness: 1,
2089
2572
  minimumSpacing: 20
2090
2573
  }
2091
2574
  },
@@ -2699,10 +3182,10 @@ function getValueY(value, domain, plot) {
2699
3182
  return plot.bottom - (value - domain.min) / (domain.max - domain.min) * plot.height;
2700
3183
  }
2701
3184
  __name(getValueY, "getValueY");
2702
- function clamp$2(value, min, max) {
3185
+ function clamp$1(value, min, max) {
2703
3186
  return Math.max(min, Math.min(max, value));
2704
3187
  }
2705
- __name(clamp$2, "clamp$2");
3188
+ __name(clamp$1, "clamp$1");
2706
3189
  function getViewportRange(points, drawCycleContext) {
2707
3190
  const tokens = drawCycleContext && drawCycleContext.tokens ? drawCycleContext.tokens : null;
2708
3191
  if (!tokens || !tokens.start || !tokens.end || points.length === 0) {
@@ -2749,6 +3232,126 @@ function clipToRect(ctx, rect) {
2749
3232
  ctx.clip();
2750
3233
  }
2751
3234
  __name(clipToRect, "clipToRect");
3235
+ function clipLineSegmentToRect(from, to, rect) {
3236
+ const dx = to.x - from.x;
3237
+ const dy = to.y - from.y;
3238
+ let startRatio = 0;
3239
+ let endRatio = 1;
3240
+ let startBoundary = null;
3241
+ let endBoundary = null;
3242
+ const boundaries = [
3243
+ [-dx, from.x - rect.left],
3244
+ [dx, rect.right - from.x],
3245
+ [-dy, from.y - rect.top],
3246
+ [dy, rect.bottom - from.y]
3247
+ ];
3248
+ for (let i = 0; i < boundaries.length; i++) {
3249
+ const [direction, distance] = boundaries[i];
3250
+ if (direction === 0) {
3251
+ if (distance < 0) {
3252
+ return null;
3253
+ }
3254
+ continue;
3255
+ }
3256
+ const ratio = distance / direction;
3257
+ if (direction < 0) {
3258
+ if (ratio > startRatio) {
3259
+ startRatio = ratio;
3260
+ startBoundary = i;
3261
+ }
3262
+ } else if (ratio < endRatio) {
3263
+ endRatio = ratio;
3264
+ endBoundary = i;
3265
+ }
3266
+ if (startRatio > endRatio) {
3267
+ return null;
3268
+ }
3269
+ }
3270
+ const getClippedPoint = /* @__PURE__ */ __name((ratio, boundary) => {
3271
+ if (ratio === 0) {
3272
+ return from;
3273
+ }
3274
+ if (ratio === 1) {
3275
+ return to;
3276
+ }
3277
+ const point = {
3278
+ x: clamp$1(from.x + dx * ratio, rect.left, rect.right),
3279
+ y: clamp$1(from.y + dy * ratio, rect.top, rect.bottom)
3280
+ };
3281
+ if (boundary === 0) {
3282
+ point.x = rect.left;
3283
+ } else if (boundary === 1) {
3284
+ point.x = rect.right;
3285
+ } else if (boundary === 2) {
3286
+ point.y = rect.top;
3287
+ } else if (boundary === 3) {
3288
+ point.y = rect.bottom;
3289
+ }
3290
+ return point;
3291
+ }, "getClippedPoint");
3292
+ return {
3293
+ from: getClippedPoint(startRatio, startBoundary),
3294
+ to: getClippedPoint(endRatio, endBoundary)
3295
+ };
3296
+ }
3297
+ __name(clipLineSegmentToRect, "clipLineSegmentToRect");
3298
+ function pointsEqual(left, right) {
3299
+ return left && right && left.x === right.x && left.y === right.y;
3300
+ }
3301
+ __name(pointsEqual, "pointsEqual");
3302
+ function clipPolygonAgainstBoundary(points, isInside2, getIntersection) {
3303
+ if (points.length === 0) {
3304
+ return points;
3305
+ }
3306
+ const clipped = [];
3307
+ let previous = points[points.length - 1];
3308
+ let previousInside = isInside2(previous);
3309
+ for (let i = 0; i < points.length; i++) {
3310
+ const current = points[i];
3311
+ const currentInside = isInside2(current);
3312
+ if (currentInside) {
3313
+ if (!previousInside) {
3314
+ clipped.push(getIntersection(previous, current));
3315
+ }
3316
+ clipped.push(current);
3317
+ } else if (previousInside) {
3318
+ clipped.push(getIntersection(previous, current));
3319
+ }
3320
+ previous = current;
3321
+ previousInside = currentInside;
3322
+ }
3323
+ return clipped;
3324
+ }
3325
+ __name(clipPolygonAgainstBoundary, "clipPolygonAgainstBoundary");
3326
+ function clipPolygonToRect(points, rect) {
3327
+ const clipVertical = /* @__PURE__ */ __name((polygon, boundary, keepGreater) => clipPolygonAgainstBoundary(
3328
+ polygon,
3329
+ (point) => keepGreater ? point.x >= boundary : point.x <= boundary,
3330
+ (from, to) => {
3331
+ const ratio = (boundary - from.x) / (to.x - from.x);
3332
+ return {
3333
+ x: boundary,
3334
+ y: clamp$1(from.y + (to.y - from.y) * ratio, rect.top, rect.bottom)
3335
+ };
3336
+ }
3337
+ ), "clipVertical");
3338
+ const clipHorizontal = /* @__PURE__ */ __name((polygon, boundary, keepGreater) => clipPolygonAgainstBoundary(
3339
+ polygon,
3340
+ (point) => keepGreater ? point.y >= boundary : point.y <= boundary,
3341
+ (from, to) => {
3342
+ const ratio = (boundary - from.y) / (to.y - from.y);
3343
+ return {
3344
+ x: clamp$1(from.x + (to.x - from.x) * ratio, rect.left, rect.right),
3345
+ y: boundary
3346
+ };
3347
+ }
3348
+ ), "clipHorizontal");
3349
+ let clipped = clipVertical(points, rect.left, true);
3350
+ clipped = clipVertical(clipped, rect.right, false);
3351
+ clipped = clipHorizontal(clipped, rect.top, true);
3352
+ return clipHorizontal(clipped, rect.bottom, false);
3353
+ }
3354
+ __name(clipPolygonToRect, "clipPolygonToRect");
2752
3355
  function drawBackground(ctx, geometry, style) {
2753
3356
  if (!style.backgroundColor) {
2754
3357
  return;
@@ -2819,7 +3422,7 @@ function shouldDrawMinorXGridLines(timeline, minimumSpacing) {
2819
3422
  return unitSizeInPixels >= minimumSpacing;
2820
3423
  }
2821
3424
  __name(shouldDrawMinorXGridLines, "shouldDrawMinorXGridLines");
2822
- function drawXGridLineGroup(ctx, plot, markerTokens, options) {
3425
+ function drawXGridLineGroup(ctx, plot, markerTokens, options, minimumSpacing = 0) {
2823
3426
  if (!options || options.visible !== true || markerTokens.length === 0) {
2824
3427
  return;
2825
3428
  }
@@ -2828,6 +3431,9 @@ function drawXGridLineGroup(ctx, plot, markerTokens, options) {
2828
3431
  ctx.strokeStyle = options.color || "rgba(0, 0, 0, 0.08)";
2829
3432
  for (let i = 0; i < markerTokens.length; i++) {
2830
3433
  const token = markerTokens[i];
3434
+ if (token.adaptive && token.markerSpacingInPixels < minimumSpacing) {
3435
+ continue;
3436
+ }
2831
3437
  if (typeof token.drawX !== "number" || !Number.isFinite(token.drawX)) {
2832
3438
  continue;
2833
3439
  }
@@ -2845,10 +3451,16 @@ function drawXGridLines(ctx, plot, gridLine, timeline, drawCycleContext) {
2845
3451
  const xGrid = gridLine.x || {};
2846
3452
  const major = xGrid.major || {};
2847
3453
  const minor = xGrid.minor || {};
2848
- const minorMinimumSpacing = Math.max(0, getNumber(minor.minimumSpacing, 40));
3454
+ const minorMinimumSpacing = Math.max(0, getNumber(minor.minimumSpacing, 20));
2849
3455
  drawXGridLineGroup(ctx, plot, getMarkers(drawCycleContext, MAJOR_MARKER), major);
2850
3456
  if (shouldDrawMinorXGridLines(timeline, minorMinimumSpacing)) {
2851
- drawXGridLineGroup(ctx, plot, getMarkers(drawCycleContext, MINOR_MARKER), minor);
3457
+ drawXGridLineGroup(
3458
+ ctx,
3459
+ plot,
3460
+ getMarkers(drawCycleContext, MINOR_MARKER),
3461
+ minor,
3462
+ minorMinimumSpacing
3463
+ );
2852
3464
  }
2853
3465
  }
2854
3466
  __name(drawXGridLines, "drawXGridLines");
@@ -2909,22 +3521,32 @@ function drawSeriesLine(ctx, timeline, series, domain, plot, drawCycleContext, p
2909
3521
  if (Array.isArray(style.lineDash) && typeof ctx.setLineDash === "function") {
2910
3522
  ctx.setLineDash(style.lineDash);
2911
3523
  }
2912
- let hasPath = false;
3524
+ let previousPoint = null;
3525
+ let pathEnd = null;
2913
3526
  const range = getViewportRange(points, drawCycleContext);
2914
3527
  for (let i = range.start; i <= range.end; i++) {
2915
3528
  const point = points[i];
2916
3529
  if (typeof point.value !== "number" || !Number.isFinite(point.value)) {
2917
- hasPath = false;
3530
+ previousPoint = null;
3531
+ pathEnd = null;
2918
3532
  continue;
2919
3533
  }
2920
3534
  const x = timeline.getPixel(point.date, drawCycleContext);
2921
3535
  const y = getValueY(point.value, domain, plot);
2922
- if (!hasPath) {
2923
- ctx.moveTo(x, y);
2924
- hasPath = true;
2925
- } else {
2926
- ctx.lineTo(x, y);
3536
+ const currentPoint = { x, y };
3537
+ if (previousPoint && Number.isFinite(previousPoint.x) && Number.isFinite(previousPoint.y) && Number.isFinite(currentPoint.x) && Number.isFinite(currentPoint.y)) {
3538
+ const clippedSegment = clipLineSegmentToRect(previousPoint, currentPoint, plot);
3539
+ if (clippedSegment) {
3540
+ if (!pointsEqual(pathEnd, clippedSegment.from)) {
3541
+ ctx.moveTo(clippedSegment.from.x, clippedSegment.from.y);
3542
+ }
3543
+ ctx.lineTo(clippedSegment.to.x, clippedSegment.to.y);
3544
+ pathEnd = clippedSegment.to;
3545
+ } else {
3546
+ pathEnd = null;
3547
+ }
2927
3548
  }
3549
+ previousPoint = currentPoint;
2928
3550
  }
2929
3551
  ctx.stroke();
2930
3552
  if (typeof ctx.setLineDash === "function") {
@@ -2938,7 +3560,7 @@ function drawSeriesArea(ctx, timeline, stackedSeries, domain, plot, drawCycleCon
2938
3560
  const points = stackedSeries.points || [];
2939
3561
  const style = series.style || {};
2940
3562
  const areaStyle = style.area || {};
2941
- const opacity = clamp$2(getNumber(areaStyle.opacity, 0.2), 0, 1);
3563
+ const opacity = clamp$1(getNumber(areaStyle.opacity, 0.2), 0, 1);
2942
3564
  ctx.save();
2943
3565
  clipToRect(ctx, plot);
2944
3566
  ctx.fillStyle = areaStyle.fillColor || style.color || "#2563eb";
@@ -2949,11 +3571,23 @@ function drawSeriesArea(ctx, timeline, stackedSeries, domain, plot, drawCycleCon
2949
3571
  if (segment.length === 0) {
2950
3572
  return;
2951
3573
  }
3574
+ const polygon = [];
3575
+ for (let i = 0; i < segment.length; i++) {
3576
+ polygon.push({ x: segment[i].x, y: segment[i].y });
3577
+ }
2952
3578
  for (let i = segment.length - 1; i >= 0; i--) {
2953
- ctx.lineTo(segment[i].x, segment[i].baseY);
3579
+ polygon.push({ x: segment[i].x, y: segment[i].baseY });
3580
+ }
3581
+ const clippedPolygon = clipPolygonToRect(polygon, plot);
3582
+ if (clippedPolygon.length >= 3) {
3583
+ ctx.beginPath();
3584
+ ctx.moveTo(clippedPolygon[0].x, clippedPolygon[0].y);
3585
+ for (let i = 1; i < clippedPolygon.length; i++) {
3586
+ ctx.lineTo(clippedPolygon[i].x, clippedPolygon[i].y);
3587
+ }
3588
+ ctx.closePath();
3589
+ ctx.fill();
2954
3590
  }
2955
- ctx.closePath();
2956
- ctx.fill();
2957
3591
  segment = [];
2958
3592
  }, "closeSegment");
2959
3593
  for (let i = range.start; i <= range.end; i++) {
@@ -2964,15 +3598,8 @@ function drawSeriesArea(ctx, timeline, stackedSeries, domain, plot, drawCycleCon
2964
3598
  }
2965
3599
  const x = timeline.getPixel(point.date, drawCycleContext);
2966
3600
  const y = getValueY(point.value, domain, plot);
2967
- const baseY = clamp$2(getValueY(point.baseValue, domain, plot), plot.top, plot.bottom);
2968
- if (segment.length === 0) {
2969
- ctx.beginPath();
2970
- ctx.moveTo(x, baseY);
2971
- ctx.lineTo(x, y);
2972
- } else {
2973
- ctx.lineTo(x, y);
2974
- }
2975
- segment.push({ x, baseY });
3601
+ const baseY = clamp$1(getValueY(point.baseValue, domain, plot), plot.top, plot.bottom);
3602
+ segment.push({ x, y, baseY });
2976
3603
  }
2977
3604
  closeSegment();
2978
3605
  ctx.restore();
@@ -3088,8 +3715,16 @@ function normalizeDate(owner, input) {
3088
3715
  const month = parseFiniteInteger(input.month, 1);
3089
3716
  const day = parseFiniteInteger(input.day, 1);
3090
3717
  const precision = input.precision;
3091
- const dmy = new Dmy(year, month, day);
3092
- owner.getDmyFromInput(dmy);
3718
+ let dmy = new Dmy(
3719
+ year,
3720
+ month,
3721
+ day,
3722
+ parseFiniteInteger(input.hour, 0),
3723
+ parseFiniteInteger(input.minute, 0),
3724
+ parseFiniteInteger(input.second, 0),
3725
+ parseFiniteInteger(input.millisecond, 0)
3726
+ );
3727
+ dmy = owner.getDmyFromInput(dmy);
3093
3728
  return Dmy.CreateAsStartOfPeriod(dmy, precision);
3094
3729
  }
3095
3730
  __name(normalizeDate, "normalizeDate");
@@ -3145,11 +3780,20 @@ function setAssignedSeriesColor(chart, id, color) {
3145
3780
  chart._seriesColors.push({ id, color });
3146
3781
  }
3147
3782
  __name(setAssignedSeriesColor, "setAssignedSeriesColor");
3148
- function normalizeSeries(owner, chartStyle, seriesInput, index, assignedColor) {
3783
+ function getSeriesPalette(chartOptions) {
3784
+ const configuredColors = chartOptions && chartOptions.seriesColors;
3785
+ if (!Array.isArray(configuredColors)) {
3786
+ return CHART_DEFAULT_PALETTE;
3787
+ }
3788
+ const palette = configuredColors.filter((color) => typeof color === "string" && color.length > 0);
3789
+ return palette.length > 0 ? palette : CHART_DEFAULT_PALETTE;
3790
+ }
3791
+ __name(getSeriesPalette, "getSeriesPalette");
3792
+ function normalizeSeries(owner, chartStyle, seriesInput, index, assignedColor, palette) {
3149
3793
  const input = seriesInput || {};
3150
3794
  const baseStyle = deepMerge({}, CHART_DEFAULT_SERIES_STYLE, chartStyle.series || {});
3151
3795
  if (!baseStyle.color) {
3152
- baseStyle.color = assignedColor || CHART_DEFAULT_PALETTE[index % CHART_DEFAULT_PALETTE.length];
3796
+ baseStyle.color = assignedColor || palette[index % palette.length];
3153
3797
  }
3154
3798
  const points = Array.isArray(input.points) ? input.points.map((point) => normalizePoint(owner, point)).filter(Boolean) : [];
3155
3799
  points.sort((a, b) => a.date.compare(b.date));
@@ -3230,14 +3874,21 @@ Chart.prototype._resolveFromData = function() {
3230
3874
  {},
3231
3875
  CHART_DEFAULT_STYLE,
3232
3876
  chartOptions.defaultStyle || {},
3233
- { gridLine: chartOptions.gridLine || {} },
3234
3877
  getPropertyFromData(resolvedData, "style") || {}
3235
3878
  );
3236
3879
  const seriesInput = Array.isArray(resolvedData.series) ? resolvedData.series : [];
3880
+ const seriesPalette = getSeriesPalette(chartOptions);
3237
3881
  this.series = seriesInput.map((series, index) => {
3238
3882
  const seriesId = getSeriesId(series, index);
3239
3883
  const assignedColor = getAssignedSeriesColor(this, seriesId);
3240
- const normalizedSeries = normalizeSeries(this.owner, this.style, series, index, assignedColor);
3884
+ const normalizedSeries = normalizeSeries(
3885
+ this.owner,
3886
+ this.style,
3887
+ series,
3888
+ index,
3889
+ assignedColor,
3890
+ seriesPalette
3891
+ );
3241
3892
  if (hasSeriesInputColor(series) || !hasChartSeriesColor(this.style)) {
3242
3893
  setAssignedSeriesColor(this, seriesId, normalizedSeries.style.color);
3243
3894
  }
@@ -3395,10 +4046,10 @@ function isFiniteNumber(value) {
3395
4046
  return typeof value === "number" && Number.isFinite(value);
3396
4047
  }
3397
4048
  __name(isFiniteNumber, "isFiniteNumber");
3398
- function clamp$1(value, min, max) {
4049
+ function clamp(value, min, max) {
3399
4050
  return Math.min(Math.max(value, min), max);
3400
4051
  }
3401
- __name(clamp$1, "clamp$1");
4052
+ __name(clamp, "clamp");
3402
4053
  function getLaneArticleOptionImpact(articleOption) {
3403
4054
  const styleChanged = hasLaneArticlePath(articleOption, LANE_ARTICLE_STYLE_PATHS);
3404
4055
  const cacheChanged = styleChanged || hasLaneArticlePath(articleOption, LANE_ARTICLE_CACHE_ONLY_PATHS);
@@ -3468,10 +4119,10 @@ function computeResponsiveDistanceToBaseline(responsiveOptions, availableHeight,
3468
4119
  const rawMax = isFiniteNumber(layoutOptions.max) ? layoutOptions.max : Number.POSITIVE_INFINITY;
3469
4120
  const min = Math.max(0, Math.min(rawMin, rawMax));
3470
4121
  const max = Math.max(min, rawMax);
3471
- const boundedMin = clamp$1(min, 0, safeAvailableHeight);
3472
- const boundedMax = clamp$1(max, boundedMin, safeAvailableHeight);
4122
+ const boundedMin = clamp(min, 0, safeAvailableHeight);
4123
+ const boundedMax = clamp(max, boundedMin, safeAvailableHeight);
3473
4124
  const scaledDistance = Math.round(safeAvailableHeight * ratio);
3474
- return clamp$1(scaledDistance, boundedMin, boundedMax);
4125
+ return clamp(scaledDistance, boundedMin, boundedMax);
3475
4126
  }
3476
4127
  __name(computeResponsiveDistanceToBaseline, "computeResponsiveDistanceToBaseline");
3477
4128
  function resolveDistanceToBaselineValue(distanceToBaseline, availableHeight, laneLayoutActive, cardLayoutName) {
@@ -3501,7 +4152,7 @@ const ARTICLE_DATA_DEFAULTS = {
3501
4152
  };
3502
4153
  const DEFAULT_STYLE = {
3503
4154
  width: 150,
3504
- color: "#e9e9e9",
4155
+ color: "#e5e5e5",
3505
4156
  backgroundColor: "#fff",
3506
4157
  topRadius: 3,
3507
4158
  // portrait card layout only
@@ -3626,6 +4277,7 @@ const TIME_BAND_DEFAULT_STYLE = {
3626
4277
  offsetY: 0
3627
4278
  }
3628
4279
  };
4280
+ const DEFAULT_MAIN_LINE_COLOR = "#cdd2d8";
3629
4281
  const ENGINE_DEFAULTS = {
3630
4282
  width: 1e3,
3631
4283
  height: 500,
@@ -3661,7 +4313,8 @@ const ENGINE_DEFAULTS = {
3661
4313
  // density region size
3662
4314
  initial: 200,
3663
4315
  showMinorLabels: 48,
3664
- minimum: 8
4316
+ minimum: 8,
4317
+ minimumMajor: 120
3665
4318
  }
3666
4319
  },
3667
4320
  initialDate: {
@@ -3691,7 +4344,22 @@ const ENGINE_DEFAULTS = {
3691
4344
  visible: true,
3692
4345
  position: "bottom",
3693
4346
  size: 8,
3694
- showDateIndicators: true
4347
+ color: DEFAULT_MAIN_LINE_COLOR,
4348
+ opacity: 0.75,
4349
+ dateIndicators: {
4350
+ visible: true,
4351
+ shape: "rectangle",
4352
+ size: 4,
4353
+ color: "#666",
4354
+ opacity: 1,
4355
+ hiddenStyle: {
4356
+ opacity: 0.25
4357
+ },
4358
+ activeStyle: {
4359
+ color: "article",
4360
+ opacity: 1
4361
+ }
4362
+ }
3695
4363
  },
3696
4364
  draggingHighlight: {
3697
4365
  visible: true,
@@ -3742,6 +4410,10 @@ const ENGINE_DEFAULTS = {
3742
4410
  x: 4,
3743
4411
  y: 0
3744
4412
  },
4413
+ sticky: {
4414
+ enabled: "auto",
4415
+ offsetX: 4
4416
+ },
3745
4417
  bceText: " ʙᴄᴇ",
3746
4418
  thousandsSeparator: ",",
3747
4419
  yearPrefixes: {
@@ -3749,6 +4421,23 @@ const ENGINE_DEFAULTS = {
3749
4421
  Ma: { label: "Ma", value: 1e6, minDivision: 1e6 },
3750
4422
  Ga: { label: "Ga", value: 1e9, minDivision: 1e9 }
3751
4423
  }
4424
+ },
4425
+ parent: {
4426
+ visible: false,
4427
+ levels: "all",
4428
+ height: 20,
4429
+ font: "normal 14px Calibri",
4430
+ color: "#444",
4431
+ futureColor: "#ccc",
4432
+ textAlign: "start",
4433
+ offset: {
4434
+ x: 4,
4435
+ y: 0
4436
+ },
4437
+ sticky: {
4438
+ enabled: true,
4439
+ offsetX: 4
4440
+ }
3752
4441
  }
3753
4442
  },
3754
4443
  cursor: {
@@ -3776,6 +4465,7 @@ const ENGINE_DEFAULTS = {
3776
4465
  up: "edge",
3777
4466
  down: 0
3778
4467
  },
4468
+ seriesColors: [...CHART_DEFAULT_PALETTE],
3779
4469
  defaultData: {},
3780
4470
  defaultStyle: deepMerge({}, CHART_DEFAULT_STYLE),
3781
4471
  data: []
@@ -3870,6 +4560,46 @@ function warnDeprecatedOptionPath(fromPath, toPath) {
3870
4560
  Logger.warn(`DEPRECATION WARNING: ${fromPath} is deprecated. Use ${toPath} instead.`);
3871
4561
  }
3872
4562
  __name(warnDeprecatedOptionPath, "warnDeprecatedOptionPath");
4563
+ const DATE_INDICATOR_SHAPES = /* @__PURE__ */ new Set(["rectangle", "circle"]);
4564
+ function validateDateIndicatorStyle(style, optionPath, { allowVisible = false } = {}) {
4565
+ if (style === void 0) {
4566
+ return;
4567
+ }
4568
+ if (!style || typeof style !== "object" || Array.isArray(style)) {
4569
+ throw new TypeError(`${optionPath} must be an object`);
4570
+ }
4571
+ if (allowVisible && style.visible !== void 0 && typeof style.visible !== "boolean") {
4572
+ throw new TypeError(`${optionPath}.visible must be a boolean`);
4573
+ }
4574
+ if (style.shape !== void 0 && !DATE_INDICATOR_SHAPES.has(style.shape)) {
4575
+ throw new TypeError(`${optionPath}.shape must be "rectangle" or "circle"`);
4576
+ }
4577
+ if (style.size !== void 0 && (typeof style.size !== "number" || !Number.isFinite(style.size) || style.size <= 0)) {
4578
+ throw new TypeError(`${optionPath}.size must be a positive finite number`);
4579
+ }
4580
+ if (style.color !== void 0 && typeof style.color !== "string") {
4581
+ throw new TypeError(`${optionPath}.color must be a CSS colour string or "article"`);
4582
+ }
4583
+ if (style.opacity !== void 0 && (typeof style.opacity !== "number" || !Number.isFinite(style.opacity) || style.opacity < 0 || style.opacity > 1)) {
4584
+ throw new TypeError(`${optionPath}.opacity must be a finite number between 0 and 1`);
4585
+ }
4586
+ }
4587
+ __name(validateDateIndicatorStyle, "validateDateIndicatorStyle");
4588
+ function validateDateIndicatorOptions(inputOptions) {
4589
+ const dateIndicators = inputOptions?.style?.mainLine?.dateIndicators;
4590
+ validateDateIndicatorStyle(dateIndicators, "style.mainLine.dateIndicators", {
4591
+ allowVisible: true
4592
+ });
4593
+ validateDateIndicatorStyle(
4594
+ dateIndicators?.hiddenStyle,
4595
+ "style.mainLine.dateIndicators.hiddenStyle"
4596
+ );
4597
+ validateDateIndicatorStyle(
4598
+ dateIndicators?.activeStyle,
4599
+ "style.mainLine.dateIndicators.activeStyle"
4600
+ );
4601
+ }
4602
+ __name(validateDateIndicatorOptions, "validateDateIndicatorOptions");
3873
4603
  function migrateArticleDistanceToBaseline(inputArticleOptions, processedArticleOptions, optionPath) {
3874
4604
  if (inputArticleOptions?.distanceToMainLine === void 0) {
3875
4605
  return;
@@ -3894,10 +4624,7 @@ function normalizeDeprecatedOptionPath(path) {
3894
4624
  if (typeof path !== "string") {
3895
4625
  return path;
3896
4626
  }
3897
- const normalizedPath = path.replace(
3898
- /(^article\.|\.article\.)distanceToMainLine$/,
3899
- "$1distanceToBaseline.value"
3900
- );
4627
+ const normalizedPath = path.replace(/(^article\.|\.article\.)distanceToMainLine$/, "$1distanceToBaseline.value").replace(/^chart\.gridLine(?=\.|$)/, "chart.defaultStyle.gridLine");
3901
4628
  if (normalizedPath !== path) {
3902
4629
  warnDeprecatedOptionPath(path, normalizedPath);
3903
4630
  }
@@ -3926,7 +4653,20 @@ function handleDeprecatedOptions(inputOptions) {
3926
4653
  if (!inputOptions) {
3927
4654
  return inputOptions;
3928
4655
  }
4656
+ validateDateIndicatorOptions(inputOptions);
3929
4657
  const processedOptions = deepMerge({}, inputOptions);
4658
+ if (inputOptions.chart?.gridLine !== void 0) {
4659
+ warnDeprecatedOptionPath("chart.gridLine", "chart.defaultStyle.gridLine");
4660
+ if (!processedOptions.chart.defaultStyle) {
4661
+ processedOptions.chart.defaultStyle = {};
4662
+ }
4663
+ processedOptions.chart.defaultStyle.gridLine = deepMerge(
4664
+ {},
4665
+ inputOptions.chart.gridLine,
4666
+ inputOptions.chart.defaultStyle?.gridLine || {}
4667
+ );
4668
+ delete processedOptions.chart.gridLine;
4669
+ }
3930
4670
  if (inputOptions.article?.distanceToMainLine !== void 0) {
3931
4671
  if (!processedOptions.article) {
3932
4672
  processedOptions.article = {};
@@ -4202,12 +4942,16 @@ TimeBand.prototype.setupByReceivedData = function(receivedData) {
4202
4942
  this.isDataLoaded = true;
4203
4943
  };
4204
4944
  TimeBand.prototype._setupPeriod = function(data) {
4205
- const fromDmy = new Dmy(
4945
+ let fromDmy = new Dmy(
4206
4946
  parseInt(getPropertyFromData(data, "from.year")),
4207
4947
  parseInt(getPropertyFromData(data, "from.month")),
4208
- parseInt(getPropertyFromData(data, "from.day"))
4948
+ parseInt(getPropertyFromData(data, "from.day")),
4949
+ parseInt(getPropertyFromData(data, "from.hour")),
4950
+ parseInt(getPropertyFromData(data, "from.minute")),
4951
+ parseInt(getPropertyFromData(data, "from.second")),
4952
+ parseInt(getPropertyFromData(data, "from.millisecond"))
4209
4953
  );
4210
- this.owner.getDmyFromInput(fromDmy);
4954
+ fromDmy = this.owner.getDmyFromInput(fromDmy);
4211
4955
  const fromPrecision = getPropertyFromData(data, "from.precision");
4212
4956
  const isToPresentProp = getPropertyFromData(data, "isToPresent");
4213
4957
  const isToPresent = isToPresentProp === true || isToPresentProp === 1;
@@ -4219,17 +4963,21 @@ TimeBand.prototype._setupPeriod = function(data) {
4219
4963
  toDmy = new Dmy(
4220
4964
  parseInt(getPropertyFromData(data, "to.year")),
4221
4965
  parseInt(getPropertyFromData(data, "to.month")),
4222
- parseInt(getPropertyFromData(data, "to.day"))
4966
+ parseInt(getPropertyFromData(data, "to.day")),
4967
+ parseInt(getPropertyFromData(data, "to.hour")),
4968
+ parseInt(getPropertyFromData(data, "to.minute")),
4969
+ parseInt(getPropertyFromData(data, "to.second")),
4970
+ parseInt(getPropertyFromData(data, "to.millisecond"))
4223
4971
  );
4224
- this.owner.getDmyFromInput(toDmy);
4972
+ toDmy = this.owner.getDmyFromInput(toDmy);
4225
4973
  toPrecision = getPropertyFromData(data, "to.precision");
4226
4974
  } else {
4227
4975
  toDmy = fromDmy;
4228
4976
  toPrecision = fromPrecision;
4229
4977
  }
4230
4978
  const from = Dmy.CreateAsStartOfPeriod(fromDmy, fromPrecision);
4231
- const to = Dmy.CreateAsEndOfPeriod(toDmy, toPrecision);
4232
- this.period = { from, to: to.getNextDay(), isToPresent };
4979
+ const to = Dmy.CreateAsExclusiveEndOfPeriod(toDmy, toPrecision);
4980
+ this.period = { from, to, isToPresent };
4233
4981
  };
4234
4982
  TimeBand.prototype.overlapsInclusiveWithDates = function(from, to) {
4235
4983
  return !(this.period.from.isAfter(to) || from.isAfter(this.period.to));
@@ -4326,16 +5074,6 @@ function findMinIndex(arr, property) {
4326
5074
  return minIndex;
4327
5075
  }
4328
5076
  __name(findMinIndex, "findMinIndex");
4329
- function clamp(value, min, max) {
4330
- if (value < min) {
4331
- return min;
4332
- }
4333
- if (value > max) {
4334
- return max;
4335
- }
4336
- return value;
4337
- }
4338
- __name(clamp, "clamp");
4339
5077
  function isScreenAutoStackingRange(range) {
4340
5078
  return range === "screen" || range === 1;
4341
5079
  }
@@ -5014,6 +5752,151 @@ const _ImageLoader = class _ImageLoader {
5014
5752
  };
5015
5753
  __name(_ImageLoader, "ImageLoader");
5016
5754
  let ImageLoader = _ImageLoader;
5755
+ const SUBDAY_UNIT_MILLISECONDS = {
5756
+ [ZOOM_MILLISECOND]: 1,
5757
+ [ZOOM_SECOND]: MILLISECONDS_PER_SECOND,
5758
+ [ZOOM_MINUTE]: MILLISECONDS_PER_MINUTE,
5759
+ [ZOOM_HOUR]: MILLISECONDS_PER_HOUR
5760
+ };
5761
+ const ADAPTIVE_MINOR_INTERVALS = {
5762
+ [ZOOM_HOUR]: [12, 6, 3, 1],
5763
+ [ZOOM_MINUTE]: [30, 10, 5, 1],
5764
+ [ZOOM_SECOND]: [30, 10, 5, 1],
5765
+ [ZOOM_MILLISECOND]: [500, 100, 50, 10, 5, 1]
5766
+ };
5767
+ const SUBDAY_VISUAL_SCALES = [
5768
+ {
5769
+ majorUnit: ZOOM_DAY,
5770
+ majorMilliseconds: MILLISECONDS_PER_DAY,
5771
+ minorUnit: ZOOM_HOUR
5772
+ },
5773
+ {
5774
+ majorUnit: ZOOM_HOUR,
5775
+ majorMilliseconds: MILLISECONDS_PER_HOUR,
5776
+ minorUnit: ZOOM_MINUTE
5777
+ },
5778
+ {
5779
+ majorUnit: ZOOM_MINUTE,
5780
+ majorMilliseconds: MILLISECONDS_PER_MINUTE,
5781
+ minorUnit: ZOOM_SECOND
5782
+ },
5783
+ {
5784
+ majorUnit: ZOOM_SECOND,
5785
+ majorMilliseconds: MILLISECONDS_PER_SECOND,
5786
+ minorUnit: ZOOM_MILLISECOND
5787
+ }
5788
+ ];
5789
+ const PARENT_CONTEXT_MAXIMUM_LEVEL = ZOOM_CENTURY;
5790
+ function getZoomUnitLengthInDays(unit) {
5791
+ if (unit < ZOOM_DAY) {
5792
+ return SUBDAY_UNIT_MILLISECONDS[unit] / MILLISECONDS_PER_DAY;
5793
+ }
5794
+ return unit === ZOOM_DAY ? 1 : null;
5795
+ }
5796
+ __name(getZoomUnitLengthInDays, "getZoomUnitLengthInDays");
5797
+ function formatSubdayVisualMajorLabel(dmy, unit, options) {
5798
+ if (unit === ZOOM_DAY) {
5799
+ return dmy.format("D MMM YYYY", options);
5800
+ }
5801
+ if (unit === ZOOM_SECOND) {
5802
+ return dmy.format("HH:mm:ss");
5803
+ }
5804
+ return dmy.format("HH:mm");
5805
+ }
5806
+ __name(formatSubdayVisualMajorLabel, "formatSubdayVisualMajorLabel");
5807
+ function formatSubdayMinorLabel(dmy, unit) {
5808
+ if (unit === ZOOM_HOUR || unit === ZOOM_MINUTE) {
5809
+ return dmy.format("HH:mm");
5810
+ }
5811
+ if (unit === ZOOM_SECOND) {
5812
+ return `:${dmy.format("ss")}`;
5813
+ }
5814
+ return `.${dmy.format("SSS")}`;
5815
+ }
5816
+ __name(formatSubdayMinorLabel, "formatSubdayMinorLabel");
5817
+ function ordinal(value, thousandsSeparator) {
5818
+ const lastTwoDigits = value % 100;
5819
+ let suffix;
5820
+ if (lastTwoDigits >= 11 && lastTwoDigits <= 13) {
5821
+ suffix = "th";
5822
+ } else {
5823
+ switch (value % 10) {
5824
+ case 1:
5825
+ suffix = "st";
5826
+ break;
5827
+ case 2:
5828
+ suffix = "nd";
5829
+ break;
5830
+ case 3:
5831
+ suffix = "rd";
5832
+ break;
5833
+ default:
5834
+ suffix = "th";
5835
+ }
5836
+ }
5837
+ const formattedValue = new Dmy(value, 1, 1).format("YYYY", {
5838
+ thousandsSeparator,
5839
+ bceText: ""
5840
+ });
5841
+ return `${formattedValue}${suffix}`;
5842
+ }
5843
+ __name(ordinal, "ordinal");
5844
+ function getParentIntervalStart(value, unit) {
5845
+ switch (unit) {
5846
+ case ZOOM_SECOND:
5847
+ return new Dmy(value.year, value.month, value.day, value.hour, value.minute, value.second);
5848
+ case ZOOM_MINUTE:
5849
+ return new Dmy(value.year, value.month, value.day, value.hour, value.minute);
5850
+ case ZOOM_HOUR:
5851
+ return new Dmy(value.year, value.month, value.day, value.hour);
5852
+ case ZOOM_DAY:
5853
+ return new Dmy(value.year, value.month, value.day);
5854
+ case ZOOM_MONTH:
5855
+ return new Dmy(value.year, value.month, 1);
5856
+ case ZOOM_YEAR:
5857
+ return new Dmy(value.year, 1, 1);
5858
+ case ZOOM_DECADE: {
5859
+ if (value.year > 0) {
5860
+ return new Dmy(Math.floor(value.year / 10) * 10, 1, 1);
5861
+ }
5862
+ const displayedYear = 1 - value.year;
5863
+ const displayedDecade = Math.floor(displayedYear / 10) * 10;
5864
+ return new Dmy(1 - (displayedDecade + 9), 1, 1);
5865
+ }
5866
+ case ZOOM_CENTURY:
5867
+ return Dmy.CreateAsStartOfPeriod(value, "century");
5868
+ case ZOOM_MILLENNIUM:
5869
+ return Dmy.CreateAsStartOfPeriod(value, "millennium");
5870
+ default:
5871
+ return null;
5872
+ }
5873
+ }
5874
+ __name(getParentIntervalStart, "getParentIntervalStart");
5875
+ function getNextParentInterval(start, unit) {
5876
+ switch (unit) {
5877
+ case ZOOM_SECOND:
5878
+ return start.addSeconds(1);
5879
+ case ZOOM_MINUTE:
5880
+ return start.addMinutes(1);
5881
+ case ZOOM_HOUR:
5882
+ return start.addHours(1);
5883
+ case ZOOM_DAY:
5884
+ return start.getNextDay();
5885
+ case ZOOM_MONTH:
5886
+ return start.getNextMonth();
5887
+ case ZOOM_YEAR:
5888
+ return start.addYears(1);
5889
+ case ZOOM_DECADE:
5890
+ return start.addYears(10);
5891
+ case ZOOM_CENTURY:
5892
+ return Dmy.CreateAsExclusiveEndOfPeriod(start, "century");
5893
+ case ZOOM_MILLENNIUM:
5894
+ return Dmy.CreateAsExclusiveEndOfPeriod(start, "millennium");
5895
+ default:
5896
+ return null;
5897
+ }
5898
+ }
5899
+ __name(getNextParentInterval, "getNextParentInterval");
5017
5900
  const _TimescaleManager = class _TimescaleManager {
5018
5901
  constructor(owner, zoomOptions) {
5019
5902
  this.owner = owner;
@@ -5022,6 +5905,9 @@ const _TimescaleManager = class _TimescaleManager {
5022
5905
  this.startToken = null;
5023
5906
  this.unit = ZOOM_DAY;
5024
5907
  this.unitSizeInPixels = 100;
5908
+ this.adaptiveMinorUnit = null;
5909
+ this.adaptiveMinorInterval = null;
5910
+ this.adaptiveMinorMarkerSpacingInPixels = null;
5025
5911
  this.unitLengthInYears = 1;
5026
5912
  this.zoomChangedHandlers = [];
5027
5913
  this.yearPrefix = {
@@ -5030,6 +5916,7 @@ const _TimescaleManager = class _TimescaleManager {
5030
5916
  major: false
5031
5917
  };
5032
5918
  this.unitLevels = [];
5919
+ this.unitOrder = [];
5033
5920
  }
5034
5921
  };
5035
5922
  __name(_TimescaleManager, "TimescaleManager");
@@ -5037,7 +5924,7 @@ let TimescaleManager = _TimescaleManager;
5037
5924
  TimescaleManager.prototype.getUnitSizeInPixelsForZoom = function(zoom) {
5038
5925
  const zoomRatio = this.zoomOptions.ratio;
5039
5926
  const currentUnitSettings = this.unitLevels[this.unit];
5040
- const zoomStart = currentUnitSettings.zoomStart;
5927
+ const zoomStart = currentUnitSettings.referenceZoom ?? currentUnitSettings.zoomStart;
5041
5928
  const startUnitSize = currentUnitSettings.startUnitSize;
5042
5929
  return startUnitSize * Math.pow(zoomRatio, zoom - zoomStart);
5043
5930
  };
@@ -5045,14 +5932,289 @@ TimescaleManager.prototype.getChangePoint = function(_unit) {
5045
5932
  return this.unitLevels[this.unit].zoomStart;
5046
5933
  };
5047
5934
  TimescaleManager.prototype.getUnitFromZoom = function(zoom) {
5048
- const unitLevels = this.unitLevels;
5049
- for (let i = unitLevels.length - 1; i >= 0; i--) {
5050
- if (zoom >= unitLevels[i].zoomStart) {
5051
- return i;
5935
+ for (let i = this.unitOrder.length - 1; i >= 0; i--) {
5936
+ const unit = this.unitOrder[i];
5937
+ if (zoom >= this.unitLevels[unit].zoomStart) {
5938
+ return unit;
5052
5939
  }
5053
5940
  }
5054
5941
  throw new Error(`Zoom ${zoom} is out of range`);
5055
5942
  };
5943
+ TimescaleManager.prototype.getAdaptiveMinorInterval = function(unit = this.unit, unitSizeInPixels = this.unitSizeInPixels) {
5944
+ const intervals = ADAPTIVE_MINOR_INTERVALS[unit];
5945
+ if (!intervals) {
5946
+ return 1;
5947
+ }
5948
+ const minimumMarkerSpacing = this.zoomOptions.unitSize.minimum;
5949
+ const minimumLabelSpacing = this.zoomOptions.unitSize.showMinorLabels ?? minimumMarkerSpacing;
5950
+ const minimumSpacing = Math.max(minimumMarkerSpacing, minimumLabelSpacing);
5951
+ for (let i = intervals.length - 1; i >= 0; i--) {
5952
+ if (unitSizeInPixels * intervals[i] >= minimumSpacing) {
5953
+ return intervals[i];
5954
+ }
5955
+ }
5956
+ return null;
5957
+ };
5958
+ TimescaleManager.prototype.getSubdayVisualMarkerLayout = function() {
5959
+ if (this.zoomOptions.minimum >= ZOOM_DAY || this.zoom > ZOOM_DAY || this.unit > ZOOM_DAY) {
5960
+ return null;
5961
+ }
5962
+ const unitMilliseconds = this.unit === ZOOM_DAY ? MILLISECONDS_PER_DAY : SUBDAY_UNIT_MILLISECONDS[this.unit];
5963
+ const pixelsPerMillisecond = this.unitSizeInPixels / unitMilliseconds;
5964
+ const minimumMajorSpacing = this.zoomOptions.unitSize.minimumMajor ?? 120;
5965
+ let scale = null;
5966
+ for (let i = SUBDAY_VISUAL_SCALES.length - 1; i >= 0; i--) {
5967
+ const candidate = SUBDAY_VISUAL_SCALES[i];
5968
+ if (candidate.majorMilliseconds * pixelsPerMillisecond >= minimumMajorSpacing) {
5969
+ scale = candidate;
5970
+ break;
5971
+ }
5972
+ }
5973
+ if (!scale) {
5974
+ return null;
5975
+ }
5976
+ const minorUnitSizeInPixels = SUBDAY_UNIT_MILLISECONDS[scale.minorUnit] * pixelsPerMillisecond;
5977
+ const minorInterval = this.getAdaptiveMinorInterval(scale.minorUnit, minorUnitSizeInPixels);
5978
+ return {
5979
+ ...scale,
5980
+ majorMarkerSpacingInPixels: scale.majorMilliseconds * pixelsPerMillisecond,
5981
+ minorInterval,
5982
+ minorMarkerSpacingInPixels: minorInterval ? minorUnitSizeInPixels * minorInterval : null,
5983
+ minorMilliseconds: minorInterval ? SUBDAY_UNIT_MILLISECONDS[scale.minorUnit] * minorInterval : null,
5984
+ pixelsPerMillisecond
5985
+ };
5986
+ };
5987
+ TimescaleManager.prototype.getParentContextScale = function() {
5988
+ const visualLayout = this.getSubdayVisualMarkerLayout();
5989
+ const levelUnit = visualLayout ? visualLayout.majorUnit : this.unit + 1;
5990
+ if (levelUnit < ZOOM_MILLISECOND || levelUnit > PARENT_CONTEXT_MAXIMUM_LEVEL) {
5991
+ return null;
5992
+ }
5993
+ return {
5994
+ level: this.zoomUnitToString(levelUnit),
5995
+ levelUnit,
5996
+ parentUnit: levelUnit <= ZOOM_HOUR ? ZOOM_DAY : levelUnit + 1
5997
+ };
5998
+ };
5999
+ TimescaleManager.prototype.isParentContextTextVisible = function(options) {
6000
+ if (options?.visible !== true) {
6001
+ return false;
6002
+ }
6003
+ const scale = this.getParentContextScale();
6004
+ if (!scale) {
6005
+ return false;
6006
+ }
6007
+ if (options.levels === "all") {
6008
+ return true;
6009
+ }
6010
+ return Array.isArray(options.levels) && options.levels.includes(scale.level);
6011
+ };
6012
+ TimescaleManager.prototype.formatParentContextLabel = function(value, unit) {
6013
+ const options = this.getDateLabelFormatOptions(MAJOR_MARKER);
6014
+ switch (unit) {
6015
+ case ZOOM_SECOND:
6016
+ case ZOOM_MINUTE:
6017
+ case ZOOM_HOUR:
6018
+ case ZOOM_DAY:
6019
+ return value.format("D MMM YYYY", options);
6020
+ case ZOOM_MONTH:
6021
+ return value.format("MMM YYYY", options);
6022
+ case ZOOM_YEAR:
6023
+ return value.format("YYYY", options);
6024
+ case ZOOM_DECADE: {
6025
+ const displayedYear = value.year <= 0 ? 1 - value.year : value.year;
6026
+ const decade = Math.floor(displayedYear / 10) * 10;
6027
+ const formattedDecade = new Dmy(decade, 1, 1).format("YYYY", {
6028
+ thousandsSeparator: options.thousandsSeparator,
6029
+ bceText: ""
6030
+ });
6031
+ return `${formattedDecade}s${value.year <= 0 ? options.bceText : ""}`;
6032
+ }
6033
+ case ZOOM_CENTURY:
6034
+ case ZOOM_MILLENNIUM: {
6035
+ const displayedYear = value.year <= 0 ? 1 - value.year : value.year;
6036
+ const periodSize = unit === ZOOM_CENTURY ? 100 : 1e3;
6037
+ const period = Math.ceil(displayedYear / periodSize);
6038
+ const periodName = unit === ZOOM_CENTURY ? "century" : "millennium";
6039
+ return `${ordinal(period, options.thousandsSeparator)} ${periodName}${value.year <= 0 ? options.bceText : ""}`;
6040
+ }
6041
+ default:
6042
+ return "";
6043
+ }
6044
+ };
6045
+ TimescaleManager.prototype.createParentContextTokens = function(visibleStart, width, repositionWindow) {
6046
+ const scale = this.getParentContextScale();
6047
+ if (!scale || !visibleStart || !this.startToken) {
6048
+ return [];
6049
+ }
6050
+ const tokens = [];
6051
+ let value = getParentIntervalStart(visibleStart, scale.parentUnit);
6052
+ let drawX = repositionWindow + this.getPixelsBetween(this.startToken.value, value);
6053
+ do {
6054
+ const nextValue = getNextParentInterval(value, scale.parentUnit);
6055
+ if (!nextValue) {
6056
+ break;
6057
+ }
6058
+ const nextDrawX = repositionWindow + this.getPixelsBetween(this.startToken.value, nextValue);
6059
+ tokens.push({
6060
+ drawX,
6061
+ label: this.formatParentContextLabel(value, scale.parentUnit),
6062
+ length: nextDrawX - drawX,
6063
+ unit: scale.parentUnit,
6064
+ value
6065
+ });
6066
+ value = nextValue;
6067
+ drawX = nextDrawX;
6068
+ } while (tokens.length < 1e3 && tokens[tokens.length - 1].drawX <= width);
6069
+ return tokens;
6070
+ };
6071
+ TimescaleManager.prototype.setAdaptiveMinorInterval = function() {
6072
+ const layout = this.getSubdayVisualMarkerLayout();
6073
+ if (!layout) {
6074
+ this.adaptiveMinorUnit = null;
6075
+ this.adaptiveMinorInterval = null;
6076
+ this.adaptiveMinorMarkerSpacingInPixels = null;
6077
+ return;
6078
+ }
6079
+ this.adaptiveMinorUnit = layout.minorUnit;
6080
+ this.adaptiveMinorInterval = layout.minorInterval;
6081
+ this.adaptiveMinorMarkerSpacingInPixels = layout.minorMarkerSpacingInPixels;
6082
+ };
6083
+ TimescaleManager.prototype.createSubdayVisualMarkers = function(width, repositionWindow, layout = this.getSubdayVisualMarkerLayout()) {
6084
+ const markers = {
6085
+ [MAJOR_MARKER]: [],
6086
+ [MINOR_MARKER]: []
6087
+ };
6088
+ if (!layout || !this.startToken) {
6089
+ return markers;
6090
+ }
6091
+ const buffer = 150;
6092
+ const stepMilliseconds = layout.minorMilliseconds || layout.majorMilliseconds;
6093
+ const millisecondsToLeftEdge = Math.floor(
6094
+ (-buffer - repositionWindow) / layout.pixelsPerMillisecond
6095
+ );
6096
+ const approximateStart = this.startToken.value.addMilliseconds(millisecondsToLeftEdge);
6097
+ const dayStart = new Dmy(approximateStart.year, approximateStart.month, approximateStart.day);
6098
+ const firstOffset = Math.floor(approximateStart.getMillisecondsOfDay() / stepMilliseconds) * stepMilliseconds;
6099
+ let value = dayStart.addMilliseconds(firstOffset);
6100
+ const showMinorLabels = layout.minorMarkerSpacingInPixels >= this.zoomOptions.unitSize.showMinorLabels;
6101
+ const createMajorMarker = /* @__PURE__ */ __name((majorValue) => ({
6102
+ drawX: repositionWindow + this.getPixelsBetween(this.startToken.value, majorValue),
6103
+ label: formatSubdayVisualMajorLabel(
6104
+ majorValue,
6105
+ layout.majorUnit,
6106
+ this.getDateLabelFormatOptions(MAJOR_MARKER)
6107
+ ),
6108
+ length: layout.majorMarkerSpacingInPixels,
6109
+ markerSpacingInPixels: layout.majorMarkerSpacingInPixels,
6110
+ type: MAJOR_MARKER,
6111
+ unit: layout.majorUnit,
6112
+ value: majorValue,
6113
+ visual: true
6114
+ }), "createMajorMarker");
6115
+ const owningMajorOffset = Math.floor(approximateStart.getMillisecondsOfDay() / layout.majorMilliseconds) * layout.majorMilliseconds;
6116
+ const owningMajorValue = dayStart.addMilliseconds(owningMajorOffset);
6117
+ if (owningMajorValue.isAfter(value) === false && !owningMajorValue.isSame(value)) {
6118
+ markers[MAJOR_MARKER].push(createMajorMarker(owningMajorValue));
6119
+ }
6120
+ while (true) {
6121
+ const drawX = repositionWindow + this.getPixelsBetween(this.startToken.value, value);
6122
+ if (drawX > width + buffer) {
6123
+ break;
6124
+ }
6125
+ const millisecondsOfDay = value.getMillisecondsOfDay();
6126
+ const isMajor = millisecondsOfDay % layout.majorMilliseconds === 0;
6127
+ if (isMajor) {
6128
+ markers[MAJOR_MARKER].push(createMajorMarker(value));
6129
+ } else if (layout.minorMilliseconds) {
6130
+ markers[MINOR_MARKER].push({
6131
+ adaptive: layout.minorInterval !== 1,
6132
+ drawX,
6133
+ label: showMinorLabels ? formatSubdayMinorLabel(value, layout.minorUnit) : void 0,
6134
+ length: layout.minorMarkerSpacingInPixels,
6135
+ markerSpacingInPixels: layout.minorMarkerSpacingInPixels,
6136
+ type: MINOR_MARKER,
6137
+ unit: layout.minorUnit,
6138
+ value,
6139
+ visual: true
6140
+ });
6141
+ }
6142
+ value = value.addMilliseconds(stepMilliseconds);
6143
+ }
6144
+ return markers;
6145
+ };
6146
+ TimescaleManager.prototype.createHourToken = function(dmy) {
6147
+ const token = {
6148
+ unit: ZOOM_HOUR,
6149
+ value: dmy,
6150
+ length: this.unitSizeInPixels
6151
+ };
6152
+ const unitSettings = this.unitLevels[ZOOM_HOUR];
6153
+ if (dmy.hour === 0) {
6154
+ token.label = dmy.format("D MMM YYYY", this.getDateLabelFormatOptions(MAJOR_MARKER));
6155
+ token.type = MAJOR_MARKER;
6156
+ } else {
6157
+ token.type = MINOR_MARKER;
6158
+ if (this.zoom < unitSettings.zoomHideMinorLabels) {
6159
+ token.label = formatSubdayMinorLabel(dmy, ZOOM_HOUR);
6160
+ }
6161
+ }
6162
+ return token;
6163
+ };
6164
+ TimescaleManager.prototype.createMinuteToken = function(dmy) {
6165
+ const token = {
6166
+ unit: ZOOM_MINUTE,
6167
+ value: dmy,
6168
+ length: this.unitSizeInPixels
6169
+ };
6170
+ const unitSettings = this.unitLevels[ZOOM_MINUTE];
6171
+ if (dmy.minute === 0) {
6172
+ token.label = dmy.format("HH:mm");
6173
+ token.type = MAJOR_MARKER;
6174
+ } else {
6175
+ token.type = MINOR_MARKER;
6176
+ if (this.zoom < unitSettings.zoomHideMinorLabels) {
6177
+ token.label = formatSubdayMinorLabel(dmy, ZOOM_MINUTE);
6178
+ }
6179
+ }
6180
+ return token;
6181
+ };
6182
+ TimescaleManager.prototype.createSecondToken = function(dmy) {
6183
+ const token = {
6184
+ unit: ZOOM_SECOND,
6185
+ value: dmy,
6186
+ length: this.unitSizeInPixels
6187
+ };
6188
+ const unitSettings = this.unitLevels[ZOOM_SECOND];
6189
+ if (dmy.second === 0) {
6190
+ token.label = dmy.format("HH:mm:ss");
6191
+ token.type = MAJOR_MARKER;
6192
+ } else {
6193
+ token.type = MINOR_MARKER;
6194
+ if (this.zoom < unitSettings.zoomHideMinorLabels) {
6195
+ token.label = formatSubdayMinorLabel(dmy, ZOOM_SECOND);
6196
+ }
6197
+ }
6198
+ return token;
6199
+ };
6200
+ TimescaleManager.prototype.createMillisecondToken = function(dmy) {
6201
+ const token = {
6202
+ unit: ZOOM_MILLISECOND,
6203
+ value: dmy,
6204
+ length: this.unitSizeInPixels
6205
+ };
6206
+ const unitSettings = this.unitLevels[ZOOM_MILLISECOND];
6207
+ if (dmy.millisecond === 0) {
6208
+ token.label = dmy.format("HH:mm:ss.SSS");
6209
+ token.type = MAJOR_MARKER;
6210
+ } else {
6211
+ token.type = MINOR_MARKER;
6212
+ if (this.zoom < unitSettings.zoomHideMinorLabels) {
6213
+ token.label = formatSubdayMinorLabel(dmy, ZOOM_MILLISECOND);
6214
+ }
6215
+ }
6216
+ return token;
6217
+ };
5056
6218
  TimescaleManager.prototype.getDateLabelFormatOptions = function(tokenType) {
5057
6219
  tokenType = tokenType === MAJOR_MARKER ? "major" : "minor";
5058
6220
  const dateLabelFormatOptions = this.owner.options.style.dateLabel[tokenType];
@@ -5142,6 +6304,7 @@ TimescaleManager.prototype.setZoom = function(value) {
5142
6304
  this.zoom = nextZoom;
5143
6305
  this.unit = this.getUnitFromZoom(this.zoom);
5144
6306
  this.unitSizeInPixels = this.getUnitSizeInPixelsForZoom(this.zoom);
6307
+ this.setAdaptiveMinorInterval();
5145
6308
  let unitLengthInYears = 0;
5146
6309
  if (this.unit >= ZOOM_YEAR) {
5147
6310
  const unitYearPowerOfTen = this.unit - ZOOM_YEAR;
@@ -5160,7 +6323,7 @@ TimescaleManager.prototype.setZoom = function(value) {
5160
6323
  TimescaleManager.prototype.getPixelsBetween = function(a, b) {
5161
6324
  const unit = this.unit;
5162
6325
  if (unit <= ZOOM_DAY) {
5163
- return a.getDaysTo(b) * this.unitSizeInPixels;
6326
+ return a.getDaysTo(b) * this.unitSizeInPixels / getZoomUnitLengthInDays(unit);
5164
6327
  }
5165
6328
  if (unit === ZOOM_MONTH) {
5166
6329
  return a.getMonthsTo(b) * this.unitSizeInPixels;
@@ -5177,9 +6340,27 @@ TimescaleManager.prototype.setStartDate = function(from) {
5177
6340
  bcMove = 1;
5178
6341
  }
5179
6342
  switch (this.unit) {
5180
- case ZOOM_DAY:
5181
- this.startToken = this.createDayToken(from);
6343
+ case ZOOM_MILLISECOND:
6344
+ this.startToken = this.createMillisecondToken(new Dmy(from));
5182
6345
  return 0;
6346
+ case ZOOM_SECOND: {
6347
+ const start = new Dmy(from.year, from.month, from.day, from.hour, from.minute, from.second);
6348
+ this.startToken = this.createSecondToken(start);
6349
+ return -this.getPixelsBetween(start, from);
6350
+ }
6351
+ case ZOOM_MINUTE: {
6352
+ const start = new Dmy(from.year, from.month, from.day, from.hour, from.minute);
6353
+ this.startToken = this.createMinuteToken(start);
6354
+ return -this.getPixelsBetween(start, from);
6355
+ }
6356
+ case ZOOM_HOUR: {
6357
+ const start = new Dmy(from.year, from.month, from.day, from.hour);
6358
+ this.startToken = this.createHourToken(start);
6359
+ return -this.getPixelsBetween(start, from);
6360
+ }
6361
+ case ZOOM_DAY:
6362
+ this.startToken = this.createDayToken(new Dmy(from.year, from.month, from.day));
6363
+ return -this.getPixelsBetween(this.startToken.value, from);
5183
6364
  case ZOOM_MONTH: {
5184
6365
  this.startToken = this.createMonthToken(new Dmy(from.year, from.month, 1));
5185
6366
  const unitSizeForDays = this.unitSizeInPixels / 30.44;
@@ -5202,6 +6383,14 @@ TimescaleManager.prototype.setStartDate = function(from) {
5202
6383
  };
5203
6384
  TimescaleManager.prototype.getStartTokenCloned = function() {
5204
6385
  switch (this.startToken.unit) {
6386
+ case ZOOM_MILLISECOND:
6387
+ return this.createMillisecondToken(this.startToken.value);
6388
+ case ZOOM_SECOND:
6389
+ return this.createSecondToken(this.startToken.value);
6390
+ case ZOOM_MINUTE:
6391
+ return this.createMinuteToken(this.startToken.value);
6392
+ case ZOOM_HOUR:
6393
+ return this.createHourToken(this.startToken.value);
5205
6394
  case ZOOM_DAY:
5206
6395
  return this.createDayToken(this.startToken.value);
5207
6396
  case ZOOM_MONTH:
@@ -5212,6 +6401,14 @@ TimescaleManager.prototype.getStartTokenCloned = function() {
5212
6401
  };
5213
6402
  TimescaleManager.prototype.getNext = function(token) {
5214
6403
  switch (token.unit) {
6404
+ case ZOOM_MILLISECOND:
6405
+ return this.createMillisecondToken(token.value.addMilliseconds(1));
6406
+ case ZOOM_SECOND:
6407
+ return this.createSecondToken(token.value.addSeconds(1));
6408
+ case ZOOM_MINUTE:
6409
+ return this.createMinuteToken(token.value.addMinutes(1));
6410
+ case ZOOM_HOUR:
6411
+ return this.createHourToken(token.value.addHours(1));
5215
6412
  case ZOOM_DAY:
5216
6413
  return this.createDayToken(token.value.getNextDay());
5217
6414
  case ZOOM_MONTH:
@@ -5230,6 +6427,14 @@ TimescaleManager.prototype.getNext = function(token) {
5230
6427
  };
5231
6428
  TimescaleManager.prototype.getPrevious = function(token) {
5232
6429
  switch (token.unit) {
6430
+ case ZOOM_MILLISECOND:
6431
+ return this.createMillisecondToken(token.value.addMilliseconds(-1));
6432
+ case ZOOM_SECOND:
6433
+ return this.createSecondToken(token.value.addSeconds(-1));
6434
+ case ZOOM_MINUTE:
6435
+ return this.createMinuteToken(token.value.addMinutes(-1));
6436
+ case ZOOM_HOUR:
6437
+ return this.createHourToken(token.value.addHours(-1));
5233
6438
  case ZOOM_DAY:
5234
6439
  return this.createDayToken(token.value.getPreviousDay());
5235
6440
  case ZOOM_MONTH:
@@ -5252,6 +6457,14 @@ TimescaleManager.prototype.getPreviousNth = function(token, n) {
5252
6457
  };
5253
6458
  TimescaleManager.prototype.zoomUnitToString = function(unit) {
5254
6459
  switch (unit) {
6460
+ case ZOOM_MILLISECOND:
6461
+ return "millisecond";
6462
+ case ZOOM_SECOND:
6463
+ return "second";
6464
+ case ZOOM_MINUTE:
6465
+ return "minute";
6466
+ case ZOOM_HOUR:
6467
+ return "hour";
5255
6468
  case ZOOM_DAY:
5256
6469
  return "day";
5257
6470
  case ZOOM_MONTH:
@@ -5282,9 +6495,14 @@ TimescaleManager.prototype.zoomUnitToString = function(unit) {
5282
6495
  };
5283
6496
  TimescaleManager.prototype.setUnitLevels = function() {
5284
6497
  this.unitLevels = [];
6498
+ this.unitOrder = [];
5285
6499
  const minUnitSize = this.zoomOptions.unitSize.minimum;
5286
6500
  const showMinorLabelUnitSize = this.zoomOptions.unitSize.showMinorLabels;
5287
6501
  const dayZoomStart = 0;
6502
+ const addUnit = /* @__PURE__ */ __name((unit, settings) => {
6503
+ this.unitLevels[unit] = settings;
6504
+ this.unitOrder.push(unit);
6505
+ }, "addUnit");
5288
6506
  const hideLabelsZoomChangeFromEnd = this.getZoomChangeByDistance(
5289
6507
  showMinorLabelUnitSize,
5290
6508
  minUnitSize
@@ -5299,12 +6517,41 @@ TimescaleManager.prototype.setUnitLevels = function() {
5299
6517
  zoomHideMinorLabels: dayZoomHideMinorLabels,
5300
6518
  zoomEnd: dayZoomEnd
5301
6519
  };
5302
- this.unitLevels.push(daySettings);
6520
+ addUnit(ZOOM_DAY, daySettings);
6521
+ const hourSizeAtDayBoundary = dayStartUnitSize / 24;
6522
+ const hourZoomStart = this.getZoomChangeByDistance(hourSizeAtDayBoundary, minUnitSize * 60);
6523
+ addUnit(ZOOM_HOUR, {
6524
+ startUnitSize: minUnitSize * 60,
6525
+ zoomStart: hourZoomStart,
6526
+ zoomEnd: dayZoomStart,
6527
+ zoomHideMinorLabels: hourZoomStart + this.getZoomChangeByDistance(minUnitSize * 60, showMinorLabelUnitSize)
6528
+ });
6529
+ const minuteZoomStart = hourZoomStart + this.getZoomChangeByDistance(minUnitSize, minUnitSize * 60);
6530
+ addUnit(ZOOM_MINUTE, {
6531
+ startUnitSize: minUnitSize * 60,
6532
+ zoomStart: minuteZoomStart,
6533
+ zoomEnd: hourZoomStart,
6534
+ zoomHideMinorLabels: minuteZoomStart + this.getZoomChangeByDistance(minUnitSize * 60, showMinorLabelUnitSize)
6535
+ });
6536
+ const secondZoomStart = minuteZoomStart + this.getZoomChangeByDistance(minUnitSize, minUnitSize * 1e3);
6537
+ addUnit(ZOOM_SECOND, {
6538
+ startUnitSize: minUnitSize * 1e3,
6539
+ zoomStart: secondZoomStart,
6540
+ zoomEnd: minuteZoomStart,
6541
+ zoomHideMinorLabels: secondZoomStart + this.getZoomChangeByDistance(minUnitSize * 1e3, showMinorLabelUnitSize)
6542
+ });
6543
+ addUnit(ZOOM_MILLISECOND, {
6544
+ startUnitSize: minUnitSize,
6545
+ referenceZoom: secondZoomStart,
6546
+ zoomStart: Number.NEGATIVE_INFINITY,
6547
+ zoomEnd: secondZoomStart,
6548
+ zoomHideMinorLabels: secondZoomStart + this.getZoomChangeByDistance(minUnitSize, showMinorLabelUnitSize)
6549
+ });
5303
6550
  const monthStartUnitSize = minUnitSize * 31;
5304
6551
  const monthZoomSpan = this.getZoomChangeByDistance(monthStartUnitSize, minUnitSize);
5305
6552
  const monthZoomEnd = dayZoomEnd + monthZoomSpan;
5306
6553
  const monthZoomHideMinorLabels = monthZoomEnd - hideLabelsZoomChangeFromEnd;
5307
- this.unitLevels.push({
6554
+ addUnit(ZOOM_MONTH, {
5308
6555
  startUnitSize: monthStartUnitSize,
5309
6556
  zoomStart: dayZoomEnd,
5310
6557
  zoomHideMinorLabels: monthZoomHideMinorLabels,
@@ -5314,7 +6561,7 @@ TimescaleManager.prototype.setUnitLevels = function() {
5314
6561
  const yearZoomSpan = this.getZoomChangeByDistance(yearStartUnitSize, minUnitSize);
5315
6562
  const yearZoomEnd = monthZoomEnd + yearZoomSpan;
5316
6563
  const yearZoomHideMinorLabels = yearZoomEnd - hideLabelsZoomChangeFromEnd;
5317
- this.unitLevels.push({
6564
+ addUnit(ZOOM_YEAR, {
5318
6565
  startUnitSize: yearStartUnitSize,
5319
6566
  zoomStart: monthZoomEnd,
5320
6567
  zoomHideMinorLabels: yearZoomHideMinorLabels,
@@ -5331,7 +6578,7 @@ TimescaleManager.prototype.setUnitLevels = function() {
5331
6578
  zoomSpan = this.getZoomChangeByDistance(startUnitSize, minUnitSize);
5332
6579
  zoomEnd = zoomStart + zoomSpan;
5333
6580
  zoomHideMinorLabels = zoomEnd - hideLabelsZoomChangeFromEnd;
5334
- this.unitLevels.push({
6581
+ addUnit(ZOOM_YEAR + i, {
5335
6582
  startUnitSize,
5336
6583
  zoomStart,
5337
6584
  zoomHideMinorLabels,
@@ -5342,6 +6589,7 @@ TimescaleManager.prototype.setUnitLevels = function() {
5342
6589
  this.zoomOptions.maximum = Math.min(this.zoomOptions.maximum, zoomEnd);
5343
6590
  }
5344
6591
  }
6592
+ this.unitOrder.sort((a, b) => a - b);
5345
6593
  };
5346
6594
  TimescaleManager.prototype.getZoomChangeByDistance = function(startDistance, endDistance) {
5347
6595
  const zoomRatio = this.zoomOptions.ratio;
@@ -5787,6 +7035,8 @@ function getPinchDistance(tracker) {
5787
7035
  __name(getPinchDistance, "getPinchDistance");
5788
7036
  function Timeline(container, inputOptions) {
5789
7037
  const processedInputOptions = handleDeprecatedOptions(inputOptions);
7038
+ this._registeredArticleLayoutStyles = deepMerge({}, DEFAULT_OPTIONS.article.layoutStyles || {});
7039
+ this._explicitArticleOptions = deepMerge({}, processedInputOptions?.article || {});
5790
7040
  this.options = deepMerge({}, DEFAULT_OPTIONS, processedInputOptions);
5791
7041
  if (isCanvas(container)) {
5792
7042
  this.canvas = container;
@@ -5865,7 +7115,7 @@ function Timeline(container, inputOptions) {
5865
7115
  }
5866
7116
  this._isInitialized = false;
5867
7117
  this._hasPendingTimelineStateChange = false;
5868
- const zoom = this.options.zoom, startDate = this.getDmyFromInput(new Dmy(this.options.initialDate));
7118
+ const zoom = this.options.zoom, startDate = this.getDmyFromInput(this.options.initialDate);
5869
7119
  this.timescaleManager = new TimescaleManager(this, zoom);
5870
7120
  this.timescaleManager.setUnitLevels();
5871
7121
  this.timescaleManager.addZoomChangedHandler(this.zoomChanged, this);
@@ -5917,7 +7167,9 @@ Timeline.prototype.isInVicinity = function(event, vicinityParameters, relativePo
5917
7167
  return vicinityParameters;
5918
7168
  }
5919
7169
  const pos = relativePosition || this.getRelativePosition(event);
5920
- return pos.top > this.top - vicinityParameters.up && pos.top < this.top + vicinityParameters.down;
7170
+ const isWithinUpperBound = vicinityParameters.up === void 0 || pos.top > this.top - vicinityParameters.up;
7171
+ const isWithinLowerBound = vicinityParameters.down === void 0 || pos.top < this.top + vicinityParameters.down;
7172
+ return isWithinUpperBound && isWithinLowerBound;
5921
7173
  };
5922
7174
  Timeline.prototype.getPixel = function(dmy, drawCycleContext) {
5923
7175
  let cache;
@@ -6155,7 +7407,7 @@ Timeline.prototype.enqueueImageLoad = function(article) {
6155
7407
  };
6156
7408
  Timeline.prototype._getArticleImageTargetSpec = function(article) {
6157
7409
  const layout = article._getCurrentCardLayoutName();
6158
- const style = article._getCurrentStyle();
7410
+ const style = article.getCurrentStyle();
6159
7411
  const imageStyle = style && style.image ? style.image : {};
6160
7412
  const shape = imageStyle.shape || (layout === "portrait" ? "square" : "square");
6161
7413
  let cssTargetW = null, cssTargetH = null;
@@ -6201,6 +7453,9 @@ Timeline.prototype.setOption = function(option, value) {
6201
7453
  option = buildObjectFromPath(normalizedOptionPath, value);
6202
7454
  }
6203
7455
  option = handleDeprecatedOptions(option);
7456
+ if (existsInData(option, "article")) {
7457
+ deepMerge(this._explicitArticleOptions, option.article || {});
7458
+ }
6204
7459
  deepMerge(this.options, option);
6205
7460
  const sizeChanged = option.width || option.height;
6206
7461
  if (sizeChanged) {
@@ -6223,6 +7478,10 @@ Timeline.prototype.setOption = function(option, value) {
6223
7478
  if (timeBandsSpaceChanged) {
6224
7479
  this.top = this.options.height - this._getEffectiveVerticalOffset();
6225
7480
  }
7481
+ const parentContextSpaceChanged = existsInData(option, "style.dateLabel.parent.visible") || existsInData(option, "style.dateLabel.parent.height");
7482
+ if (parentContextSpaceChanged) {
7483
+ this.top = this.options.height - this._getEffectiveVerticalOffset();
7484
+ }
6226
7485
  const articleImpact = getLaneArticleOptionImpact(getPropertyFromData(option, "article"));
6227
7486
  const timeBandsDefaultStyleChanged = existsInData(option, "timeBand.defaultStyle");
6228
7487
  if (timeBandsDefaultStyleChanged && Array.isArray(this.timeBands) && this.timeBands.length > 0) {
@@ -6237,11 +7496,15 @@ Timeline.prototype.setOption = function(option, value) {
6237
7496
  if (Array.isArray(chartData) && typeof this.addCharts === "function") {
6238
7497
  this.addCharts(chartData);
6239
7498
  }
6240
- const chartDefaultsChanged = existsInData(option, "chart.defaultData") || existsInData(option, "chart.defaultStyle");
7499
+ const chartSeriesColorsChanged = existsInData(option, "chart.seriesColors");
7500
+ const chartDefaultsChanged = chartSeriesColorsChanged || existsInData(option, "chart.defaultData") || existsInData(option, "chart.defaultStyle");
6241
7501
  if (chartDefaultsChanged && Array.isArray(this.charts) && this.charts.length > 0) {
6242
7502
  let chartLaneChanged = false;
6243
7503
  for (let i = 0; i < this.charts.length; i++) {
6244
7504
  const previousLaneId = this.charts[i].laneId;
7505
+ if (chartSeriesColorsChanged) {
7506
+ this.charts[i]._seriesColors = [];
7507
+ }
6245
7508
  this.charts[i]._resolveFromData();
6246
7509
  if (existsInData(option, "chart.defaultData") && this.charts[i].laneId != null) {
6247
7510
  this._ensureChartLane(this.charts[i].laneId);
@@ -6298,18 +7561,38 @@ Timeline.prototype._getEffectiveVerticalOffset = function() {
6298
7561
  const tb = this.options.timeBand || {};
6299
7562
  const hasLoadedBands = Array.isArray(this.timeBands) && this.timeBands.length > 0;
6300
7563
  const reserve = !!tb.reserveSpace;
6301
- const extra = reserve && tb.visible && hasLoadedBands ? tb.reserveSpacePixels || 0 : 0;
6302
- return base + extra;
7564
+ const timeBandExtra = reserve && tb.visible && hasLoadedBands ? tb.reserveSpacePixels || 0 : 0;
7565
+ const parent = this.options.style?.dateLabel?.parent || {};
7566
+ const parentExtra = parent.visible === true ? parent.height || 0 : 0;
7567
+ return base + timeBandExtra + parentExtra;
6303
7568
  };
6304
7569
  Timeline.prototype.generateDensitySettings = function() {
6305
7570
  const zoomRatio = this.timescaleManager.zoomOptions.ratio;
6306
- Logger.log(this.timescaleManager);
6307
7571
  const newSettings = [];
6308
- const halfZoom = Math.round(Math.log(0.5) / Math.log(zoomRatio) * 100) / 100;
6309
- for (let i = 0; i < 19; i++) {
7572
+ const roundZoom = /* @__PURE__ */ __name((value) => Math.round(value * 100) / 100, "roundZoom");
7573
+ const halfZoom = roundZoom(Math.log(0.5) / Math.log(zoomRatio));
7574
+ const finestSubdayPower = Math.ceil(Math.log2(MILLISECONDS_PER_DAY));
7575
+ for (let power = finestSubdayPower; power >= 1; power--) {
7576
+ newSettings.push({
7577
+ region: 1 - power,
7578
+ zoom: {
7579
+ from: power === finestSubdayPower ? Number.NEGATIVE_INFINITY : roundZoom(-power * halfZoom),
7580
+ to: roundZoom(-(power - 1) * halfZoom)
7581
+ },
7582
+ low: 1,
7583
+ medium: 2,
7584
+ high: 3,
7585
+ all: 25,
7586
+ step: { months: 0, days: 0, divisionsPerDay: Math.pow(2, power) }
7587
+ });
7588
+ }
7589
+ const positiveRegionCount = Math.ceil(this.timescaleManager.zoomOptions.maximum / halfZoom);
7590
+ for (let i = 0; i < positiveRegionCount; i++) {
7591
+ const from = roundZoom(i * halfZoom);
7592
+ const to = roundZoom((i + 1) * halfZoom);
6310
7593
  const nextRegion = {
6311
7594
  region: i + 1,
6312
- zoom: { from: i * halfZoom, to: (i + 1) * halfZoom },
7595
+ zoom: { from, to },
6313
7596
  low: 1,
6314
7597
  medium: 2,
6315
7598
  high: 3,
@@ -6318,12 +7601,109 @@ Timeline.prototype.generateDensitySettings = function() {
6318
7601
  };
6319
7602
  newSettings.push(nextRegion);
6320
7603
  }
6321
- Logger.log(JSON.stringify(newSettings));
6322
- };
6323
- function getLaneGroupKey(laneId, groupIndex) {
6324
- return `${laneId}::${groupIndex}`;
7604
+ const output = JSON.stringify(
7605
+ newSettings,
7606
+ (_key, value) => value === Number.NEGATIVE_INFINITY ? "__DENSITY_NEGATIVE_INFINITY__" : value,
7607
+ 2
7608
+ ).replace('"__DENSITY_NEGATIVE_INFINITY__"', "Number.NEGATIVE_INFINITY");
7609
+ Logger.log(output);
7610
+ };
7611
+ function isSubdayDensityGroupIndex(index) {
7612
+ return index != null && typeof index === "object";
7613
+ }
7614
+ __name(isSubdayDensityGroupIndex, "isSubdayDensityGroupIndex");
7615
+ function createSubdayDensityGroupIndex(date, divisionsPerDay) {
7616
+ return {
7617
+ day: date.getDaysSinceYearZero(),
7618
+ division: Math.floor(date.getMillisecondsOfDay() * divisionsPerDay / MILLISECONDS_PER_DAY),
7619
+ divisionsPerDay
7620
+ };
7621
+ }
7622
+ __name(createSubdayDensityGroupIndex, "createSubdayDensityGroupIndex");
7623
+ function compareDensityGroupIndexes(left, right) {
7624
+ if (!isSubdayDensityGroupIndex(left) && !isSubdayDensityGroupIndex(right)) {
7625
+ return left === right ? 0 : left < right ? -1 : 1;
7626
+ }
7627
+ if (left.day !== right.day) {
7628
+ return left.day < right.day ? -1 : 1;
7629
+ }
7630
+ if (left.division !== right.division) {
7631
+ return left.division < right.division ? -1 : 1;
7632
+ }
7633
+ return 0;
7634
+ }
7635
+ __name(compareDensityGroupIndexes, "compareDensityGroupIndexes");
7636
+ function shiftDensityGroupIndex(index, offset) {
7637
+ if (!isSubdayDensityGroupIndex(index)) {
7638
+ return index + offset;
7639
+ }
7640
+ const unnormalisedDivision = index.division + offset;
7641
+ const dayOffset = Math.floor(unnormalisedDivision / index.divisionsPerDay);
7642
+ return {
7643
+ day: index.day + dayOffset,
7644
+ division: unnormalisedDivision - dayOffset * index.divisionsPerDay,
7645
+ divisionsPerDay: index.divisionsPerDay
7646
+ };
7647
+ }
7648
+ __name(shiftDensityGroupIndex, "shiftDensityGroupIndex");
7649
+ function clampDensityGroupIndex(index, minimum, maximum) {
7650
+ if (compareDensityGroupIndexes(index, minimum) < 0) {
7651
+ return minimum;
7652
+ }
7653
+ if (compareDensityGroupIndexes(index, maximum) > 0) {
7654
+ return maximum;
7655
+ }
7656
+ return index;
7657
+ }
7658
+ __name(clampDensityGroupIndex, "clampDensityGroupIndex");
7659
+ function getDensityGroupKey(index) {
7660
+ if (!isSubdayDensityGroupIndex(index)) {
7661
+ return String(index);
7662
+ }
7663
+ return `${index.day}:${index.division}`;
7664
+ }
7665
+ __name(getDensityGroupKey, "getDensityGroupKey");
7666
+ function getSubdayDensityGroupBoundary(index) {
7667
+ return {
7668
+ dayNumber: index.day,
7669
+ dayFraction: index.division / index.divisionsPerDay
7670
+ };
7671
+ }
7672
+ __name(getSubdayDensityGroupBoundary, "getSubdayDensityGroupBoundary");
7673
+ function getLaneGroupKey(laneId, groupIndex) {
7674
+ return `${laneId}::${getDensityGroupKey(groupIndex)}`;
7675
+ }
7676
+ __name(getLaneGroupKey, "getLaneGroupKey");
7677
+ const STARRED_INCREASE_CONSTANT = 1e7;
7678
+ const ACTIVE_INCREASE_CONSTANT = 2e7;
7679
+ function getArticleEffectiveRank(article) {
7680
+ if (article.isActive) {
7681
+ return article.rank + ACTIVE_INCREASE_CONSTANT;
7682
+ }
7683
+ return article.rank + (article.isStarred ? STARRED_INCREASE_CONSTANT : 0);
7684
+ }
7685
+ __name(getArticleEffectiveRank, "getArticleEffectiveRank");
7686
+ function selectRankedArticles(articles, pick) {
7687
+ const topArticles = [];
7688
+ let minRankIndex = 0;
7689
+ for (let i = 0; i < articles.length; i++) {
7690
+ const article = articles[i];
7691
+ article.effectiveRank = getArticleEffectiveRank(article);
7692
+ if (topArticles.length < pick) {
7693
+ topArticles.push(article);
7694
+ if (article.effectiveRank < topArticles[minRankIndex].effectiveRank) {
7695
+ minRankIndex = topArticles.length - 1;
7696
+ }
7697
+ continue;
7698
+ }
7699
+ if (article.effectiveRank > topArticles[minRankIndex].effectiveRank) {
7700
+ topArticles[minRankIndex] = article;
7701
+ minRankIndex = findMinIndex(topArticles, "effectiveRank");
7702
+ }
7703
+ }
7704
+ return topArticles;
6325
7705
  }
6326
- __name(getLaneGroupKey, "getLaneGroupKey");
7706
+ __name(selectRankedArticles, "selectRankedArticles");
6327
7707
  Timeline.prototype.updateArticlesGroupIndex = function() {
6328
7708
  this.forLoadedArticles(function(article) {
6329
7709
  article.groupIndex = this.calculateGroupIndex(article.period.from);
@@ -6333,15 +7713,21 @@ Timeline.prototype.updateVisibleArticlesOfGroups = function(drawCycleContext) {
6333
7713
  if (this._isLaneLayoutActive()) {
6334
7714
  this.groups = {};
6335
7715
  const groupIndexExtension2 = isScreenAutoStackingRange(this.options.article.autoStacking.range) ? 1 : 20;
6336
- const leftmostGroupIndex2 = this.calculateGroupIndex(drawCycleContext.tokens.start) - groupIndexExtension2;
6337
- const rightmostGroupIndex2 = this.calculateGroupIndex(drawCycleContext.tokens.end) + groupIndexExtension2;
7716
+ const leftmostGroupIndex2 = shiftDensityGroupIndex(
7717
+ this.calculateGroupIndex(drawCycleContext.tokens.start),
7718
+ -groupIndexExtension2
7719
+ );
7720
+ const rightmostGroupIndex2 = shiftDensityGroupIndex(
7721
+ this.calculateGroupIndex(drawCycleContext.tokens.end),
7722
+ groupIndexExtension2
7723
+ );
6338
7724
  const activeLanes = this._getActiveLanesForLayout();
6339
7725
  this.forLoadedArticles(function(article) {
6340
7726
  article.isVisibleInGroup = false;
6341
7727
  });
6342
7728
  for (let l = 0; l < activeLanes.length; l++) {
6343
7729
  const lane = activeLanes[l];
6344
- for (let index = leftmostGroupIndex2; index <= rightmostGroupIndex2; index++) {
7730
+ for (let index = leftmostGroupIndex2; compareDensityGroupIndexes(index, rightmostGroupIndex2) <= 0; index = shiftDensityGroupIndex(index, 1)) {
6345
7731
  const key = getLaneGroupKey(lane.id, index);
6346
7732
  this.groups[key] = this.getArticlesInGroup(
6347
7733
  index,
@@ -6358,13 +7744,19 @@ Timeline.prototype.updateVisibleArticlesOfGroups = function(drawCycleContext) {
6358
7744
  }
6359
7745
  this.groups = {};
6360
7746
  const groupIndexExtension = isScreenAutoStackingRange(this.options.article.autoStacking.range) ? 1 : 20;
6361
- const leftmostGroupIndex = this.calculateGroupIndex(drawCycleContext.tokens.start) - groupIndexExtension;
6362
- const rightmostGroupIndex = this.calculateGroupIndex(drawCycleContext.tokens.end) + groupIndexExtension;
7747
+ const leftmostGroupIndex = shiftDensityGroupIndex(
7748
+ this.calculateGroupIndex(drawCycleContext.tokens.start),
7749
+ -groupIndexExtension
7750
+ );
7751
+ const rightmostGroupIndex = shiftDensityGroupIndex(
7752
+ this.calculateGroupIndex(drawCycleContext.tokens.end),
7753
+ groupIndexExtension
7754
+ );
6363
7755
  this.forLoadedArticles(function(article) {
6364
7756
  article.isVisibleInGroup = false;
6365
7757
  });
6366
- for (let index = leftmostGroupIndex; index <= rightmostGroupIndex; index++) {
6367
- this.groups[index] = this.getArticlesInGroup(
7758
+ for (let index = leftmostGroupIndex; compareDensityGroupIndexes(index, rightmostGroupIndex) <= 0; index = shiftDensityGroupIndex(index, 1)) {
7759
+ this.groups[getDensityGroupKey(index)] = this.getArticlesInGroup(
6368
7760
  index,
6369
7761
  leftmostGroupIndex,
6370
7762
  rightmostGroupIndex
@@ -6374,26 +7766,11 @@ Timeline.prototype.updateVisibleArticlesOfGroups = function(drawCycleContext) {
6374
7766
  });
6375
7767
  }
6376
7768
  };
6377
- const STARRED_INCREASE_CONSTANT = 1e7;
6378
- const ACTIVE_INCREASE_CONSTANT = 2e7;
6379
7769
  Timeline.prototype.getArticlesInGroup = function(index, leftmostGroupIndex, rightmostGroupIndex, laneId) {
6380
- function getArticleEffectiveRank(article) {
6381
- let effectiveRank = article.rank;
6382
- if (article.isActive) {
6383
- effectiveRank += ACTIVE_INCREASE_CONSTANT;
6384
- } else {
6385
- if (article.isStarred) {
6386
- effectiveRank += STARRED_INCREASE_CONSTANT;
6387
- }
6388
- }
6389
- return effectiveRank;
6390
- }
6391
- __name(getArticleEffectiveRank, "getArticleEffectiveRank");
6392
7770
  const lane = typeof laneId !== "undefined" ? this.getLaneById(laneId) : null;
6393
7771
  const density = lane && lane.resolvedArticleOptions ? lane.resolvedArticleOptions.density : this.options.article.density;
6394
7772
  const pick = this.getDensityPick(density);
6395
- const topArticles = [];
6396
- let minRankIndex = 0;
7773
+ const articlesInGroup = [];
6397
7774
  for (let i = 0; i < this.articles.length; i++) {
6398
7775
  const article = this.articles[i];
6399
7776
  if (!article.isDataLoaded || article.isHiddenByFilter) {
@@ -6402,26 +7779,21 @@ Timeline.prototype.getArticlesInGroup = function(index, leftmostGroupIndex, righ
6402
7779
  if (typeof laneId !== "undefined" && article.laneId !== laneId) {
6403
7780
  continue;
6404
7781
  }
6405
- const belongsToThisGroup = clamp(article.groupIndex, leftmostGroupIndex, rightmostGroupIndex) === index;
7782
+ const belongsToThisGroup = compareDensityGroupIndexes(
7783
+ clampDensityGroupIndex(article.groupIndex, leftmostGroupIndex, rightmostGroupIndex),
7784
+ index
7785
+ ) === 0;
6406
7786
  if (!belongsToThisGroup) {
6407
7787
  continue;
6408
7788
  }
6409
- article.effectiveRank = getArticleEffectiveRank(article);
6410
- if (topArticles.length < pick) {
6411
- topArticles.push(article);
6412
- if (article.effectiveRank < topArticles[minRankIndex].effectiveRank) {
6413
- minRankIndex = topArticles.length - 1;
6414
- }
6415
- continue;
6416
- }
6417
- if (article.effectiveRank > topArticles[minRankIndex].effectiveRank) {
6418
- topArticles[minRankIndex] = article;
6419
- minRankIndex = findMinIndex(topArticles, "effectiveRank");
6420
- }
7789
+ articlesInGroup.push(article);
6421
7790
  }
6422
- return topArticles;
7791
+ return selectRankedArticles(articlesInGroup, pick);
6423
7792
  };
6424
7793
  Timeline.prototype.calculateGroupIndex = function(date) {
7794
+ if (this.currentDensitySetting.step.divisionsPerDay) {
7795
+ return createSubdayDensityGroupIndex(date, this.currentDensitySetting.step.divisionsPerDay);
7796
+ }
6425
7797
  if (this.timescaleManager.unit >= ZOOM_MONTH && this.currentDensitySetting.step.months !== 0) {
6426
7798
  const g2 = date.year * 12 + (date.month - 1);
6427
7799
  return Math.floor(g2 / this.currentDensitySetting.step.months);
@@ -6438,17 +7810,18 @@ Timeline.prototype.canBeVisibleInGroup = function(article, startToken, endToken)
6438
7810
  }
6439
7811
  startToken = startToken || this.timescaleManager.startToken;
6440
7812
  endToken = endToken || this.calculateEndToken();
6441
- const leftmostGroupIndex = this.calculateGroupIndex(startToken.value) - 1;
6442
- const rightmostGroupIndex = this.calculateGroupIndex(endToken.value) + 1;
6443
- const effectiveArticleGroupIndex = clamp(
7813
+ const leftmostGroupIndex = shiftDensityGroupIndex(this.calculateGroupIndex(startToken.value), -1);
7814
+ const rightmostGroupIndex = shiftDensityGroupIndex(this.calculateGroupIndex(endToken.value), 1);
7815
+ const effectiveArticleGroupIndex = clampDensityGroupIndex(
6444
7816
  article.groupIndex,
6445
7817
  leftmostGroupIndex,
6446
7818
  rightmostGroupIndex
6447
7819
  );
6448
- if (!Object.prototype.hasOwnProperty.call(this.groups, effectiveArticleGroupIndex)) {
7820
+ const effectiveGroupKey = getDensityGroupKey(effectiveArticleGroupIndex);
7821
+ if (!Object.prototype.hasOwnProperty.call(this.groups, effectiveGroupKey)) {
6449
7822
  return false;
6450
7823
  }
6451
- return this.groups[effectiveArticleGroupIndex].indexOf(article.id) >= 0;
7824
+ return this.groups[effectiveGroupKey].indexOf(article.id) >= 0;
6452
7825
  };
6453
7826
  const DEFAULT_MAX_FRAME_DELTA = 64;
6454
7827
  function startInertia({
@@ -6525,16 +7898,76 @@ function normalisePadding(padding = 0) {
6525
7898
  }
6526
7899
  __name(normalisePadding, "normalisePadding");
6527
7900
  function normaliseDateInput(date) {
6528
- if (!date || !Number.isFinite(date.year)) {
6529
- throw new Error("fitDateRange requires start and end dates with a numeric year");
7901
+ const parsed = typeof date === "string" ? parseDmyString(date)?.date : date;
7902
+ if (!parsed || !Number.isFinite(parsed.year)) {
7903
+ throw new Error("Date input requires a numeric year or canonical Histropedia date string");
6530
7904
  }
6531
7905
  return new Dmy({
6532
- year: date.year,
6533
- month: date.month ?? 1,
6534
- day: date.day ?? 1
7906
+ year: parsed.year,
7907
+ month: parsed.month ?? 1,
7908
+ day: parsed.day ?? 1,
7909
+ hour: parsed.hour ?? 0,
7910
+ minute: parsed.minute ?? 0,
7911
+ second: parsed.second ?? 0,
7912
+ millisecond: parsed.millisecond ?? 0
6535
7913
  });
6536
7914
  }
6537
7915
  __name(normaliseDateInput, "normaliseDateInput");
7916
+ function normaliseDayPoint(point) {
7917
+ const dayOffset = Math.floor(point.millisecond / MILLISECONDS_PER_DAY);
7918
+ return {
7919
+ dayNumber: point.dayNumber + dayOffset,
7920
+ millisecond: point.millisecond - dayOffset * MILLISECONDS_PER_DAY
7921
+ };
7922
+ }
7923
+ __name(normaliseDayPoint, "normaliseDayPoint");
7924
+ function getDayPoint(dmy) {
7925
+ return {
7926
+ dayNumber: dmy.getDaysSinceYearZero(),
7927
+ millisecond: dmy.getMillisecondsOfDay()
7928
+ };
7929
+ }
7930
+ __name(getDayPoint, "getDayPoint");
7931
+ function compareDayPoints(left, right) {
7932
+ if (left.dayNumber !== right.dayNumber) {
7933
+ return left.dayNumber < right.dayNumber ? -1 : 1;
7934
+ }
7935
+ if (left.millisecond !== right.millisecond) {
7936
+ return left.millisecond < right.millisecond ? -1 : 1;
7937
+ }
7938
+ return 0;
7939
+ }
7940
+ __name(compareDayPoints, "compareDayPoints");
7941
+ function getDayPointDifference(start, end) {
7942
+ const wholeDays = end.dayNumber - start.dayNumber;
7943
+ const milliseconds = end.millisecond - start.millisecond;
7944
+ return wholeDays + milliseconds / MILLISECONDS_PER_DAY;
7945
+ }
7946
+ __name(getDayPointDifference, "getDayPointDifference");
7947
+ function addDaysToDayPoint(point, days) {
7948
+ const wholeDays = Math.floor(days);
7949
+ return normaliseDayPoint({
7950
+ dayNumber: point.dayNumber + wholeDays,
7951
+ millisecond: point.millisecond + (days - wholeDays) * MILLISECONDS_PER_DAY
7952
+ });
7953
+ }
7954
+ __name(addDaysToDayPoint, "addDaysToDayPoint");
7955
+ function interpolateDayPoints(start, end, progress) {
7956
+ if (progress <= 0) {
7957
+ return { ...start };
7958
+ }
7959
+ if (progress >= 1) {
7960
+ return { ...end };
7961
+ }
7962
+ const scaledWholeDayDifference = (end.dayNumber - start.dayNumber) * progress;
7963
+ const wholeDays = Math.floor(scaledWholeDayDifference);
7964
+ const milliseconds = start.millisecond + (scaledWholeDayDifference - wholeDays) * MILLISECONDS_PER_DAY + (end.millisecond - start.millisecond) * progress;
7965
+ return normaliseDayPoint({
7966
+ dayNumber: start.dayNumber + wholeDays,
7967
+ millisecond: milliseconds
7968
+ });
7969
+ }
7970
+ __name(interpolateDayPoints, "interpolateDayPoints");
6538
7971
  function getNextYearStart(year) {
6539
7972
  return new Dmy(year === -1 ? 1 : year + 1, 1, 1);
6540
7973
  }
@@ -6548,18 +7981,20 @@ function getNextMonthStart(year, month) {
6548
7981
  __name(getNextMonthStart, "getNextMonthStart");
6549
7982
  function normaliseDateRangeInput(date) {
6550
7983
  const start = normaliseDateInput(date);
6551
- const hasMonth = date.month != null;
6552
- const hasDay = date.day != null;
6553
- if (!hasMonth) {
7984
+ const parsedString = typeof date === "string" ? parseDmyString(date) : null;
7985
+ const hasMonth = parsedString ? parsedString.hasMonth : date.month != null;
7986
+ const hasDay = parsedString ? parsedString.hasDay : date.day != null;
7987
+ const hasTime = parsedString ? parsedString.hasTime : date.hour != null || date.minute != null || date.second != null || date.millisecond != null;
7988
+ if (!hasMonth && !hasTime) {
6554
7989
  return {
6555
7990
  start,
6556
- end: getNextYearStart(date.year)
7991
+ end: getNextYearStart(start.year)
6557
7992
  };
6558
7993
  }
6559
- if (!hasDay) {
7994
+ if (!hasDay && !hasTime) {
6560
7995
  return {
6561
7996
  start,
6562
- end: getNextMonthStart(date.year, date.month)
7997
+ end: getNextMonthStart(start.year, start.month)
6563
7998
  };
6564
7999
  }
6565
8000
  return {
@@ -6593,13 +8028,18 @@ function normaliseZoomForManager(manager, zoom) {
6593
8028
  }
6594
8029
  __name(normaliseZoomForManager, "normaliseZoomForManager");
6595
8030
  function getUnitFromZoom(manager, zoom) {
8031
+ if (typeof manager.getUnitFromZoom === "function") {
8032
+ return manager.getUnitFromZoom(zoom);
8033
+ }
6596
8034
  const unitLevels = manager.unitLevels;
6597
8035
  if (!Array.isArray(unitLevels) || unitLevels.length === 0) {
6598
8036
  return manager.unit;
6599
8037
  }
6600
- for (let i = unitLevels.length - 1; i >= 0; i--) {
6601
- if (zoom >= unitLevels[i].zoomStart) {
6602
- return i;
8038
+ const unitOrder = manager.unitOrder || unitLevels.map((_settings, unit) => unit);
8039
+ for (let i = unitOrder.length - 1; i >= 0; i--) {
8040
+ const unit = unitOrder[i];
8041
+ if (zoom >= unitLevels[unit].zoomStart) {
8042
+ return unit;
6603
8043
  }
6604
8044
  }
6605
8045
  return manager.unit;
@@ -6608,8 +8048,8 @@ __name(getUnitFromZoom, "getUnitFromZoom");
6608
8048
  function getUnitSizeInPixelsForZoom(manager, unit, zoom) {
6609
8049
  const unitSettings = manager.unitLevels?.[unit];
6610
8050
  const zoomRatio = manager.zoomOptions?.ratio;
6611
- if (unitSettings && Number.isFinite(unitSettings.startUnitSize) && Number.isFinite(unitSettings.zoomStart) && Number.isFinite(zoomRatio) && zoomRatio > 0) {
6612
- return unitSettings.startUnitSize * Math.pow(zoomRatio, zoom - unitSettings.zoomStart);
8051
+ if (unitSettings && Number.isFinite(unitSettings.startUnitSize) && Number.isFinite(unitSettings.referenceZoom ?? unitSettings.zoomStart) && Number.isFinite(zoomRatio) && zoomRatio > 0) {
8052
+ return unitSettings.startUnitSize * Math.pow(zoomRatio, zoom - (unitSettings.referenceZoom ?? unitSettings.zoomStart));
6613
8053
  }
6614
8054
  if (!Number.isFinite(manager.unitSizeInPixels)) {
6615
8055
  return NaN;
@@ -6630,7 +8070,7 @@ function getPixelsBetweenAtZoom(manager, startDate, endDate, zoom) {
6630
8070
  return NaN;
6631
8071
  }
6632
8072
  if (unit <= ZOOM_DAY) {
6633
- return startDate.getDaysTo(endDate) * unitSizeInPixels;
8073
+ return startDate.getDaysTo(endDate) * unitSizeInPixels / getZoomUnitLengthInDays(unit);
6634
8074
  }
6635
8075
  if (unit === ZOOM_MONTH) {
6636
8076
  return startDate.getMonthsTo(endDate) * unitSizeInPixels;
@@ -6638,23 +8078,23 @@ function getPixelsBetweenAtZoom(manager, startDate, endDate, zoom) {
6638
8078
  return startDate.getYearsTo(endDate) * unitSizeInPixels / getUnitLengthInYears(unit);
6639
8079
  }
6640
8080
  __name(getPixelsBetweenAtZoom, "getPixelsBetweenAtZoom");
6641
- function getPixelsBetweenDayNumbersAtZoom(manager, startDayNumber, endDayNumber, zoom) {
8081
+ function getPixelsBetweenDayPointsAtZoom(manager, startPoint, endPoint, zoom) {
6642
8082
  const normalisedZoom = normaliseZoomForManager(manager, zoom);
6643
8083
  const unit = getUnitFromZoom(manager, normalisedZoom);
6644
8084
  const unitSizeInPixels = getUnitSizeInPixelsForZoom(manager, unit, normalisedZoom);
6645
8085
  if (!Number.isFinite(unitSizeInPixels) || unitSizeInPixels <= 0) {
6646
8086
  return NaN;
6647
8087
  }
6648
- const daySpan = endDayNumber - startDayNumber;
8088
+ const daySpan = getDayPointDifference(startPoint, endPoint);
6649
8089
  if (unit <= ZOOM_DAY) {
6650
- return daySpan * unitSizeInPixels;
8090
+ return daySpan * unitSizeInPixels / getZoomUnitLengthInDays(unit);
6651
8091
  }
6652
8092
  if (unit === ZOOM_MONTH) {
6653
8093
  return daySpan * unitSizeInPixels / 30.44;
6654
8094
  }
6655
8095
  return daySpan * unitSizeInPixels / (365.2422 * getUnitLengthInYears(unit));
6656
8096
  }
6657
- __name(getPixelsBetweenDayNumbersAtZoom, "getPixelsBetweenDayNumbersAtZoom");
8097
+ __name(getPixelsBetweenDayPointsAtZoom, "getPixelsBetweenDayPointsAtZoom");
6658
8098
  function getFitZoomForInternalDates(timeline, internalStart, internalEnd, padding) {
6659
8099
  const manager = timeline.timescaleManager;
6660
8100
  const currentPixelSpan = manager.getPixelsBetween(internalStart, internalEnd);
@@ -6702,14 +8142,7 @@ function normaliseBoundDate(timeline, date) {
6702
8142
  if (date == null) {
6703
8143
  return null;
6704
8144
  }
6705
- const normalised = normaliseDateInput(typeof date === "string" ? Dmy.fromString(date) : date);
6706
- return timeline.getDmyFromInput(
6707
- new Dmy({
6708
- year: normalised.year,
6709
- month: normalised.month,
6710
- day: normalised.day
6711
- })
6712
- );
8145
+ return timeline.getDmyFromInput(normaliseDateInput(date));
6713
8146
  }
6714
8147
  __name(normaliseBoundDate, "normaliseBoundDate");
6715
8148
  function getTimelineDateBounds(timeline) {
@@ -6728,24 +8161,27 @@ function getTimelineDateBounds(timeline) {
6728
8161
  return {
6729
8162
  minDate,
6730
8163
  maxDate,
8164
+ minPoint: minDate ? getDayPoint(minDate) : null,
8165
+ maxPoint: maxDate ? getDayPoint(maxDate) : null,
6731
8166
  minDay: minDate ? getDayNumber(minDate) : null,
6732
8167
  maxDay: maxDate ? getDayNumber(maxDate) : null,
6733
8168
  overflow: bounds.overflow === "clamp" ? "clamp" : "elastic"
6734
8169
  };
6735
8170
  }
6736
8171
  __name(getTimelineDateBounds, "getTimelineDateBounds");
6737
- function getViewportDayRange(timeline) {
6738
- const start = getDayNumberAtPixel(timeline, 0);
6739
- const end = getDayNumberAtPixel(timeline, timeline.getWidth());
6740
- return start <= end ? { start, end } : { start: end, end: start };
8172
+ function getViewportDayPointRange(timeline) {
8173
+ const start = getDayPointAtPixel(timeline, 0);
8174
+ const end = getDayPointAtPixel(timeline, timeline.getWidth());
8175
+ return compareDayPoints(start, end) <= 0 ? { start, end } : { start: end, end: start };
6741
8176
  }
6742
- __name(getViewportDayRange, "getViewportDayRange");
8177
+ __name(getViewportDayPointRange, "getViewportDayPointRange");
6743
8178
  function getDateBoundSideCorrection(bounds, viewport, width) {
6744
- const minOverflow = bounds.minDay != null ? bounds.minDay - viewport.start : 0;
6745
- const maxOverflow = bounds.maxDay != null ? viewport.end - bounds.maxDay : 0;
8179
+ const minOverflow = bounds.minPoint ? getDayPointDifference(viewport.start, bounds.minPoint) : 0;
8180
+ const maxOverflow = bounds.maxPoint ? getDayPointDifference(bounds.maxPoint, viewport.end) : 0;
6746
8181
  if (minOverflow > 0 && minOverflow >= maxOverflow) {
6747
8182
  return {
6748
8183
  type: "min",
8184
+ dayPoint: bounds.minPoint,
6749
8185
  dayNumber: bounds.minDay,
6750
8186
  pixelOffset: 0,
6751
8187
  overflowDays: minOverflow
@@ -6754,6 +8190,7 @@ function getDateBoundSideCorrection(bounds, viewport, width) {
6754
8190
  if (maxOverflow > 0) {
6755
8191
  return {
6756
8192
  type: "max",
8193
+ dayPoint: bounds.maxPoint,
6757
8194
  dayNumber: bounds.maxDay,
6758
8195
  pixelOffset: width,
6759
8196
  overflowDays: maxOverflow
@@ -6766,22 +8203,23 @@ function getDateBoundCorrection(timeline, bounds = getTimelineDateBounds(timelin
6766
8203
  if (!bounds) {
6767
8204
  return null;
6768
8205
  }
6769
- const viewport = getViewportDayRange(timeline);
8206
+ const viewport = getViewportDayPointRange(timeline);
6770
8207
  const width = timeline.getWidth();
6771
8208
  const sideCorrection = getDateBoundSideCorrection(bounds, viewport, width);
6772
8209
  if (options.mode === "elastic" && bounds.overflow === "elastic" && sideCorrection) {
6773
8210
  return sideCorrection;
6774
8211
  }
6775
- if (bounds.minDay != null && bounds.maxDay != null && viewport.end - viewport.start > bounds.maxDay - bounds.minDay) {
8212
+ if (bounds.minPoint && bounds.maxPoint && getDayPointDifference(viewport.start, viewport.end) > getDayPointDifference(bounds.minPoint, bounds.maxPoint)) {
6776
8213
  return {
6777
8214
  type: "range",
6778
- zoom: getFitZoomForDayNumbers(
8215
+ zoom: getFitZoomForDayPoints(
6779
8216
  timeline,
6780
- bounds.minDay,
6781
- bounds.maxDay,
8217
+ bounds.minPoint,
8218
+ bounds.maxPoint,
6782
8219
  { left: 0, right: 0 },
6783
8220
  timeline.timescaleManager.zoom
6784
8221
  ),
8222
+ dayPoint: bounds.minPoint,
6785
8223
  dayNumber: bounds.minDay,
6786
8224
  pixelOffset: 0
6787
8225
  };
@@ -6801,22 +8239,44 @@ function getElasticOverflowDays(timeline, overflowDays) {
6801
8239
  return resistedPixels / pixelsPerDay;
6802
8240
  }
6803
8241
  __name(getElasticOverflowDays, "getElasticOverflowDays");
6804
- function setElasticBoundedViewportStartDay(timeline, targetStartDayNumber, viewportSpanDays) {
8242
+ function getUnresistedElasticOverflowDays(timeline, overflowDays) {
8243
+ const pixelsPerDay = getPixelsPerDay(timeline);
8244
+ if (!pixelsPerDay || !Number.isFinite(overflowDays) || overflowDays <= 0) {
8245
+ return 0;
8246
+ }
8247
+ const width = Math.max(1, timeline.getWidth());
8248
+ const maxElasticPixels = Math.max(32, width * 0.22);
8249
+ const resistedPixels = overflowDays * pixelsPerDay;
8250
+ if (resistedPixels >= maxElasticPixels) {
8251
+ return Number.MAX_SAFE_INTEGER / pixelsPerDay;
8252
+ }
8253
+ return maxElasticPixels * resistedPixels / (maxElasticPixels - resistedPixels) / pixelsPerDay;
8254
+ }
8255
+ __name(getUnresistedElasticOverflowDays, "getUnresistedElasticOverflowDays");
8256
+ function setElasticBoundedViewportStartPoint(timeline, targetStartPoint, viewportSpanDays) {
6805
8257
  const bounds = getTimelineDateBounds(timeline);
6806
- let nextStartDayNumber = targetStartDayNumber;
8258
+ let nextStartPoint = targetStartPoint;
6807
8259
  if (bounds?.overflow === "elastic" && Number.isFinite(viewportSpanDays)) {
6808
- const targetEndDayNumber = targetStartDayNumber + viewportSpanDays;
6809
- if (bounds.minDay != null && targetStartDayNumber < bounds.minDay) {
6810
- nextStartDayNumber = bounds.minDay - getElasticOverflowDays(timeline, bounds.minDay - targetStartDayNumber);
6811
- } else if (bounds.maxDay != null && targetEndDayNumber > bounds.maxDay) {
6812
- nextStartDayNumber = bounds.maxDay + getElasticOverflowDays(timeline, targetEndDayNumber - bounds.maxDay) - viewportSpanDays;
8260
+ const targetEndPoint = addDaysToDayPoint(targetStartPoint, viewportSpanDays);
8261
+ if (bounds.minPoint && compareDayPoints(targetStartPoint, bounds.minPoint) < 0) {
8262
+ const overflow = getDayPointDifference(targetStartPoint, bounds.minPoint);
8263
+ nextStartPoint = addDaysToDayPoint(
8264
+ bounds.minPoint,
8265
+ -getElasticOverflowDays(timeline, overflow)
8266
+ );
8267
+ } else if (bounds.maxPoint && compareDayPoints(targetEndPoint, bounds.maxPoint) > 0) {
8268
+ const overflow = getDayPointDifference(bounds.maxPoint, targetEndPoint);
8269
+ nextStartPoint = addDaysToDayPoint(
8270
+ bounds.maxPoint,
8271
+ getElasticOverflowDays(timeline, overflow) - viewportSpanDays
8272
+ );
6813
8273
  }
6814
8274
  }
6815
- setInternalStartDayNumber(timeline, nextStartDayNumber, 0, false, {
8275
+ setInternalStartDayPoint(timeline, nextStartPoint, 0, false, {
6816
8276
  skipDateBounds: true
6817
8277
  });
6818
8278
  }
6819
- __name(setElasticBoundedViewportStartDay, "setElasticBoundedViewportStartDay");
8279
+ __name(setElasticBoundedViewportStartPoint, "setElasticBoundedViewportStartPoint");
6820
8280
  function applyTimelineDateBounds(timeline, options = {}) {
6821
8281
  const bounds = getTimelineDateBounds(timeline);
6822
8282
  if (!bounds) {
@@ -6831,7 +8291,7 @@ function applyTimelineDateBounds(timeline, options = {}) {
6831
8291
  if (correction.zoom !== timeline.timescaleManager.zoom) {
6832
8292
  timeline.timescaleManager.setZoom(correction.zoom);
6833
8293
  }
6834
- setInternalStartDayNumber(timeline, correction.dayNumber, correction.pixelOffset, false, {
8294
+ setInternalStartDayPoint(timeline, correction.dayPoint, correction.pixelOffset, false, {
6835
8295
  skipDateBounds: true
6836
8296
  });
6837
8297
  return true;
@@ -6839,13 +8299,17 @@ function applyTimelineDateBounds(timeline, options = {}) {
6839
8299
  if (mode === "elastic" && bounds.overflow === "elastic") {
6840
8300
  const elasticOverflowDays = getElasticOverflowDays(timeline, correction.overflowDays);
6841
8301
  if (correction.type === "min") {
6842
- setInternalStartDayNumber(timeline, correction.dayNumber - elasticOverflowDays, 0, false, {
6843
- skipDateBounds: true
6844
- });
8302
+ setInternalStartDayPoint(
8303
+ timeline,
8304
+ addDaysToDayPoint(correction.dayPoint, -elasticOverflowDays),
8305
+ 0,
8306
+ false,
8307
+ { skipDateBounds: true }
8308
+ );
6845
8309
  } else {
6846
- setInternalStartDayNumber(
8310
+ setInternalStartDayPoint(
6847
8311
  timeline,
6848
- correction.dayNumber + elasticOverflowDays,
8312
+ addDaysToDayPoint(correction.dayPoint, elasticOverflowDays),
6849
8313
  correction.pixelOffset,
6850
8314
  false,
6851
8315
  { skipDateBounds: true }
@@ -6853,7 +8317,7 @@ function applyTimelineDateBounds(timeline, options = {}) {
6853
8317
  }
6854
8318
  return true;
6855
8319
  }
6856
- setInternalStartDayNumber(timeline, correction.dayNumber, correction.pixelOffset, false, {
8320
+ setInternalStartDayPoint(timeline, correction.dayPoint, correction.pixelOffset, false, {
6857
8321
  skipDateBounds: true
6858
8322
  });
6859
8323
  return true;
@@ -6870,7 +8334,7 @@ function getPixelsPerDay(timeline) {
6870
8334
  return 0;
6871
8335
  }
6872
8336
  if (manager.unit == null || manager.unit <= ZOOM_DAY) {
6873
- return unitSize;
8337
+ return unitSize / getZoomUnitLengthInDays(manager.unit ?? ZOOM_DAY);
6874
8338
  }
6875
8339
  if (manager.unit === ZOOM_MONTH) {
6876
8340
  return unitSize / 30.44;
@@ -6879,10 +8343,12 @@ function getPixelsPerDay(timeline) {
6879
8343
  return unitSize / (365.2422 * unitLengthInYears);
6880
8344
  }
6881
8345
  __name(getPixelsPerDay, "getPixelsPerDay");
6882
- function setInternalStartDayNumber(timeline, dayNumber, pixelOffset, emitStateChange = false, options = {}) {
6883
- const startDayNumber = Math.floor(dayNumber);
6884
- const startDmy = getDmyFromDayNumber(startDayNumber);
6885
- const partialDayPixels = (dayNumber - startDayNumber) * getPixelsPerDay(timeline);
8346
+ function setInternalStartDayPoint(timeline, dayPoint, pixelOffset, emitStateChange = false, options = {}) {
8347
+ const normalisedPoint = normaliseDayPoint(dayPoint);
8348
+ const milliseconds = Math.floor(normalisedPoint.millisecond);
8349
+ const millisecondFraction = normalisedPoint.millisecond - milliseconds;
8350
+ const startDmy = getDmyFromDayNumber(normalisedPoint.dayNumber).addMilliseconds(milliseconds);
8351
+ const partialDayPixels = millisecondFraction * getPixelsPerDay(timeline) / MILLISECONDS_PER_DAY;
6886
8352
  const dateFraction = timeline.timescaleManager.setStartDate(startDmy);
6887
8353
  timeline.repositionWindow = dateFraction - partialDayPixels;
6888
8354
  if (pixelOffset) {
@@ -6897,26 +8363,26 @@ function setInternalStartDayNumber(timeline, dayNumber, pixelOffset, emitStateCh
6897
8363
  timeline._emitTimelineStateChange();
6898
8364
  }
6899
8365
  }
6900
- __name(setInternalStartDayNumber, "setInternalStartDayNumber");
8366
+ __name(setInternalStartDayPoint, "setInternalStartDayPoint");
6901
8367
  function setInternalStartDate(timeline, dmy, pixelOffset, emitStateChange = false, options = {}) {
6902
- const dayNumber = getDayNumber(dmy);
6903
- setInternalStartDayNumber(timeline, dayNumber, pixelOffset, emitStateChange, options);
8368
+ setInternalStartDayPoint(timeline, getDayPoint(dmy), pixelOffset, emitStateChange, options);
6904
8369
  }
6905
8370
  __name(setInternalStartDate, "setInternalStartDate");
6906
- function getDayNumberFromPivotPoint(timeline, pivotPoint) {
8371
+ function getDayPointFromPivotPoint(timeline, pivotPoint) {
6907
8372
  const pixelsPerDay = getPixelsPerDay(timeline);
8373
+ const point = getDayPoint(pivotPoint.date);
6908
8374
  if (!pixelsPerDay) {
6909
- return getDayNumber(pivotPoint.date);
8375
+ return point;
6910
8376
  }
6911
- return getDayNumber(pivotPoint.date) + pivotPoint.fraction / pixelsPerDay;
8377
+ return addDaysToDayPoint(point, pivotPoint.fraction / pixelsPerDay);
6912
8378
  }
6913
- __name(getDayNumberFromPivotPoint, "getDayNumberFromPivotPoint");
6914
- function getDayNumberAtPixel(timeline, pixel) {
6915
- return getDayNumberFromPivotPoint(timeline, timeline.getPivotPoint(pixel));
8379
+ __name(getDayPointFromPivotPoint, "getDayPointFromPivotPoint");
8380
+ function getDayPointAtPixel(timeline, pixel) {
8381
+ return getDayPointFromPivotPoint(timeline, timeline.getPivotPoint(pixel));
6916
8382
  }
6917
- __name(getDayNumberAtPixel, "getDayNumberAtPixel");
8383
+ __name(getDayPointAtPixel, "getDayPointAtPixel");
6918
8384
  function getDayNumber(dmy) {
6919
- return dmy.getDaysSinceYearZero();
8385
+ return dmy.getDaysSinceYearZero() + dmy.getMillisecondsOfDay() / MILLISECONDS_PER_DAY;
6920
8386
  }
6921
8387
  __name(getDayNumber, "getDayNumber");
6922
8388
  function getDmyFromDayNumber(dayNumber) {
@@ -6941,12 +8407,12 @@ function interpolate(start, end, progress) {
6941
8407
  return start + (end - start) * progress;
6942
8408
  }
6943
8409
  __name(interpolate, "interpolate");
6944
- function getFitZoomForDayNumbers(timeline, startDayNumber, endDayNumber, padding, fallbackZoom) {
6945
- if (endDayNumber <= startDayNumber) {
8410
+ function getFitZoomForDayPoints(timeline, startPoint, endPoint, padding, fallbackZoom) {
8411
+ if (compareDayPoints(endPoint, startPoint) <= 0) {
6946
8412
  return fallbackZoom;
6947
8413
  }
6948
8414
  const manager = timeline.timescaleManager;
6949
- const currentPixelSpan = (endDayNumber - startDayNumber) * getPixelsPerDay(timeline);
8415
+ const currentPixelSpan = getDayPointDifference(startPoint, endPoint) * getPixelsPerDay(timeline);
6950
8416
  if (!Number.isFinite(currentPixelSpan) || currentPixelSpan <= 0) {
6951
8417
  return fallbackZoom;
6952
8418
  }
@@ -6957,10 +8423,10 @@ function getFitZoomForDayNumbers(timeline, startDayNumber, endDayNumber, padding
6957
8423
  }
6958
8424
  let targetZoom = manager.zoom + zoomChange;
6959
8425
  for (let i = 0; i < 4; i++) {
6960
- const predictedPixelSpan = getPixelsBetweenDayNumbersAtZoom(
8426
+ const predictedPixelSpan = getPixelsBetweenDayPointsAtZoom(
6961
8427
  manager,
6962
- startDayNumber,
6963
- endDayNumber,
8428
+ startPoint,
8429
+ endPoint,
6964
8430
  targetZoom
6965
8431
  );
6966
8432
  if (!Number.isFinite(predictedPixelSpan) || predictedPixelSpan <= 0) {
@@ -6977,7 +8443,7 @@ function getFitZoomForDayNumbers(timeline, startDayNumber, endDayNumber, padding
6977
8443
  }
6978
8444
  return normaliseZoomForManager(manager, targetZoom);
6979
8445
  }
6980
- __name(getFitZoomForDayNumbers, "getFitZoomForDayNumbers");
8446
+ __name(getFitZoomForDayPoints, "getFitZoomForDayPoints");
6981
8447
  function stopNavigationAnimations(timeline) {
6982
8448
  if (typeof timeline.stopPanAnimation === "function") {
6983
8449
  timeline.stopPanAnimation();
@@ -7014,13 +8480,13 @@ function animateDateRangeFit(timeline, targetStartDate, targetEndDate, targetZoo
7014
8480
  timeline.startAnimation();
7015
8481
  const startZoom = timeline.timescaleManager.zoom;
7016
8482
  const fitPixelSpan = getFitPixelSpan(timeline, padding);
7017
- const startDayNumberAtPadding = getDayNumberAtPixel(timeline, padding.left);
7018
- const startEndDayNumberAtPadding = getDayNumberAtPixel(timeline, padding.left + fitPixelSpan);
8483
+ const startPointAtPadding = getDayPointAtPixel(timeline, padding.left);
8484
+ const startEndPointAtPadding = getDayPointAtPixel(timeline, padding.left + fitPixelSpan);
7019
8485
  const targetInternalStart = timeline.getDmyFromInput(normaliseDateInput(targetStartDate));
7020
8486
  const targetInternalEnd = timeline.getDmyFromInput(normaliseDateInput(targetEndDate));
7021
- const targetDayNumber = getDayNumber(targetInternalStart);
7022
- const targetEndDayNumber = getDayNumber(targetInternalEnd);
7023
- const canInterpolateBoundaries = startEndDayNumberAtPadding > startDayNumberAtPadding && targetEndDayNumber > targetDayNumber;
8487
+ const targetPoint = getDayPoint(targetInternalStart);
8488
+ const targetEndPoint = getDayPoint(targetInternalEnd);
8489
+ const canInterpolateBoundaries = compareDayPoints(startEndPointAtPadding, startPointAtPadding) > 0 && compareDayPoints(targetEndPoint, targetPoint) > 0;
7024
8490
  const rangeTween = tween({
7025
8491
  from: 0,
7026
8492
  to: 1,
@@ -7032,13 +8498,13 @@ function animateDateRangeFit(timeline, targetStartDate, targetEndDate, targetZoo
7032
8498
  rangeTween.stop();
7033
8499
  return;
7034
8500
  }
7035
- const nextDayNumber = interpolate(startDayNumberAtPadding, targetDayNumber, progress);
8501
+ const nextPoint = interpolateDayPoints(startPointAtPadding, targetPoint, progress);
7036
8502
  let nextZoom = startZoom;
7037
8503
  if (progress > 0) {
7038
- nextZoom = canInterpolateBoundaries ? getFitZoomForDayNumbers(
8504
+ nextZoom = canInterpolateBoundaries ? getFitZoomForDayPoints(
7039
8505
  timeline,
7040
- nextDayNumber,
7041
- interpolate(startEndDayNumberAtPadding, targetEndDayNumber, progress),
8506
+ nextPoint,
8507
+ interpolateDayPoints(startEndPointAtPadding, targetEndPoint, progress),
7042
8508
  padding,
7043
8509
  timeline.timescaleManager.zoom
7044
8510
  ) : interpolate(startZoom, targetZoom, progress);
@@ -7046,7 +8512,7 @@ function animateDateRangeFit(timeline, targetStartDate, targetEndDate, targetZoo
7046
8512
  if (nextZoom !== timeline.timescaleManager.zoom) {
7047
8513
  timeline.timescaleManager.setZoom(nextZoom);
7048
8514
  }
7049
- setInternalStartDayNumber(timeline, nextDayNumber, padding.left);
8515
+ setInternalStartDayPoint(timeline, nextPoint, padding.left);
7050
8516
  },
7051
8517
  onComplete() {
7052
8518
  if (targetZoom !== timeline.timescaleManager.zoom) {
@@ -7077,7 +8543,7 @@ function getArticleFitRange(timeline, articleFilter) {
7077
8543
  if (!latest || from.compare(latest) > 0) {
7078
8544
  latest = from;
7079
8545
  }
7080
- const style = article._getCurrentStyle();
8546
+ const style = article.getCurrentStyle();
7081
8547
  const connectorOffset = style.connectorLine?.offsetX ?? 0;
7082
8548
  const width = style.width ?? 0;
7083
8549
  maxCardLeftOffset = Math.max(maxCardLeftOffset, connectorOffset);
@@ -7434,10 +8900,7 @@ Timeline.prototype.enableDragging = /* @__PURE__ */ __name(function enableDraggi
7434
8900
  __name(onPointerMove, "onPointerMove");
7435
8901
  function onPointerEnd(evt, tracker) {
7436
8902
  if (me.isPinching && tracker.list.length < 2) {
7437
- if (tracker.list.length === 1) {
7438
- const primary = getPrimary(tracker);
7439
- me.dragStartX = primary.x;
7440
- }
8903
+ const remainingPointer = tracker.list.length === 1 ? getPrimary(tracker) : null;
7441
8904
  const endScale = pinch.scale || 1;
7442
8905
  const actualEndZoom = me.timescaleManager.zoom;
7443
8906
  const endTotalZoomDelta = actualEndZoom - pinch.initial.zoom;
@@ -7459,9 +8922,18 @@ Timeline.prototype.enableDragging = /* @__PURE__ */ __name(function enableDraggi
7459
8922
  }),
7460
8923
  evt
7461
8924
  );
7462
- me.settleDateBounds?.();
7463
- if (!me.isPanning) {
7464
- me._flushTimelineStateChange?.();
8925
+ if (remainingPointer) {
8926
+ if (me.isDragging) {
8927
+ me.resetDragTracking(remainingPointer);
8928
+ }
8929
+ } else if (me.isDragging) {
8930
+ me.resetDragTracking(getPoint(evt));
8931
+ me.endDragging(evt);
8932
+ } else {
8933
+ me.settleDateBounds?.();
8934
+ if (!me.isPanning) {
8935
+ me._flushTimelineStateChange?.();
8936
+ }
7465
8937
  }
7466
8938
  return;
7467
8939
  }
@@ -7523,31 +8995,51 @@ Timeline.prototype.startDragging = /* @__PURE__ */ __name(function startDragging
7523
8995
  this.stopZoomAnimation?.();
7524
8996
  this.isDragging = true;
7525
8997
  const page = getPoint(evt);
8998
+ Timeline.prototype.resetDragTracking.call(this, page);
8999
+ this.trigger(
9000
+ "viewport-drag-start",
9001
+ makeViewportDragPayload({
9002
+ startToken: this.timescaleManager.startToken,
9003
+ offsetX: this.repositionWindow
9004
+ }),
9005
+ evt
9006
+ );
9007
+ }, "startDragging");
9008
+ Timeline.prototype.resetDragTracking = /* @__PURE__ */ __name(function resetDragTracking(page) {
7526
9009
  this.dragOrigin = { left: page.x, top: page.y };
7527
9010
  this.dragStartX = page.x;
7528
9011
  this.dragStartY = page.y;
7529
9012
  this.dragMomentumSamples = [{ time: Date.now(), x: page.x }];
7530
9013
  this.dragDateBoundsState = null;
7531
9014
  const bounds = getTimelineDateBounds(this);
7532
- const pixelsPerDay = getPixelsPerDay(this);
7533
- if (bounds?.overflow === "elastic" && pixelsPerDay) {
7534
- const leftDayNumber = getDayNumberAtPixel(this, 0);
7535
- const rightDayNumber = getDayNumberAtPixel(this, this.getWidth());
9015
+ if (bounds?.overflow === "elastic") {
9016
+ const pixelsPerDay = getPixelsPerDay(this);
9017
+ if (!pixelsPerDay) {
9018
+ return;
9019
+ }
9020
+ const viewport = getViewportDayPointRange(this);
9021
+ const viewportSpanDays = getDayPointDifference(viewport.start, viewport.end);
9022
+ let leftDayPoint = viewport.start;
9023
+ if (bounds.minPoint && compareDayPoints(viewport.start, bounds.minPoint) < 0) {
9024
+ const overflow = getDayPointDifference(viewport.start, bounds.minPoint);
9025
+ leftDayPoint = addDaysToDayPoint(
9026
+ bounds.minPoint,
9027
+ -getUnresistedElasticOverflowDays(this, overflow)
9028
+ );
9029
+ } else if (bounds.maxPoint && compareDayPoints(viewport.end, bounds.maxPoint) > 0) {
9030
+ const overflow = getDayPointDifference(bounds.maxPoint, viewport.end);
9031
+ leftDayPoint = addDaysToDayPoint(
9032
+ bounds.maxPoint,
9033
+ getUnresistedElasticOverflowDays(this, overflow) - viewportSpanDays
9034
+ );
9035
+ }
7536
9036
  this.dragDateBoundsState = {
7537
- leftDayNumber,
9037
+ leftDayPoint,
7538
9038
  pixelsPerDay,
7539
- viewportSpanDays: rightDayNumber - leftDayNumber
9039
+ viewportSpanDays
7540
9040
  };
7541
9041
  }
7542
- this.trigger(
7543
- "viewport-drag-start",
7544
- makeViewportDragPayload({
7545
- startToken: this.timescaleManager.startToken,
7546
- offsetX: this.repositionWindow
7547
- }),
7548
- evt
7549
- );
7550
- }, "startDragging");
9042
+ }, "resetDragTracking");
7551
9043
  Timeline.prototype.endDragging = /* @__PURE__ */ __name(function endDragging(evt) {
7552
9044
  this.isDragging = false;
7553
9045
  const page = getPoint(evt);
@@ -7592,10 +9084,13 @@ Timeline.prototype.doDrag = /* @__PURE__ */ __name(function doDrag(evt) {
7592
9084
  this.animation.dragRedrawFunction = this.defaultRedraw;
7593
9085
  }
7594
9086
  if (this.dragDateBoundsState) {
7595
- const targetLeftDayNumber = this.dragDateBoundsState.leftDayNumber + cumulativeMovePixelsX / this.dragDateBoundsState.pixelsPerDay;
7596
- setElasticBoundedViewportStartDay(
9087
+ const targetLeftDayPoint = addDaysToDayPoint(
9088
+ this.dragDateBoundsState.leftDayPoint,
9089
+ cumulativeMovePixelsX / this.dragDateBoundsState.pixelsPerDay
9090
+ );
9091
+ setElasticBoundedViewportStartPoint(
7597
9092
  this,
7598
- targetLeftDayNumber,
9093
+ targetLeftDayPoint,
7599
9094
  this.dragDateBoundsState.viewportSpanDays
7600
9095
  );
7601
9096
  } else {
@@ -7782,13 +9277,20 @@ Timeline.prototype.getDmyFromInput = function(dmy) {
7782
9277
  let normalised;
7783
9278
  if (typeof dmy === "string") {
7784
9279
  normalised = Dmy.fromString(dmy);
9280
+ if (!Number.isFinite(normalised.year)) {
9281
+ throw new Error(`Invalid Histropedia date string: ${dmy}`);
9282
+ }
7785
9283
  } else if (dmy instanceof Dmy) {
7786
9284
  normalised = new Dmy(dmy);
7787
9285
  } else {
7788
9286
  normalised = new Dmy({
7789
9287
  year: dmy.year,
7790
9288
  month: dmy.month ?? 1,
7791
- day: dmy.day ?? 1
9289
+ day: dmy.day ?? 1,
9290
+ hour: dmy.hour ?? 0,
9291
+ minute: dmy.minute ?? 0,
9292
+ second: dmy.second ?? 0,
9293
+ millisecond: dmy.millisecond ?? 0
7792
9294
  });
7793
9295
  }
7794
9296
  if (!this.options?.shiftBceDates) {
@@ -7862,7 +9364,7 @@ Timeline.prototype.hasArticle = function(articleId) {
7862
9364
  article = this.articles[i];
7863
9365
  }
7864
9366
  }
7865
- return !isUndefined$1(article);
9367
+ return !isUndefined(article);
7866
9368
  };
7867
9369
  Timeline.prototype.reposition = function(article, drawCycleContext) {
7868
9370
  if (article.isDragging) {
@@ -7906,7 +9408,7 @@ Timeline.prototype.getOriginalPosition = function(article, drawCycleContext) {
7906
9408
  from = 0;
7907
9409
  }
7908
9410
  }
7909
- const style = article._getCurrentStyle();
9411
+ const style = article.getCurrentStyle();
7910
9412
  const lane = this.getLaneById(article.laneId);
7911
9413
  if (this._isLaneLayoutActive() && lane && lane.geometry) {
7912
9414
  return {
@@ -8275,6 +9777,7 @@ function resolveAreaExtents(areaConfig, options = {}) {
8275
9777
  };
8276
9778
  }
8277
9779
  __name(resolveAreaExtents, "resolveAreaExtents");
9780
+ const MAIN_RANGE_RIGHT_BUFFER = 150;
8278
9781
  Timeline.prototype.defaultRedraw = function() {
8279
9782
  this.redraw({
8280
9783
  // 1. update isVisibleInGroup and after articles IsInRange is decided
@@ -8380,7 +9883,6 @@ Timeline.prototype.redraw = function(callbacks) {
8380
9883
  });
8381
9884
  };
8382
9885
  Timeline.prototype.render = function(ctx, top, width, markers, drawCycleContext) {
8383
- const halfMainlineSize = this.options.style.mainLine.size >> 1;
8384
9886
  const effectiveVerticalOffset = this._getEffectiveVerticalOffset ? this._getEffectiveVerticalOffset() : this.options.verticalOffset;
8385
9887
  if (!this.owner) {
8386
9888
  ctx.clearRect(0, 0, width, this.top + effectiveVerticalOffset);
@@ -8392,19 +9894,7 @@ Timeline.prototype.render = function(ctx, top, width, markers, drawCycleContext)
8392
9894
  this.drawDraggingHighlight(ctx, top, width);
8393
9895
  const mainLineTop = this.options.style.mainLine.position === "top" ? -effectiveVerticalOffset : top;
8394
9896
  this.drawMainline(ctx, mainLineTop, width, markers);
8395
- if (this.options.style.mainLine.showDateIndicators) {
8396
- ctx.beginPath();
8397
- this.forInRangeArticles(/* @__PURE__ */ __name(function redraw_articles_drawIndicators(article) {
8398
- if (!article.isHiddenByFilter && article.isInView) {
8399
- ctx.moveTo(article.indicator.fromX, top - halfMainlineSize);
8400
- ctx.lineTo(article.indicator.fromX, top + halfMainlineSize);
8401
- }
8402
- }, "redraw_articles_drawIndicators"));
8403
- ctx.lineWidth = 3;
8404
- ctx.globalAlpha = 0.75;
8405
- ctx.strokeStyle = "#303030";
8406
- ctx.stroke();
8407
- }
9897
+ this.drawDateIndicators(ctx, mainLineTop);
8408
9898
  ctx.globalAlpha = 1;
8409
9899
  for (let i = 0; i < this.articles.length; i++) {
8410
9900
  const article = this.articles[i];
@@ -8434,7 +9924,53 @@ Timeline.prototype.drawDebugOverlays = function(ctx, width, drawCycleContext) {
8434
9924
  this.drawDensityRegionBoundaries(ctx, width, drawCycleContext);
8435
9925
  }
8436
9926
  };
9927
+ function resolveDateIndicatorColor(timeline, article, color, useActiveArticleColor) {
9928
+ if (color !== "article") {
9929
+ return color;
9930
+ }
9931
+ if (useActiveArticleColor) {
9932
+ return article.activeStyle?.color || article.style?.color || timeline.options.article.defaultStyle.color;
9933
+ }
9934
+ return article.style?.color || timeline.options.article.defaultStyle.color;
9935
+ }
9936
+ __name(resolveDateIndicatorColor, "resolveDateIndicatorColor");
9937
+ Timeline.prototype.drawDateIndicators = function(ctx, top) {
9938
+ const dateIndicators = this.options.style.mainLine.dateIndicators;
9939
+ if (!dateIndicators.visible) {
9940
+ return;
9941
+ }
9942
+ const mainLineSize = this.options.style.mainLine.size;
9943
+ const halfMainLineSize = mainLineSize / 2;
9944
+ ctx.save();
9945
+ ctx.globalAlpha = 1;
9946
+ this.forInRangeArticles(/* @__PURE__ */ __name(function redraw_articles_drawIndicators(article) {
9947
+ if (article.isHiddenByFilter || !article.isInView) {
9948
+ return;
9949
+ }
9950
+ const isActive = article.isActive === true;
9951
+ const isDensityHidden = !isActive && article.isVisibleInLane !== false && article.isVisibleInGroup === false;
9952
+ const stateStyle = isActive ? dateIndicators.activeStyle : isDensityHidden ? dateIndicators.hiddenStyle : null;
9953
+ const style = stateStyle ? { ...dateIndicators, ...stateStyle } : dateIndicators;
9954
+ const size = style.size;
9955
+ const halfSize = size / 2;
9956
+ const x = article.indicator.fromX;
9957
+ const useActiveArticleColor = isActive && stateStyle?.color === "article";
9958
+ ctx.globalAlpha = style.opacity ?? 1;
9959
+ ctx.fillStyle = resolveDateIndicatorColor(this, article, style.color, useActiveArticleColor);
9960
+ if (style.shape === "circle") {
9961
+ ctx.beginPath();
9962
+ ctx.arc(x, top, halfSize, 0, Math.PI * 2);
9963
+ ctx.fill();
9964
+ return;
9965
+ }
9966
+ ctx.fillRect(x - halfSize, top - halfMainLineSize, size, mainLineSize);
9967
+ }, "redraw_articles_drawIndicators"));
9968
+ ctx.restore();
9969
+ };
8437
9970
  Timeline.prototype.getDensityRegionBoundaryDate = function(groupIndex) {
9971
+ if (isSubdayDensityGroupIndex(groupIndex)) {
9972
+ return getDmyFromDayNumber(groupIndex.day);
9973
+ }
8438
9974
  const step = this.currentDensitySetting.step;
8439
9975
  if (this.timescaleManager.unit >= ZOOM_MONTH && step.months !== 0) {
8440
9976
  const monthIndex = groupIndex * step.months;
@@ -8459,14 +9995,28 @@ Timeline.prototype.getDensityRegionBoundaryDate = function(groupIndex) {
8459
9995
  }
8460
9996
  return boundary;
8461
9997
  };
9998
+ Timeline.prototype.getDensityRegionBoundaryPoint = function(groupIndex) {
9999
+ if (isSubdayDensityGroupIndex(groupIndex)) {
10000
+ const boundary = getSubdayDensityGroupBoundary(groupIndex);
10001
+ return {
10002
+ date: getDmyFromDayNumber(boundary.dayNumber),
10003
+ dayFraction: boundary.dayFraction
10004
+ };
10005
+ }
10006
+ return {
10007
+ date: this.getDensityRegionBoundaryDate(groupIndex),
10008
+ dayFraction: 0
10009
+ };
10010
+ };
8462
10011
  Timeline.prototype.drawDensityRegionBoundaries = function(ctx, width, drawCycleContext) {
8463
10012
  const tokens = drawCycleContext?.tokens;
8464
10013
  if (!tokens?.start || !tokens?.end || !this.currentDensitySetting?.step) {
8465
10014
  return;
8466
10015
  }
8467
- const firstGroup = this.calculateGroupIndex(tokens.start) - 1;
8468
- const lastGroup = this.calculateGroupIndex(tokens.end) + 1;
10016
+ const firstGroup = shiftDensityGroupIndex(this.calculateGroupIndex(tokens.start), -1);
10017
+ const lastGroup = shiftDensityGroupIndex(this.calculateGroupIndex(tokens.end), 1);
8469
10018
  const startDate = this.timescaleManager.startToken.value;
10019
+ const pixelsPerDay = getPixelsPerDay(this);
8470
10020
  const height = this.options.height;
8471
10021
  let previousX = null;
8472
10022
  ctx.save();
@@ -8475,9 +10025,9 @@ Timeline.prototype.drawDensityRegionBoundaries = function(ctx, width, drawCycleC
8475
10025
  ctx.lineWidth = 1;
8476
10026
  ctx.strokeStyle = "rgba(204, 0, 153, 0.8)";
8477
10027
  ctx.setLineDash([4, 4]);
8478
- for (let groupIndex = firstGroup; groupIndex <= lastGroup; groupIndex++) {
8479
- const boundary = this.getDensityRegionBoundaryDate(groupIndex);
8480
- const rawX = this.timescaleManager.getPixelsBetween(startDate, boundary) + this.repositionWindow;
10028
+ for (let groupIndex = firstGroup; compareDensityGroupIndexes(groupIndex, lastGroup) <= 0; groupIndex = shiftDensityGroupIndex(groupIndex, 1)) {
10029
+ const boundary = this.getDensityRegionBoundaryPoint(groupIndex);
10030
+ const rawX = this.timescaleManager.getPixelsBetween(startDate, boundary.date) + boundary.dayFraction * pixelsPerDay + this.repositionWindow;
8481
10031
  const x = Math.round(rawX) + 0.5;
8482
10032
  if (x < 0 || x > width || x === previousX) {
8483
10033
  continue;
@@ -8493,23 +10043,26 @@ Timeline.prototype.drawMainline = function(ctx, top, width, markers) {
8493
10043
  if (this.options.style.mainLine.visible) {
8494
10044
  ctx.globalAlpha = 1;
8495
10045
  const halfMainlineSize = this.options.style.mainLine.size >> 1;
8496
- const mainLineGradient = ctx.createLinearGradient(
8497
- 0,
8498
- top - halfMainlineSize,
8499
- 0,
8500
- top + halfMainlineSize
8501
- );
8502
- mainLineGradient.addColorStop(0, "#808080");
8503
- mainLineGradient.addColorStop(1, "#ccc");
10046
+ const color = this.options.style.mainLine.color;
10047
+ let fillStyle = typeof color === "string" ? color : DEFAULT_MAIN_LINE_COLOR;
10048
+ if (color?.type === "gradient" && typeof color.topColor === "string" && typeof color.bottomColor === "string") {
10049
+ fillStyle = ctx.createLinearGradient(0, top - halfMainlineSize, 0, top + halfMainlineSize);
10050
+ fillStyle.addColorStop(0, color.topColor);
10051
+ fillStyle.addColorStop(1, color.bottomColor);
10052
+ }
8504
10053
  ctx.beginPath();
8505
10054
  ctx.lineWidth = this.options.style.mainLine.size;
8506
- ctx.strokeStyle = ctx.fillStyle = mainLineGradient;
10055
+ ctx.strokeStyle = ctx.fillStyle = fillStyle;
8507
10056
  ctx.moveTo(0, top);
8508
10057
  ctx.lineTo(width, top);
8509
10058
  ctx.stroke();
8510
10059
  for (const type in markers) {
8511
- this.drawMarkersAndLabels(ctx, markers[type], type, top + halfMainlineSize);
10060
+ this.drawMarkersAndLabels(ctx, markers[type], type, top + halfMainlineSize, {
10061
+ minorTokens: markers[MINOR_MARKER],
10062
+ viewportWidth: width
10063
+ });
8512
10064
  }
10065
+ this.drawParentDateLabels(ctx, top + halfMainlineSize, width);
8513
10066
  }
8514
10067
  };
8515
10068
  Timeline.prototype.drawDraggingHighlight = function(ctx, top, width) {
@@ -8583,24 +10136,196 @@ Timeline.prototype.getTokensAndMarkers = function(width) {
8583
10136
  const markers = {};
8584
10137
  markers[MAJOR_MARKER] = [];
8585
10138
  markers[MINOR_MARKER] = [];
10139
+ const visualMarkerLayout = this.timescaleManager.getSubdayVisualMarkerLayout?.();
8586
10140
  let x = this.repositionWindow;
8587
10141
  let token = this.timescaleManager.startToken;
8588
10142
  tokens.start = token.value;
8589
- let endToken = token;
8590
- const leftBufferNumberOfTokens = 9;
8591
- token = this.timescaleManager.getPreviousNth(token, leftBufferNumberOfTokens);
8592
- x -= token.length * leftBufferNumberOfTokens;
10143
+ const minimumLeftBufferTokens = 9;
10144
+ let bufferedTokenCount = 0;
10145
+ let hasMajorBeforeViewport = token.type === MAJOR_MARKER && x <= 0;
10146
+ while (bufferedTokenCount < minimumLeftBufferTokens || !hasMajorBeforeViewport) {
10147
+ token = this.timescaleManager.getPrevious(token);
10148
+ x -= token.length;
10149
+ bufferedTokenCount++;
10150
+ if (token.type === MAJOR_MARKER && x <= 0) {
10151
+ hasMajorBeforeViewport = true;
10152
+ }
10153
+ }
8593
10154
  do {
8594
- markers[token.type].push(token);
8595
- tokens[token.value.toKeyString()] = token.drawX = x;
10155
+ token.drawX = x;
10156
+ tokens[token.value.toKeyString()] = x;
10157
+ if (!visualMarkerLayout) {
10158
+ markers[token.type].push(token);
10159
+ }
8596
10160
  x += token.length;
8597
- endToken = token;
8598
10161
  token = this.timescaleManager.getNext(token);
8599
- } while (x < width + 150);
8600
- tokens.end = endToken.value;
10162
+ } while (x < width + MAIN_RANGE_RIGHT_BUFFER);
10163
+ if (visualMarkerLayout) {
10164
+ const visualMarkers = this.timescaleManager.createSubdayVisualMarkers(
10165
+ width,
10166
+ this.repositionWindow,
10167
+ visualMarkerLayout
10168
+ );
10169
+ markers[MAJOR_MARKER] = visualMarkers[MAJOR_MARKER];
10170
+ markers[MINOR_MARKER] = visualMarkers[MINOR_MARKER];
10171
+ }
10172
+ tokens.end = this.getPivotPoint(width + MAIN_RANGE_RIGHT_BUFFER).date;
8601
10173
  return { tokens, markers };
8602
10174
  };
8603
- Timeline.prototype.drawMarkersAndLabels = function(ctx, tokens, tokenType, markerTop) {
10175
+ function getLabelMetrics(ctx, token, labelStyle) {
10176
+ const width = ctx.measureText(token.label).width;
10177
+ const offsetX = Number.isFinite(labelStyle.offset?.x) ? labelStyle.offset.x : 0;
10178
+ const anchorX = Math.round(token.drawX) + offsetX;
10179
+ let left = anchorX;
10180
+ if (labelStyle.textAlign === "center") {
10181
+ left -= width / 2;
10182
+ } else if (labelStyle.textAlign === "right" || labelStyle.textAlign === "end") {
10183
+ left -= width;
10184
+ }
10185
+ return { left, width };
10186
+ }
10187
+ __name(getLabelMetrics, "getLabelMetrics");
10188
+ function getTextAnchorForLeft(left, width, textAlign) {
10189
+ if (textAlign === "center") {
10190
+ return left + width / 2;
10191
+ }
10192
+ if (textAlign === "right" || textAlign === "end") {
10193
+ return left + width;
10194
+ }
10195
+ return left;
10196
+ }
10197
+ __name(getTextAnchorForLeft, "getTextAnchorForLeft");
10198
+ function isStickyMajorLabelEnabled(stickyStyle, tokens) {
10199
+ if (stickyStyle?.enabled === true) {
10200
+ return true;
10201
+ }
10202
+ if (stickyStyle?.enabled !== "auto") {
10203
+ return false;
10204
+ }
10205
+ return Number.isFinite(tokens[0]?.unit) && tokens[0].unit <= ZOOM_MONTH;
10206
+ }
10207
+ __name(isStickyMajorLabelEnabled, "isStickyMajorLabelEnabled");
10208
+ const MAJOR_LABEL_MINIMUM_GAP = 8;
10209
+ function getStickyTextLabel(ctx, tokens, labelStyle, viewportWidth) {
10210
+ if (labelStyle.sticky?.enabled !== true || !Number.isFinite(viewportWidth)) {
10211
+ return null;
10212
+ }
10213
+ const stickyLeft = Number.isFinite(labelStyle.sticky.offsetX) ? labelStyle.sticky.offsetX : 0;
10214
+ let candidateIndex = -1;
10215
+ let candidateMetrics;
10216
+ for (let i = 0; i < tokens.length; i++) {
10217
+ const metrics = getLabelMetrics(ctx, tokens[i], labelStyle);
10218
+ if (metrics.left <= stickyLeft) {
10219
+ candidateIndex = i;
10220
+ candidateMetrics = metrics;
10221
+ }
10222
+ }
10223
+ if (candidateIndex < 0) {
10224
+ return null;
10225
+ }
10226
+ const token = tokens[candidateIndex];
10227
+ const nextToken = tokens[candidateIndex + 1];
10228
+ if (token.drawX > viewportWidth || !nextToken && token.drawX + token.length < 0) {
10229
+ return null;
10230
+ }
10231
+ let labelLeft = stickyLeft;
10232
+ if (nextToken) {
10233
+ const nextMetrics = getLabelMetrics(ctx, nextToken, labelStyle);
10234
+ labelLeft = Math.min(
10235
+ labelLeft,
10236
+ nextMetrics.left - candidateMetrics.width - MAJOR_LABEL_MINIMUM_GAP
10237
+ );
10238
+ }
10239
+ return {
10240
+ token,
10241
+ labelX: getTextAnchorForLeft(labelLeft, candidateMetrics.width, labelStyle.textAlign)
10242
+ };
10243
+ }
10244
+ __name(getStickyTextLabel, "getStickyTextLabel");
10245
+ function getStickyMajorLabel(ctx, tokens, labelStyle, drawOptions) {
10246
+ const stickyStyle = labelStyle.sticky;
10247
+ const minorTokens = drawOptions.minorTokens || [];
10248
+ const viewportWidth = drawOptions.viewportWidth;
10249
+ if (!isStickyMajorLabelEnabled(stickyStyle, tokens) || !Number.isFinite(viewportWidth)) {
10250
+ return null;
10251
+ }
10252
+ const stickyLeft = Number.isFinite(stickyStyle.offsetX) ? stickyStyle.offsetX : 0;
10253
+ let candidateIndex = -1;
10254
+ let candidateMetrics;
10255
+ for (let i = 0; i < tokens.length; i++) {
10256
+ const metrics = getLabelMetrics(ctx, tokens[i], labelStyle);
10257
+ if (metrics.left <= stickyLeft) {
10258
+ candidateIndex = i;
10259
+ candidateMetrics = metrics;
10260
+ }
10261
+ }
10262
+ if (candidateIndex < 0) {
10263
+ return null;
10264
+ }
10265
+ const token = tokens[candidateIndex];
10266
+ const nextToken = tokens[candidateIndex + 1];
10267
+ const hasVisibleInterval = token.drawX <= viewportWidth && (nextToken ? nextToken.drawX >= 0 : token.visual === true && token.drawX + token.length >= 0 || minorTokens.some(
10268
+ (minorToken) => minorToken.drawX >= 0 && minorToken.drawX <= viewportWidth && minorToken.drawX > token.drawX
10269
+ ));
10270
+ if (!hasVisibleInterval) {
10271
+ return null;
10272
+ }
10273
+ let labelLeft = stickyLeft;
10274
+ if (nextToken) {
10275
+ const nextMetrics = getLabelMetrics(ctx, nextToken, labelStyle);
10276
+ labelLeft = Math.min(
10277
+ labelLeft,
10278
+ nextMetrics.left - candidateMetrics.width - MAJOR_LABEL_MINIMUM_GAP
10279
+ );
10280
+ }
10281
+ return {
10282
+ token,
10283
+ labelX: getTextAnchorForLeft(labelLeft, candidateMetrics.width, labelStyle.textAlign)
10284
+ };
10285
+ }
10286
+ __name(getStickyMajorLabel, "getStickyMajorLabel");
10287
+ Timeline.prototype.drawParentDateLabels = function(ctx, markerTop, viewportWidth) {
10288
+ const labelStyle = this.options.style.dateLabel.parent;
10289
+ if (!this.timescaleManager.isParentContextTextVisible(labelStyle)) {
10290
+ return;
10291
+ }
10292
+ const visibleStart = this.getPivotPoint(0).date;
10293
+ const tokens = this.timescaleManager.createParentContextTokens(
10294
+ visibleStart,
10295
+ viewportWidth,
10296
+ this.repositionWindow
10297
+ );
10298
+ if (tokens.length === 0) {
10299
+ return;
10300
+ }
10301
+ const markerDirection = this.options.style.mainLine.position === "top" ? -1 : 1;
10302
+ const majorStyle = this.options.style.dateLabel.major;
10303
+ const majorMarkerHeight = this.options.style.marker.major.height;
10304
+ const labelY = markerTop + majorMarkerHeight * markerDirection + majorStyle.offset.y + labelStyle.height * markerDirection + labelStyle.offset.y;
10305
+ ctx.font = labelStyle.font;
10306
+ ctx.textAlign = labelStyle.textAlign;
10307
+ ctx.textBaseline = markerDirection === 1 ? "alphabetic" : "top";
10308
+ const stickyLabel = getStickyTextLabel(ctx, tokens, labelStyle, viewportWidth);
10309
+ let previousLabelRight = Number.NEGATIVE_INFINITY;
10310
+ for (let i = 0; i < tokens.length; i++) {
10311
+ const token = tokens[i];
10312
+ const labelX = stickyLabel?.token === token ? stickyLabel.labelX : Math.round(token.drawX) + labelStyle.offset.x;
10313
+ const labelWidth = ctx.measureText(token.label).width;
10314
+ let labelLeft = labelX;
10315
+ if (labelStyle.textAlign === "center") {
10316
+ labelLeft -= labelWidth / 2;
10317
+ } else if (labelStyle.textAlign === "right" || labelStyle.textAlign === "end") {
10318
+ labelLeft -= labelWidth;
10319
+ }
10320
+ if (stickyLabel?.token !== token && labelLeft < previousLabelRight + MAJOR_LABEL_MINIMUM_GAP) {
10321
+ continue;
10322
+ }
10323
+ ctx.fillStyle = token.value.isInFuture() ? labelStyle.futureColor : labelStyle.color;
10324
+ ctx.fillText(token.label, labelX, labelY);
10325
+ previousLabelRight = labelLeft + labelWidth;
10326
+ }
10327
+ };
10328
+ Timeline.prototype.drawMarkersAndLabels = function(ctx, tokens, tokenType, markerTop, drawOptions = {}) {
8604
10329
  if (tokens.length === 0) {
8605
10330
  return;
8606
10331
  }
@@ -8618,7 +10343,7 @@ Timeline.prototype.drawMarkersAndLabels = function(ctx, tokens, tokenType, marke
8618
10343
  const isExtended = markerStyle.extend;
8619
10344
  const markerDirection = this.options.style.mainLine.position === "top" ? -1 : 1;
8620
10345
  const markerHeight = markerStyle.height;
8621
- const withLabel = !!tokens[0].label;
10346
+ const withLabel = tokens.some((token) => token.label);
8622
10347
  let isInFuture = tokens[0].value.isInFuture();
8623
10348
  let wasInPast = !isInFuture;
8624
10349
  let labelStyle;
@@ -8633,6 +10358,7 @@ Timeline.prototype.drawMarkersAndLabels = function(ctx, tokens, tokenType, marke
8633
10358
  ctx.textBaseline = markerDirection === 1 ? "alphabetic" : "top";
8634
10359
  labelY = markerTop + markerHeight * markerDirection + labelStyle.offset.y;
8635
10360
  }
10361
+ const stickyLabel = withLabel && tokenType === "major" ? getStickyMajorLabel(ctx, tokens, labelStyle, drawOptions) : null;
8636
10362
  ctx.beginPath();
8637
10363
  for (let i = 0; i < tokens.length; i++) {
8638
10364
  const token = tokens[i];
@@ -8649,7 +10375,7 @@ Timeline.prototype.drawMarkersAndLabels = function(ctx, tokens, tokenType, marke
8649
10375
  const markerEndY = isExtended ? markerTop + -1 * markerDirection * this.top : markerTop;
8650
10376
  ctx.lineTo(drawX, markerEndY);
8651
10377
  if (withLabel && token.label) {
8652
- const labelX = drawX + labelStyle.offset.x;
10378
+ const labelX = stickyLabel?.token === token ? stickyLabel.labelX : drawX + labelStyle.offset.x;
8653
10379
  ctx.fillStyle = isInFuture ? labelStyle.futureColor : labelStyle.color;
8654
10380
  ctx.fillText(token.label, labelX, labelY);
8655
10381
  }
@@ -8705,6 +10431,45 @@ Timeline.prototype._getResolvedArticleOptions = function(articleOrData) {
8705
10431
  const lane = this.getLaneById(laneId);
8706
10432
  return getResolvedLaneArticleOptions(this, lane);
8707
10433
  };
10434
+ Timeline.prototype._getResolvedArticleStyleOverrides = function(articleOrData, cardLayout) {
10435
+ const laneId = articleOrData && articleOrData.laneId != null ? articleOrData.laneId : this._resolveLaneIdFromArticleData(articleOrData);
10436
+ const lane = this.getLaneById(laneId);
10437
+ const baseOptions = ENGINE_DEFAULTS.article || {};
10438
+ const layoutDefaults = this._registeredArticleLayoutStyles?.[cardLayout] || {};
10439
+ const timelineOptions = this._explicitArticleOptions || {};
10440
+ const timelineLayoutStyles = timelineOptions.layoutStyles?.[cardLayout] || {};
10441
+ const laneOptions = lane?.articleOptions || {};
10442
+ const laneLayoutStyles = laneOptions.layoutStyles?.[cardLayout] || {};
10443
+ return {
10444
+ style: deepMerge(
10445
+ {},
10446
+ baseOptions.defaultStyle || {},
10447
+ layoutDefaults.style || {},
10448
+ timelineOptions.defaultStyle || {},
10449
+ timelineLayoutStyles.style || {},
10450
+ laneOptions.defaultStyle || {},
10451
+ laneLayoutStyles.style || {}
10452
+ ),
10453
+ hoverStyle: deepMerge(
10454
+ {},
10455
+ baseOptions.defaultHoverStyle || {},
10456
+ layoutDefaults.hoverStyle || {},
10457
+ timelineOptions.defaultHoverStyle || {},
10458
+ timelineLayoutStyles.hoverStyle || {},
10459
+ laneOptions.defaultHoverStyle || {},
10460
+ laneLayoutStyles.hoverStyle || {}
10461
+ ),
10462
+ activeStyle: deepMerge(
10463
+ {},
10464
+ baseOptions.defaultActiveStyle || {},
10465
+ layoutDefaults.activeStyle || {},
10466
+ timelineOptions.defaultActiveStyle || {},
10467
+ timelineLayoutStyles.activeStyle || {},
10468
+ laneOptions.defaultActiveStyle || {},
10469
+ laneLayoutStyles.activeStyle || {}
10470
+ )
10471
+ };
10472
+ };
8708
10473
  Timeline.prototype._getExplicitArticleCardLayout = function(articleOrData) {
8709
10474
  if (!articleOrData) {
8710
10475
  return null;
@@ -10052,6 +11817,7 @@ const WHEEL_ZOOM_STATE_CHANGE_DELAY = 120;
10052
11817
  const FRAME_MS = 1e3 / 60;
10053
11818
  const STANDARD_WHEEL_NOTCH_DELTA = 120;
10054
11819
  const MIN_BROWSER_ZOOM_SCALED_NOTCH_DELTA = 24;
11820
+ const MILLISECOND_BOUNDARY_EPSILON = 1e-7;
10055
11821
  const BROWSER_ZOOM_SCALE_FACTORS = [
10056
11822
  0.25,
10057
11823
  1 / 3,
@@ -10322,7 +12088,8 @@ Timeline.prototype.setZoomByPivotPixel = /* @__PURE__ */ __name(function setZoom
10322
12088
  Timeline.prototype.setZoomByPivotPoint = /* @__PURE__ */ __name(function setZoomByPivotPoint(newZoom, pivotPoint, pivotPixel, offsetX = 0) {
10323
12089
  this.timescaleManager.setZoom(newZoom);
10324
12090
  const compensation = this.timescaleManager.setStartDate(pivotPoint.date);
10325
- this.repositionWindow = compensation - pivotPoint.fraction;
12091
+ const residualPixels = Number.isFinite(pivotPoint.millisecondFraction) ? pivotPoint.millisecondFraction * getPixelsPerDay(this) / MILLISECONDS_PER_DAY : pivotPoint.fraction;
12092
+ this.repositionWindow = compensation - residualPixels;
10326
12093
  this.updateFromByPixels(-pivotPixel + offsetX);
10327
12094
  applyTimelineDateBounds(this, {
10328
12095
  mode: this.isDragging || this.isPinching ? "elastic" : "clamp"
@@ -10346,7 +12113,23 @@ Timeline.prototype.getPivotPoint = /* @__PURE__ */ __name(function getPivotPoint
10346
12113
  let fraction = pixel - (x - token.length);
10347
12114
  const dmy = prevToken.value;
10348
12115
  let { year, month, day } = dmy;
10349
- if (unit === ZOOM_MONTH) {
12116
+ if (unit <= ZOOM_DAY) {
12117
+ const pixelsPerMillisecond = getPixelsPerDay(this) / MILLISECONDS_PER_DAY;
12118
+ let millisecondsFromToken = pixelsPerMillisecond > 0 ? fraction / pixelsPerMillisecond : 0;
12119
+ const nearestMillisecond = Math.round(millisecondsFromToken);
12120
+ if (Math.abs(millisecondsFromToken - nearestMillisecond) < MILLISECOND_BOUNDARY_EPSILON) {
12121
+ millisecondsFromToken = nearestMillisecond;
12122
+ }
12123
+ const wholeMilliseconds = Math.floor(millisecondsFromToken);
12124
+ const millisecondFraction = millisecondsFromToken - wholeMilliseconds;
12125
+ prevToken.value = prevToken.value.addMilliseconds(wholeMilliseconds);
12126
+ fraction -= wholeMilliseconds * pixelsPerMillisecond;
12127
+ return {
12128
+ date: prevToken.value,
12129
+ fraction,
12130
+ millisecondFraction
12131
+ };
12132
+ } else if (unit === ZOOM_MONTH) {
10350
12133
  day = Math.floor(fraction / len * 30.44) + 1;
10351
12134
  fraction -= (day - 1) * len / 30.44;
10352
12135
  prevToken.value = new Dmy(year, month, day);
@@ -10621,10 +12404,7 @@ __name(animateStartDate, "animateStartDate");
10621
12404
  Timeline.prototype.setStartDate = function(dmy, options) {
10622
12405
  const settings = normaliseSetStartDateOptions(options);
10623
12406
  const padding = settings.padding || 0;
10624
- if (typeof dmy === "string") {
10625
- dmy = Dmy.fromString(dmy);
10626
- }
10627
- dmy = this.getDmyFromInput(dmy);
12407
+ dmy = this.getDmyFromInput(normaliseDateInput(dmy));
10628
12408
  if (settings.animation?.active) {
10629
12409
  animateStartDate(this, dmy, padding, settings.animation);
10630
12410
  return;
@@ -10780,44 +12560,6 @@ Timeline.prototype.offMultiple = function(handlers) {
10780
12560
  this.off(event, handlers[event]);
10781
12561
  }
10782
12562
  };
10783
- function roundPixel(value) {
10784
- return Math.round(value);
10785
- }
10786
- __name(roundPixel, "roundPixel");
10787
- function snapPixel(value, dpr = 1) {
10788
- const ratio = typeof dpr === "number" && isFinite(dpr) && dpr > 0 ? dpr : 1;
10789
- return Math.round(value * ratio) / ratio;
10790
- }
10791
- __name(snapPixel, "snapPixel");
10792
- function retainPixel(value) {
10793
- return value;
10794
- }
10795
- __name(retainPixel, "retainPixel");
10796
- function createPixelSnap(dpr = 1, mode = "device") {
10797
- if (mode === false || mode === "none") {
10798
- return retainPixel;
10799
- }
10800
- if (mode === "css") {
10801
- return roundPixel;
10802
- }
10803
- return /* @__PURE__ */ __name(function snapToDevicePixel(value) {
10804
- return snapPixel(value, dpr);
10805
- }, "snapToDevicePixel");
10806
- }
10807
- __name(createPixelSnap, "createPixelSnap");
10808
- function createInnerSpace(outerSpace, borderWidth, snap) {
10809
- if (!snap) {
10810
- snap = roundPixel;
10811
- }
10812
- const halfBorder = borderWidth / 2;
10813
- return {
10814
- left: snap(outerSpace.left + halfBorder),
10815
- top: snap(outerSpace.top + halfBorder),
10816
- width: snap(outerSpace.width - borderWidth),
10817
- height: snap(outerSpace.height - borderWidth)
10818
- };
10819
- }
10820
- __name(createInnerSpace, "createInnerSpace");
10821
12563
  function getPixelSnap$1(article) {
10822
12564
  const dpr = article.owner?.getCanvasDpr?.() || 1;
10823
12565
  const mode = article.owner?.options?.article?.rendering?.pixelSnap;
@@ -10838,7 +12580,7 @@ const landscape = {
10838
12580
  if (!this.isDataLoaded || isNaN(this.position.left)) {
10839
12581
  return;
10840
12582
  }
10841
- const style = this._getCurrentStyle();
12583
+ const style = this.getCurrentStyle();
10842
12584
  const snap = getPixelSnap$1(this);
10843
12585
  const articleOrigin = getSnappedArticleOrigin$1(this);
10844
12586
  const top = articleOrigin.top;
@@ -11014,7 +12756,11 @@ const landscape = {
11014
12756
  }
11015
12757
  }
11016
12758
  if (showStar) {
11017
- const star = this.getIconBox();
12759
+ const box = this.getIconBox();
12760
+ const centreX = snap(box.left + box.width / 2);
12761
+ const centreY = snap(box.top + box.height / 2);
12762
+ const outerRadius = snap(Math.min(box.width, box.height) / 2);
12763
+ const innerRadius = snap(outerRadius / 2);
11018
12764
  let starStroke, starFill;
11019
12765
  if (!this.isStarred) {
11020
12766
  if (this.isMouseOverStar) {
@@ -11033,26 +12779,17 @@ const landscape = {
11033
12779
  starFill = "#fff800";
11034
12780
  }
11035
12781
  }
11036
- drawStar(
11037
- ctx,
11038
- star.centreX,
11039
- star.centreY,
11040
- 5,
11041
- star.innerRadius,
11042
- star.outerRadius,
11043
- starFill,
11044
- starStroke
11045
- );
12782
+ drawStar(ctx, centreX, centreY, 5, innerRadius, outerRadius, starFill, starStroke);
11046
12783
  }
11047
12784
  },
11048
12785
  getWidth() {
11049
- return this._getCurrentStyle().width;
12786
+ return this.getCurrentStyle().width;
11050
12787
  },
11051
12788
  getHeight() {
11052
- return this._getCurrentStyle().height;
12789
+ return this.getCurrentStyle().height;
11053
12790
  },
11054
12791
  getConnectorEnd() {
11055
- const style = this._getCurrentStyle();
12792
+ const style = this.getCurrentStyle();
11056
12793
  const articleOrigin = getSnappedArticleOrigin$1(this);
11057
12794
  return {
11058
12795
  left: articleOrigin.left + style.connectorLine.offsetX,
@@ -11063,7 +12800,7 @@ const landscape = {
11063
12800
  if (this.owner?.options?.article?.star?.visible === false) {
11064
12801
  return false;
11065
12802
  }
11066
- const style = this._getCurrentStyle();
12803
+ const style = this.getCurrentStyle();
11067
12804
  const snap = getPixelSnap$1(this);
11068
12805
  const sideLength = style.star.width;
11069
12806
  const box = {
@@ -11074,10 +12811,6 @@ const landscape = {
11074
12811
  const articleOrigin = getSnappedArticleOrigin$1(this);
11075
12812
  box.left = snap(articleOrigin.left - br / 4 + this._getWidth() - box.width - style.star.margin);
11076
12813
  box.top = snap(articleOrigin.top + br / 4 + style.star.margin);
11077
- box.centreX = snap(box.left + box.width / 2);
11078
- box.centreY = snap(box.top + box.height / 2);
11079
- box.outerRadius = snap(box.width / 2);
11080
- box.innerRadius = snap(box.outerRadius / 2);
11081
12814
  return box;
11082
12815
  },
11083
12816
  defaultStyle: {
@@ -11139,7 +12872,7 @@ __name(getSnappedArticleOrigin, "getSnappedArticleOrigin");
11139
12872
  const portrait = {
11140
12873
  name: "portrait",
11141
12874
  draw(ctx) {
11142
- const style = this._getCurrentStyle();
12875
+ const style = this.getCurrentStyle();
11143
12876
  const snap = getPixelSnap(this);
11144
12877
  const borderWidth = style.border.width;
11145
12878
  const maxBorderWidth = Math.max(
@@ -11378,7 +13111,11 @@ const portrait = {
11378
13111
  ctx.stroke();
11379
13112
  }
11380
13113
  if (this.owner?.options?.article?.star?.visible !== false && (this.isActive || this.isStarred)) {
11381
- const star = getStarBox(this);
13114
+ const box = this.getIconBox();
13115
+ const centreX = snap(box.left + box.width / 2);
13116
+ const centreY = snap(box.top + box.height / 2);
13117
+ const outerRadius = snap(Math.min(box.width, box.height) / 2);
13118
+ const innerRadius = snap(outerRadius / 2);
11382
13119
  let starStroke, starFill;
11383
13120
  if (!this.isStarred) {
11384
13121
  if (this.isMouseOverStar) {
@@ -11397,23 +13134,14 @@ const portrait = {
11397
13134
  starFill = "#fff800";
11398
13135
  }
11399
13136
  }
11400
- drawStar(
11401
- ctx,
11402
- star.centreX,
11403
- star.centreY,
11404
- 5,
11405
- star.innerRadius,
11406
- star.outerRadius,
11407
- starFill,
11408
- starStroke
11409
- );
13137
+ drawStar(ctx, centreX, centreY, 5, innerRadius, outerRadius, starFill, starStroke);
11410
13138
  }
11411
13139
  },
11412
13140
  getWidth() {
11413
- return this._getCurrentStyle().width;
13141
+ return this.getCurrentStyle().width;
11414
13142
  },
11415
13143
  getHeight() {
11416
- const style = this._getCurrentStyle();
13144
+ const style = this.getCurrentStyle();
11417
13145
  const snap = getPixelSnap(this);
11418
13146
  let imageHeight = 0;
11419
13147
  if (this.imageLoaded) {
@@ -11439,7 +13167,7 @@ const portrait = {
11439
13167
  return imageHeight + articleTopHeight + style.border.width;
11440
13168
  },
11441
13169
  getConnectorEnd() {
11442
- const style = this._getCurrentStyle();
13170
+ const style = this.getCurrentStyle();
11443
13171
  const articleOrigin = getSnappedArticleOrigin(this);
11444
13172
  return {
11445
13173
  left: articleOrigin.left + style.connectorLine.offsetX,
@@ -11450,7 +13178,7 @@ const portrait = {
11450
13178
  if (this.owner?.options?.article?.star?.visible === false) {
11451
13179
  return false;
11452
13180
  }
11453
- const style = this._getCurrentStyle();
13181
+ const style = this.getCurrentStyle();
11454
13182
  const snap = getPixelSnap(this);
11455
13183
  const articleOrigin = getSnappedArticleOrigin(this);
11456
13184
  const sideLength = style.star.width;
@@ -11465,36 +13193,16 @@ const portrait = {
11465
13193
  return box;
11466
13194
  }
11467
13195
  };
11468
- function getStarBox(article) {
11469
- const style = article._getCurrentStyle();
11470
- const snap = getPixelSnap(article);
11471
- const articleOrigin = getSnappedArticleOrigin(article);
11472
- const sideLength = style.star.width;
11473
- const box = {
11474
- width: sideLength,
11475
- height: sideLength
11476
- };
11477
- box.left = snap(
11478
- articleOrigin.left - style.topRadius / 4 + article._getWidth() - box.width - style.star.margin
11479
- );
11480
- box.top = snap(articleOrigin.top + style.topRadius / 4 + style.star.margin);
11481
- box.centreX = snap(box.left + box.width / 2);
11482
- box.centreY = snap(box.top + box.height / 2);
11483
- box.outerRadius = snap(box.width / 2);
11484
- box.innerRadius = snap(box.outerRadius / 2);
11485
- return box;
11486
- }
11487
- __name(getStarBox, "getStarBox");
11488
13196
  const _cardLayouts = /* @__PURE__ */ new Map();
11489
13197
  function _injectDimensionFallbacks(layout) {
11490
13198
  if (typeof layout.getWidth !== "function") {
11491
13199
  layout.getWidth = function() {
11492
- return this._getCurrentStyle().width;
13200
+ return this.getCurrentStyle().width;
11493
13201
  };
11494
13202
  }
11495
13203
  if (typeof layout.getHeight !== "function") {
11496
13204
  layout.getHeight = function() {
11497
- return this._getCurrentStyle().height;
13205
+ return this.getCurrentStyle().height;
11498
13206
  };
11499
13207
  }
11500
13208
  if (typeof layout.getIconBox !== "function") {
@@ -11556,6 +13264,19 @@ Timeline.registerCardLayout({ name: "default", aliasOf: "portrait" });
11556
13264
  Timeline.prototype.getStatusForSave = function() {
11557
13265
  const from = this.timescaleManager.startToken.value;
11558
13266
  const articleSaveInfo = [];
13267
+ const fromTime = {};
13268
+ if (from.hour) {
13269
+ fromTime.fhr = from.hour;
13270
+ }
13271
+ if (from.minute) {
13272
+ fromTime.fmi = from.minute;
13273
+ }
13274
+ if (from.second) {
13275
+ fromTime.fs = from.second;
13276
+ }
13277
+ if (from.millisecond) {
13278
+ fromTime.fms = from.millisecond;
13279
+ }
11559
13280
  for (let i = 0; i < this.articles.length; i++) {
11560
13281
  articleSaveInfo.push(this.articles[i].getStatusForSave());
11561
13282
  }
@@ -11568,6 +13289,7 @@ Timeline.prototype.getStatusForSave = function() {
11568
13289
  fy: from.year,
11569
13290
  fm: from.month,
11570
13291
  fd: from.day,
13292
+ ...fromTime,
11571
13293
  w: this.repositionWindow,
11572
13294
  a: articleSaveInfo
11573
13295
  });
@@ -11644,9 +13366,13 @@ export {
11644
13366
  PRECISION_CENTURY,
11645
13367
  PRECISION_DAY,
11646
13368
  PRECISION_DECADE,
13369
+ PRECISION_HOUR,
11647
13370
  PRECISION_MILLENNIUM,
11648
13371
  PRECISION_MILLION_YEARS,
13372
+ PRECISION_MILLISECOND,
13373
+ PRECISION_MINUTE,
11649
13374
  PRECISION_MONTH,
13375
+ PRECISION_SECOND,
11650
13376
  PRECISION_YEAR,
11651
13377
  RANGE_ALL,
11652
13378
  RANGE_SCREEN,
@@ -11661,9 +13387,13 @@ export {
11661
13387
  ZOOM_CENTURY,
11662
13388
  ZOOM_DAY,
11663
13389
  ZOOM_DECADE,
13390
+ ZOOM_HOUR,
11664
13391
  ZOOM_MILLENNIUM,
11665
13392
  ZOOM_MILLION_YEARS,
13393
+ ZOOM_MILLISECOND,
13394
+ ZOOM_MINUTE,
11666
13395
  ZOOM_MONTH,
13396
+ ZOOM_SECOND,
11667
13397
  ZOOM_YEAR,
11668
13398
  buildChartTicks,
11669
13399
  buildLinearTicks,