@d3plus/core 3.1.2 → 3.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /*
2
- @d3plus/core v3.1.1
2
+ @d3plus/core v3.1.2
3
3
  Data visualization made easy. A javascript library that extends the popular D3.js to enable fast and beautiful visualizations.
4
4
  Copyright (c) 2026 D3plus - https://d3plus.org
5
5
  @license MIT
@@ -6442,7 +6442,7 @@
6442
6442
  }
6443
6443
  return 'L';
6444
6444
  }
6445
- function computeBidiLevels$1(str) {
6445
+ function computeBidiLevels(str) {
6446
6446
  const len = str.length;
6447
6447
  if (len === 0) return null;
6448
6448
  // eslint-disable-next-line unicorn/no-new-array
@@ -6557,8 +6557,8 @@
6557
6557
  }
6558
6558
  return levels;
6559
6559
  }
6560
- function computeSegmentLevels$1(normalized, segStarts) {
6561
- const bidiLevels = computeBidiLevels$1(normalized);
6560
+ function computeSegmentLevels(normalized, segStarts) {
6561
+ const bidiLevels = computeBidiLevels(normalized);
6562
6562
  if (bidiLevels === null) return null;
6563
6563
  const segLevels = new Int8Array(segStarts.length);
6564
6564
  for(let i = 0; i < segStarts.length; i++){
@@ -6567,9 +6567,9 @@
6567
6567
  return segLevels;
6568
6568
  }
6569
6569
 
6570
- const collapsibleWhitespaceRunRe$1 = /[ \t\n\r\f]+/g;
6571
- const needsWhitespaceNormalizationRe$1 = /[\t\n\r\f]| {2,}|^ | $/;
6572
- function getWhiteSpaceProfile$1(whiteSpace) {
6570
+ const collapsibleWhitespaceRunRe = /[ \t\n\r\f]+/g;
6571
+ const needsWhitespaceNormalizationRe = /[\t\n\r\f]| {2,}|^ | $/;
6572
+ function getWhiteSpaceProfile(whiteSpace) {
6573
6573
  const mode = whiteSpace ?? 'normal';
6574
6574
  return mode === 'pre-wrap' ? {
6575
6575
  mode,
@@ -6581,9 +6581,9 @@
6581
6581
  preserveHardBreaks: false
6582
6582
  };
6583
6583
  }
6584
- function normalizeWhitespaceNormal$1(text) {
6585
- if (!needsWhitespaceNormalizationRe$1.test(text)) return text;
6586
- let normalized = text.replace(collapsibleWhitespaceRunRe$1, ' ');
6584
+ function normalizeWhitespaceNormal(text) {
6585
+ if (!needsWhitespaceNormalizationRe.test(text)) return text;
6586
+ let normalized = text.replace(collapsibleWhitespaceRunRe, ' ');
6587
6587
  if (normalized.charCodeAt(0) === 0x20) {
6588
6588
  normalized = normalized.slice(1);
6589
6589
  }
@@ -6592,30 +6592,30 @@
6592
6592
  }
6593
6593
  return normalized;
6594
6594
  }
6595
- function normalizeWhitespacePreWrap$1(text) {
6595
+ function normalizeWhitespacePreWrap(text) {
6596
6596
  if (!/[\r\f]/.test(text)) return text.replace(/\r\n/g, '\n');
6597
6597
  return text.replace(/\r\n/g, '\n').replace(/[\r\f]/g, '\n');
6598
6598
  }
6599
- let sharedWordSegmenter$1 = null;
6600
- let segmenterLocale$1;
6601
- function getSharedWordSegmenter$1() {
6602
- if (sharedWordSegmenter$1 === null) {
6603
- sharedWordSegmenter$1 = new Intl.Segmenter(segmenterLocale$1, {
6599
+ let sharedWordSegmenter = null;
6600
+ let segmenterLocale;
6601
+ function getSharedWordSegmenter() {
6602
+ if (sharedWordSegmenter === null) {
6603
+ sharedWordSegmenter = new Intl.Segmenter(segmenterLocale, {
6604
6604
  granularity: 'word'
6605
6605
  });
6606
6606
  }
6607
- return sharedWordSegmenter$1;
6607
+ return sharedWordSegmenter;
6608
6608
  }
6609
- const arabicScriptRe$1 = /\p{Script=Arabic}/u;
6610
- const combiningMarkRe$1 = /\p{M}/u;
6611
- const decimalDigitRe$1 = /\p{Nd}/u;
6612
- function containsArabicScript$1(text) {
6613
- return arabicScriptRe$1.test(text);
6609
+ const arabicScriptRe = /\p{Script=Arabic}/u;
6610
+ const combiningMarkRe = /\p{M}/u;
6611
+ const decimalDigitRe = /\p{Nd}/u;
6612
+ function containsArabicScript(text) {
6613
+ return arabicScriptRe.test(text);
6614
6614
  }
6615
6615
  function isCJKCodePoint(codePoint) {
6616
6616
  return codePoint >= 0x4E00 && codePoint <= 0x9FFF || codePoint >= 0x3400 && codePoint <= 0x4DBF || codePoint >= 0x20000 && codePoint <= 0x2A6DF || codePoint >= 0x2A700 && codePoint <= 0x2B73F || codePoint >= 0x2B740 && codePoint <= 0x2B81F || codePoint >= 0x2B820 && codePoint <= 0x2CEAF || codePoint >= 0x2CEB0 && codePoint <= 0x2EBEF || codePoint >= 0x2EBF0 && codePoint <= 0x2EE5D || codePoint >= 0x2F800 && codePoint <= 0x2FA1F || codePoint >= 0x30000 && codePoint <= 0x3134F || codePoint >= 0x31350 && codePoint <= 0x323AF || codePoint >= 0x323B0 && codePoint <= 0x33479 || codePoint >= 0xF900 && codePoint <= 0xFAFF || codePoint >= 0x3000 && codePoint <= 0x303F || codePoint >= 0x3040 && codePoint <= 0x309F || codePoint >= 0x30A0 && codePoint <= 0x30FF || codePoint >= 0xAC00 && codePoint <= 0xD7AF || codePoint >= 0xFF00 && codePoint <= 0xFFEF;
6617
6617
  }
6618
- function isCJK$1(s) {
6618
+ function isCJK(s) {
6619
6619
  for(let i = 0; i < s.length; i++){
6620
6620
  const first = s.charCodeAt(i);
6621
6621
  if (first < 0x3000) continue;
@@ -6634,7 +6634,7 @@
6634
6634
  }
6635
6635
  function endsWithLineStartProhibitedText(text) {
6636
6636
  const last = getLastCodePoint(text);
6637
- return last !== null && (kinsokuStart$1.has(last) || leftStickyPunctuation$1.has(last));
6637
+ return last !== null && (kinsokuStart.has(last) || leftStickyPunctuation.has(last));
6638
6638
  }
6639
6639
  const keepAllGlueChars = new Set([
6640
6640
  '\u00A0',
@@ -6643,7 +6643,7 @@
6643
6643
  '\uFEFF'
6644
6644
  ]);
6645
6645
  function containsCJKText(text) {
6646
- return isCJK$1(text);
6646
+ return isCJK(text);
6647
6647
  }
6648
6648
  function endsWithKeepAllGlueText(text) {
6649
6649
  const last = getLastCodePoint(text);
@@ -6652,7 +6652,7 @@
6652
6652
  function canContinueKeepAllTextRun(previousText) {
6653
6653
  return !endsWithLineStartProhibitedText(previousText) && !endsWithKeepAllGlueText(previousText);
6654
6654
  }
6655
- const kinsokuStart$1 = new Set([
6655
+ const kinsokuStart = new Set([
6656
6656
  '\uFF0C',
6657
6657
  '\uFF0E',
6658
6658
  '\uFF01',
@@ -6680,7 +6680,7 @@
6680
6680
  '\u30FD',
6681
6681
  '\u30FE'
6682
6682
  ]);
6683
- const kinsokuEnd$1 = new Set([
6683
+ const kinsokuEnd = new Set([
6684
6684
  '"',
6685
6685
  '(',
6686
6686
  '[',
@@ -6700,11 +6700,11 @@
6700
6700
  '\u3018',
6701
6701
  '\u301A'
6702
6702
  ]);
6703
- const forwardStickyGlue$1 = new Set([
6703
+ const forwardStickyGlue = new Set([
6704
6704
  "'",
6705
6705
  '’'
6706
6706
  ]);
6707
- const leftStickyPunctuation$1 = new Set([
6707
+ const leftStickyPunctuation = new Set([
6708
6708
  '.',
6709
6709
  ',',
6710
6710
  '!',
@@ -6732,16 +6732,16 @@
6732
6732
  '›',
6733
6733
  '…'
6734
6734
  ]);
6735
- const arabicNoSpaceTrailingPunctuation$1 = new Set([
6735
+ const arabicNoSpaceTrailingPunctuation = new Set([
6736
6736
  ':',
6737
6737
  '.',
6738
6738
  '\u060C',
6739
6739
  '\u061B'
6740
6740
  ]);
6741
- const myanmarMedialGlue$1 = new Set([
6741
+ const myanmarMedialGlue = new Set([
6742
6742
  '\u104F'
6743
6743
  ]);
6744
- const closingQuoteChars$1 = new Set([
6744
+ const closingQuoteChars = new Set([
6745
6745
  '”',
6746
6746
  '’',
6747
6747
  '»',
@@ -6754,37 +6754,37 @@
6754
6754
  '\u3015',
6755
6755
  '\uFF09'
6756
6756
  ]);
6757
- function isLeftStickyPunctuationSegment$1(segment) {
6758
- if (isEscapedQuoteClusterSegment$1(segment)) return true;
6757
+ function isLeftStickyPunctuationSegment(segment) {
6758
+ if (isEscapedQuoteClusterSegment(segment)) return true;
6759
6759
  let sawPunctuation = false;
6760
6760
  for (const ch of segment){
6761
- if (leftStickyPunctuation$1.has(ch)) {
6761
+ if (leftStickyPunctuation.has(ch)) {
6762
6762
  sawPunctuation = true;
6763
6763
  continue;
6764
6764
  }
6765
- if (sawPunctuation && combiningMarkRe$1.test(ch)) continue;
6765
+ if (sawPunctuation && combiningMarkRe.test(ch)) continue;
6766
6766
  return false;
6767
6767
  }
6768
6768
  return sawPunctuation;
6769
6769
  }
6770
- function isCJKLineStartProhibitedSegment$1(segment) {
6770
+ function isCJKLineStartProhibitedSegment(segment) {
6771
6771
  for (const ch of segment){
6772
- if (!kinsokuStart$1.has(ch) && !leftStickyPunctuation$1.has(ch)) return false;
6772
+ if (!kinsokuStart.has(ch) && !leftStickyPunctuation.has(ch)) return false;
6773
6773
  }
6774
6774
  return segment.length > 0;
6775
6775
  }
6776
- function isForwardStickyClusterSegment$1(segment) {
6777
- if (isEscapedQuoteClusterSegment$1(segment)) return true;
6776
+ function isForwardStickyClusterSegment(segment) {
6777
+ if (isEscapedQuoteClusterSegment(segment)) return true;
6778
6778
  for (const ch of segment){
6779
- if (!kinsokuEnd$1.has(ch) && !forwardStickyGlue$1.has(ch) && !combiningMarkRe$1.test(ch)) return false;
6779
+ if (!kinsokuEnd.has(ch) && !forwardStickyGlue.has(ch) && !combiningMarkRe.test(ch)) return false;
6780
6780
  }
6781
6781
  return segment.length > 0;
6782
6782
  }
6783
- function isEscapedQuoteClusterSegment$1(segment) {
6783
+ function isEscapedQuoteClusterSegment(segment) {
6784
6784
  let sawQuote = false;
6785
6785
  for (const ch of segment){
6786
- if (ch === '\\' || combiningMarkRe$1.test(ch)) continue;
6787
- if (kinsokuEnd$1.has(ch) || leftStickyPunctuation$1.has(ch) || forwardStickyGlue$1.has(ch)) {
6786
+ if (ch === '\\' || combiningMarkRe.test(ch)) continue;
6787
+ if (kinsokuEnd.has(ch) || leftStickyPunctuation.has(ch) || forwardStickyGlue.has(ch)) {
6788
6788
  sawQuote = true;
6789
6789
  continue;
6790
6790
  }
@@ -6807,16 +6807,16 @@
6807
6807
  const start = previousCodePointStart(text, text.length);
6808
6808
  return text.slice(start);
6809
6809
  }
6810
- function splitTrailingForwardStickyCluster$1(text) {
6810
+ function splitTrailingForwardStickyCluster(text) {
6811
6811
  const chars = Array.from(text);
6812
6812
  let splitIndex = chars.length;
6813
6813
  while(splitIndex > 0){
6814
6814
  const ch = chars[splitIndex - 1];
6815
- if (combiningMarkRe$1.test(ch)) {
6815
+ if (combiningMarkRe.test(ch)) {
6816
6816
  splitIndex--;
6817
6817
  continue;
6818
6818
  }
6819
- if (kinsokuEnd$1.has(ch) || forwardStickyGlue$1.has(ch)) {
6819
+ if (kinsokuEnd.has(ch) || forwardStickyGlue.has(ch)) {
6820
6820
  splitIndex--;
6821
6821
  continue;
6822
6822
  }
@@ -6842,13 +6842,13 @@
6842
6842
  return materialized;
6843
6843
  }
6844
6844
  function hasArabicNoSpacePunctuation(containsArabic, lastCodePoint) {
6845
- return containsArabic && lastCodePoint !== null && arabicNoSpaceTrailingPunctuation$1.has(lastCodePoint);
6845
+ return containsArabic && lastCodePoint !== null && arabicNoSpaceTrailingPunctuation.has(lastCodePoint);
6846
6846
  }
6847
- function endsWithMyanmarMedialGlue$1(segment) {
6847
+ function endsWithMyanmarMedialGlue(segment) {
6848
6848
  const lastCodePoint = getLastCodePoint(segment);
6849
- return lastCodePoint !== null && myanmarMedialGlue$1.has(lastCodePoint);
6849
+ return lastCodePoint !== null && myanmarMedialGlue.has(lastCodePoint);
6850
6850
  }
6851
- function splitLeadingSpaceAndMarks$1(segment) {
6851
+ function splitLeadingSpaceAndMarks(segment) {
6852
6852
  if (segment.length < 2 || segment[0] !== ' ') return null;
6853
6853
  const marks = segment.slice(1);
6854
6854
  if (/^\p{M}+$/u.test(marks)) {
@@ -6859,18 +6859,18 @@
6859
6859
  }
6860
6860
  return null;
6861
6861
  }
6862
- function endsWithClosingQuote$1(text) {
6862
+ function endsWithClosingQuote(text) {
6863
6863
  let end = text.length;
6864
6864
  while(end > 0){
6865
6865
  const start = previousCodePointStart(text, end);
6866
6866
  const ch = text.slice(start, end);
6867
- if (closingQuoteChars$1.has(ch)) return true;
6868
- if (!leftStickyPunctuation$1.has(ch)) return false;
6867
+ if (closingQuoteChars.has(ch)) return true;
6868
+ if (!leftStickyPunctuation.has(ch)) return false;
6869
6869
  end = start;
6870
6870
  }
6871
6871
  return false;
6872
6872
  }
6873
- function classifySegmentBreakChar$1(ch, whiteSpaceProfile) {
6873
+ function classifySegmentBreakChar(ch, whiteSpaceProfile) {
6874
6874
  if (whiteSpaceProfile.preserveOrdinarySpaces || whiteSpaceProfile.preserveHardBreaks) {
6875
6875
  if (ch === ' ') return 'preserved-space';
6876
6876
  if (ch === '\t') return 'tab';
@@ -6897,7 +6897,7 @@
6897
6897
  parts.push(tail);
6898
6898
  return joinTextParts(parts);
6899
6899
  }
6900
- function splitSegmentByBreakKind$1(segment, isWordLike, start, whiteSpaceProfile) {
6900
+ function splitSegmentByBreakKind(segment, isWordLike, start, whiteSpaceProfile) {
6901
6901
  if (!breakCharRe.test(segment)) {
6902
6902
  return [
6903
6903
  {
@@ -6915,7 +6915,7 @@
6915
6915
  let currentWordLike = false;
6916
6916
  let offset = 0;
6917
6917
  for (const ch of segment){
6918
- const kind = classifySegmentBreakChar$1(ch, whiteSpaceProfile);
6918
+ const kind = classifySegmentBreakChar(ch, whiteSpaceProfile);
6919
6919
  const wordLike = kind === 'text' && isWordLike;
6920
6920
  if (currentKind !== null && kind === currentKind && wordLike === currentWordLike) {
6921
6921
  currentTextParts.push(ch);
@@ -6948,30 +6948,30 @@
6948
6948
  }
6949
6949
  return pieces;
6950
6950
  }
6951
- function isTextRunBoundary$1(kind) {
6951
+ function isTextRunBoundary(kind) {
6952
6952
  return kind === 'space' || kind === 'preserved-space' || kind === 'zero-width-break' || kind === 'hard-break';
6953
6953
  }
6954
- const urlSchemeSegmentRe$1 = /^[A-Za-z][A-Za-z0-9+.-]*:$/;
6955
- function isUrlLikeRunStart$1(segmentation, index) {
6954
+ const urlSchemeSegmentRe = /^[A-Za-z][A-Za-z0-9+.-]*:$/;
6955
+ function isUrlLikeRunStart(segmentation, index) {
6956
6956
  const text = segmentation.texts[index];
6957
6957
  if (text.startsWith('www.')) return true;
6958
- return urlSchemeSegmentRe$1.test(text) && index + 1 < segmentation.len && segmentation.kinds[index + 1] === 'text' && segmentation.texts[index + 1] === '//';
6958
+ return urlSchemeSegmentRe.test(text) && index + 1 < segmentation.len && segmentation.kinds[index + 1] === 'text' && segmentation.texts[index + 1] === '//';
6959
6959
  }
6960
- function isUrlQueryBoundarySegment$1(text) {
6960
+ function isUrlQueryBoundarySegment(text) {
6961
6961
  return text.includes('?') && (text.includes('://') || text.startsWith('www.'));
6962
6962
  }
6963
- function mergeUrlLikeRuns$1(segmentation) {
6963
+ function mergeUrlLikeRuns(segmentation) {
6964
6964
  const texts = segmentation.texts.slice();
6965
6965
  const isWordLike = segmentation.isWordLike.slice();
6966
6966
  const kinds = segmentation.kinds.slice();
6967
6967
  const starts = segmentation.starts.slice();
6968
6968
  for(let i = 0; i < segmentation.len; i++){
6969
- if (kinds[i] !== 'text' || !isUrlLikeRunStart$1(segmentation, i)) continue;
6969
+ if (kinds[i] !== 'text' || !isUrlLikeRunStart(segmentation, i)) continue;
6970
6970
  const mergedParts = [
6971
6971
  texts[i]
6972
6972
  ];
6973
6973
  let j = i + 1;
6974
- while(j < segmentation.len && !isTextRunBoundary$1(kinds[j])){
6974
+ while(j < segmentation.len && !isTextRunBoundary(kinds[j])){
6975
6975
  mergedParts.push(texts[j]);
6976
6976
  isWordLike[i] = true;
6977
6977
  const endsQueryPrefix = texts[j].includes('?');
@@ -7006,7 +7006,7 @@
7006
7006
  starts
7007
7007
  };
7008
7008
  }
7009
- function mergeUrlQueryRuns$1(segmentation) {
7009
+ function mergeUrlQueryRuns(segmentation) {
7010
7010
  const texts = [];
7011
7011
  const isWordLike = [];
7012
7012
  const kinds = [];
@@ -7017,15 +7017,15 @@
7017
7017
  isWordLike.push(segmentation.isWordLike[i]);
7018
7018
  kinds.push(segmentation.kinds[i]);
7019
7019
  starts.push(segmentation.starts[i]);
7020
- if (!isUrlQueryBoundarySegment$1(text)) continue;
7020
+ if (!isUrlQueryBoundarySegment(text)) continue;
7021
7021
  const nextIndex = i + 1;
7022
- if (nextIndex >= segmentation.len || isTextRunBoundary$1(segmentation.kinds[nextIndex])) {
7022
+ if (nextIndex >= segmentation.len || isTextRunBoundary(segmentation.kinds[nextIndex])) {
7023
7023
  continue;
7024
7024
  }
7025
7025
  const queryParts = [];
7026
7026
  const queryStart = segmentation.starts[nextIndex];
7027
7027
  let j = nextIndex;
7028
- while(j < segmentation.len && !isTextRunBoundary$1(segmentation.kinds[j])){
7028
+ while(j < segmentation.len && !isTextRunBoundary(segmentation.kinds[j])){
7029
7029
  queryParts.push(segmentation.texts[j]);
7030
7030
  j++;
7031
7031
  }
@@ -7045,7 +7045,7 @@
7045
7045
  starts
7046
7046
  };
7047
7047
  }
7048
- const numericJoinerChars$1 = new Set([
7048
+ const numericJoinerChars = new Set([
7049
7049
  ':',
7050
7050
  '-',
7051
7051
  '/',
@@ -7056,23 +7056,23 @@
7056
7056
  '\u2013',
7057
7057
  '\u2014'
7058
7058
  ]);
7059
- const asciiPunctuationChainSegmentRe$1 = /^[A-Za-z0-9_]+[,:;]*$/;
7060
- const asciiPunctuationChainTrailingJoinersRe$1 = /[,:;]+$/;
7061
- function segmentContainsDecimalDigit$1(text) {
7059
+ const asciiPunctuationChainSegmentRe = /^[A-Za-z0-9_]+[,:;]*$/;
7060
+ const asciiPunctuationChainTrailingJoinersRe = /[,:;]+$/;
7061
+ function segmentContainsDecimalDigit(text) {
7062
7062
  for (const ch of text){
7063
- if (decimalDigitRe$1.test(ch)) return true;
7063
+ if (decimalDigitRe.test(ch)) return true;
7064
7064
  }
7065
7065
  return false;
7066
7066
  }
7067
- function isNumericRunSegment$1(text) {
7067
+ function isNumericRunSegment(text) {
7068
7068
  if (text.length === 0) return false;
7069
7069
  for (const ch of text){
7070
- if (decimalDigitRe$1.test(ch) || numericJoinerChars$1.has(ch)) continue;
7070
+ if (decimalDigitRe.test(ch) || numericJoinerChars.has(ch)) continue;
7071
7071
  return false;
7072
7072
  }
7073
7073
  return true;
7074
7074
  }
7075
- function mergeNumericRuns$1(segmentation) {
7075
+ function mergeNumericRuns(segmentation) {
7076
7076
  const texts = [];
7077
7077
  const isWordLike = [];
7078
7078
  const kinds = [];
@@ -7080,12 +7080,12 @@
7080
7080
  for(let i = 0; i < segmentation.len; i++){
7081
7081
  const text = segmentation.texts[i];
7082
7082
  const kind = segmentation.kinds[i];
7083
- if (kind === 'text' && isNumericRunSegment$1(text) && segmentContainsDecimalDigit$1(text)) {
7083
+ if (kind === 'text' && isNumericRunSegment(text) && segmentContainsDecimalDigit(text)) {
7084
7084
  const mergedParts = [
7085
7085
  text
7086
7086
  ];
7087
7087
  let j = i + 1;
7088
- while(j < segmentation.len && segmentation.kinds[j] === 'text' && isNumericRunSegment$1(segmentation.texts[j])){
7088
+ while(j < segmentation.len && segmentation.kinds[j] === 'text' && isNumericRunSegment(segmentation.texts[j])){
7089
7089
  mergedParts.push(segmentation.texts[j]);
7090
7090
  j++;
7091
7091
  }
@@ -7109,7 +7109,7 @@
7109
7109
  starts
7110
7110
  };
7111
7111
  }
7112
- function mergeAsciiPunctuationChains$1(segmentation) {
7112
+ function mergeAsciiPunctuationChains(segmentation) {
7113
7113
  const texts = [];
7114
7114
  const isWordLike = [];
7115
7115
  const kinds = [];
@@ -7118,16 +7118,16 @@
7118
7118
  const text = segmentation.texts[i];
7119
7119
  const kind = segmentation.kinds[i];
7120
7120
  const wordLike = segmentation.isWordLike[i];
7121
- if (kind === 'text' && wordLike && asciiPunctuationChainSegmentRe$1.test(text)) {
7121
+ if (kind === 'text' && wordLike && asciiPunctuationChainSegmentRe.test(text)) {
7122
7122
  const mergedParts = [
7123
7123
  text
7124
7124
  ];
7125
- let endsWithJoiners = asciiPunctuationChainTrailingJoinersRe$1.test(text);
7125
+ let endsWithJoiners = asciiPunctuationChainTrailingJoinersRe.test(text);
7126
7126
  let j = i + 1;
7127
- while(endsWithJoiners && j < segmentation.len && segmentation.kinds[j] === 'text' && segmentation.isWordLike[j] && asciiPunctuationChainSegmentRe$1.test(segmentation.texts[j])){
7127
+ while(endsWithJoiners && j < segmentation.len && segmentation.kinds[j] === 'text' && segmentation.isWordLike[j] && asciiPunctuationChainSegmentRe.test(segmentation.texts[j])){
7128
7128
  const nextText = segmentation.texts[j];
7129
7129
  mergedParts.push(nextText);
7130
- endsWithJoiners = asciiPunctuationChainTrailingJoinersRe$1.test(nextText);
7130
+ endsWithJoiners = asciiPunctuationChainTrailingJoinersRe.test(nextText);
7131
7131
  j++;
7132
7132
  }
7133
7133
  texts.push(joinTextParts(mergedParts));
@@ -7150,7 +7150,7 @@
7150
7150
  starts
7151
7151
  };
7152
7152
  }
7153
- function splitHyphenatedNumericRuns$1(segmentation) {
7153
+ function splitHyphenatedNumericRuns(segmentation) {
7154
7154
  const texts = [];
7155
7155
  const isWordLike = [];
7156
7156
  const kinds = [];
@@ -7163,7 +7163,7 @@
7163
7163
  for(let j = 0; j < parts.length; j++){
7164
7164
  const part = parts[j];
7165
7165
  if (!shouldSplit) break;
7166
- if (part.length === 0 || !segmentContainsDecimalDigit$1(part) || !isNumericRunSegment$1(part)) {
7166
+ if (part.length === 0 || !segmentContainsDecimalDigit(part) || !isNumericRunSegment(part)) {
7167
7167
  shouldSplit = false;
7168
7168
  }
7169
7169
  }
@@ -7194,7 +7194,7 @@
7194
7194
  starts
7195
7195
  };
7196
7196
  }
7197
- function mergeGlueConnectedTextRuns$1(segmentation) {
7197
+ function mergeGlueConnectedTextRuns(segmentation) {
7198
7198
  const texts = [];
7199
7199
  const isWordLike = [];
7200
7200
  const kinds = [];
@@ -7265,15 +7265,15 @@
7265
7265
  starts
7266
7266
  };
7267
7267
  }
7268
- function carryTrailingForwardStickyAcrossCJKBoundary$1(segmentation) {
7268
+ function carryTrailingForwardStickyAcrossCJKBoundary(segmentation) {
7269
7269
  const texts = segmentation.texts.slice();
7270
7270
  const isWordLike = segmentation.isWordLike.slice();
7271
7271
  const kinds = segmentation.kinds.slice();
7272
7272
  const starts = segmentation.starts.slice();
7273
7273
  for(let i = 0; i < texts.length - 1; i++){
7274
7274
  if (kinds[i] !== 'text' || kinds[i + 1] !== 'text') continue;
7275
- if (!isCJK$1(texts[i]) || !isCJK$1(texts[i + 1])) continue;
7276
- const split = splitTrailingForwardStickyCluster$1(texts[i]);
7275
+ if (!isCJK(texts[i]) || !isCJK(texts[i + 1])) continue;
7276
+ const split = splitTrailingForwardStickyCluster(texts[i]);
7277
7277
  if (split === null) continue;
7278
7278
  texts[i] = split.head;
7279
7279
  texts[i + 1] = split.tail + texts[i + 1];
@@ -7287,8 +7287,8 @@
7287
7287
  starts
7288
7288
  };
7289
7289
  }
7290
- function buildMergedSegmentation$1(normalized, profile, whiteSpaceProfile) {
7291
- const wordSegmenter = getSharedWordSegmenter$1();
7290
+ function buildMergedSegmentation(normalized, profile, whiteSpaceProfile) {
7291
+ const wordSegmenter = getSharedWordSegmenter();
7292
7292
  let mergedLen = 0;
7293
7293
  const mergedTexts = [];
7294
7294
  const mergedTextParts = [];
@@ -7305,14 +7305,14 @@
7305
7305
  const mergedEndsWithMyanmarMedialGlue = [];
7306
7306
  const mergedHasArabicNoSpacePunctuation = [];
7307
7307
  for (const s of wordSegmenter.segment(normalized)){
7308
- for (const piece of splitSegmentByBreakKind$1(s.segment, s.isWordLike ?? false, s.index, whiteSpaceProfile)){
7308
+ for (const piece of splitSegmentByBreakKind(s.segment, s.isWordLike ?? false, s.index, whiteSpaceProfile)){
7309
7309
  const isText = piece.kind === 'text';
7310
7310
  const repeatableSingleCharRunChar = getRepeatableSingleCharRunChar(piece.text, piece.isWordLike, piece.kind);
7311
- const pieceContainsCJK = isCJK$1(piece.text);
7312
- const pieceContainsArabicScript = containsArabicScript$1(piece.text);
7311
+ const pieceContainsCJK = isCJK(piece.text);
7312
+ const pieceContainsArabicScript = containsArabicScript(piece.text);
7313
7313
  const pieceLastCodePoint = getLastCodePoint(piece.text);
7314
- const pieceEndsWithClosingQuote = endsWithClosingQuote$1(piece.text);
7315
- const pieceEndsWithMyanmarMedialGlue = endsWithMyanmarMedialGlue$1(piece.text);
7314
+ const pieceEndsWithClosingQuote = endsWithClosingQuote(piece.text);
7315
+ const pieceEndsWithMyanmarMedialGlue = endsWithMyanmarMedialGlue(piece.text);
7316
7316
  const prevIndex = mergedLen - 1;
7317
7317
  function appendPieceToPrevious() {
7318
7318
  if (mergedSingleCharRunChars[prevIndex] !== null) {
@@ -7333,7 +7333,7 @@
7333
7333
  // that depend on the immediately preceding text run.
7334
7334
  if (profile.carryCJKAfterClosingQuote && isText && mergedLen > 0 && mergedKinds[prevIndex] === 'text' && pieceContainsCJK && mergedContainsCJK[prevIndex] && mergedEndsWithClosingQuote[prevIndex]) {
7335
7335
  appendPieceToPrevious();
7336
- } else if (isText && mergedLen > 0 && mergedKinds[prevIndex] === 'text' && isCJKLineStartProhibitedSegment$1(piece.text) && mergedContainsCJK[prevIndex]) {
7336
+ } else if (isText && mergedLen > 0 && mergedKinds[prevIndex] === 'text' && isCJKLineStartProhibitedSegment(piece.text) && mergedContainsCJK[prevIndex]) {
7337
7337
  appendPieceToPrevious();
7338
7338
  } else if (isText && mergedLen > 0 && mergedKinds[prevIndex] === 'text' && mergedEndsWithMyanmarMedialGlue[prevIndex]) {
7339
7339
  appendPieceToPrevious();
@@ -7342,7 +7342,7 @@
7342
7342
  mergedWordLike[prevIndex] = true;
7343
7343
  } else if (repeatableSingleCharRunChar !== null && mergedLen > 0 && mergedKinds[prevIndex] === 'text' && mergedSingleCharRunChars[prevIndex] === repeatableSingleCharRunChar) {
7344
7344
  mergedSingleCharRunLengths[prevIndex] = (mergedSingleCharRunLengths[prevIndex] ?? 1) + 1;
7345
- } else if (isText && !piece.isWordLike && mergedLen > 0 && mergedKinds[prevIndex] === 'text' && (isLeftStickyPunctuationSegment$1(piece.text) || piece.text === '-' && mergedWordLike[prevIndex])) {
7345
+ } else if (isText && !piece.isWordLike && mergedLen > 0 && mergedKinds[prevIndex] === 'text' && (isLeftStickyPunctuationSegment(piece.text) || piece.text === '-' && mergedWordLike[prevIndex])) {
7346
7346
  appendPieceToPrevious();
7347
7347
  } else {
7348
7348
  mergedTexts[mergedLen] = piece.text;
@@ -7374,7 +7374,7 @@
7374
7374
  // quote glue, forward-sticky carry, compaction, then the broader URL/numeric
7375
7375
  // and Arabic-leading-mark fixes.
7376
7376
  for(let i = 1; i < mergedLen; i++){
7377
- if (mergedKinds[i] === 'text' && !mergedWordLike[i] && isEscapedQuoteClusterSegment$1(mergedTexts[i]) && mergedKinds[i - 1] === 'text') {
7377
+ if (mergedKinds[i] === 'text' && !mergedWordLike[i] && isEscapedQuoteClusterSegment(mergedTexts[i]) && mergedKinds[i - 1] === 'text') {
7378
7378
  mergedTexts[i - 1] += mergedTexts[i];
7379
7379
  mergedWordLike[i - 1] = mergedWordLike[i - 1] || mergedWordLike[i];
7380
7380
  mergedTexts[i] = '';
@@ -7387,7 +7387,7 @@
7387
7387
  for(let i = mergedLen - 1; i >= 0; i--){
7388
7388
  const text = mergedTexts[i];
7389
7389
  if (text.length === 0) continue;
7390
- if (mergedKinds[i] === 'text' && !mergedWordLike[i] && isForwardStickyClusterSegment$1(text) && nextLiveIndex >= 0 && mergedKinds[nextLiveIndex] === 'text') {
7390
+ if (mergedKinds[i] === 'text' && !mergedWordLike[i] && isForwardStickyClusterSegment(text) && nextLiveIndex >= 0 && mergedKinds[nextLiveIndex] === 'text') {
7391
7391
  const prefixParts = forwardStickyPrefixParts[nextLiveIndex] ?? [];
7392
7392
  prefixParts.push(text);
7393
7393
  forwardStickyPrefixParts[nextLiveIndex] = prefixParts;
@@ -7418,18 +7418,18 @@
7418
7418
  mergedWordLike.length = compactLen;
7419
7419
  mergedKinds.length = compactLen;
7420
7420
  mergedStarts.length = compactLen;
7421
- const compacted = mergeGlueConnectedTextRuns$1({
7421
+ const compacted = mergeGlueConnectedTextRuns({
7422
7422
  len: compactLen,
7423
7423
  texts: mergedTexts,
7424
7424
  isWordLike: mergedWordLike,
7425
7425
  kinds: mergedKinds,
7426
7426
  starts: mergedStarts
7427
7427
  });
7428
- const withMergedUrls = carryTrailingForwardStickyAcrossCJKBoundary$1(mergeAsciiPunctuationChains$1(splitHyphenatedNumericRuns$1(mergeNumericRuns$1(mergeUrlQueryRuns$1(mergeUrlLikeRuns$1(compacted))))));
7428
+ const withMergedUrls = carryTrailingForwardStickyAcrossCJKBoundary(mergeAsciiPunctuationChains(splitHyphenatedNumericRuns(mergeNumericRuns(mergeUrlQueryRuns(mergeUrlLikeRuns(compacted))))));
7429
7429
  for(let i = 0; i < withMergedUrls.len - 1; i++){
7430
- const split = splitLeadingSpaceAndMarks$1(withMergedUrls.texts[i]);
7430
+ const split = splitLeadingSpaceAndMarks(withMergedUrls.texts[i]);
7431
7431
  if (split === null) continue;
7432
- if (withMergedUrls.kinds[i] !== 'space' && withMergedUrls.kinds[i] !== 'preserved-space' || withMergedUrls.kinds[i + 1] !== 'text' || !containsArabicScript$1(withMergedUrls.texts[i + 1])) {
7432
+ if (withMergedUrls.kinds[i] !== 'space' && withMergedUrls.kinds[i] !== 'preserved-space' || withMergedUrls.kinds[i + 1] !== 'text' || !containsArabicScript(withMergedUrls.texts[i + 1])) {
7433
7433
  continue;
7434
7434
  }
7435
7435
  withMergedUrls.texts[i] = split.space;
@@ -7440,7 +7440,7 @@
7440
7440
  }
7441
7441
  return withMergedUrls;
7442
7442
  }
7443
- function compileAnalysisChunks$1(segmentation, whiteSpaceProfile) {
7443
+ function compileAnalysisChunks(segmentation, whiteSpaceProfile) {
7444
7444
  if (segmentation.len === 0) return [];
7445
7445
  if (!whiteSpaceProfile.preserveHardBreaks) {
7446
7446
  return [
@@ -7530,9 +7530,9 @@
7530
7530
  starts
7531
7531
  };
7532
7532
  }
7533
- function analyzeText$1(text, profile, whiteSpace = 'normal', wordBreak = 'normal') {
7534
- const whiteSpaceProfile = getWhiteSpaceProfile$1(whiteSpace);
7535
- const normalized = whiteSpaceProfile.mode === 'pre-wrap' ? normalizeWhitespacePreWrap$1(text) : normalizeWhitespaceNormal$1(text);
7533
+ function analyzeText(text, profile, whiteSpace = 'normal', wordBreak = 'normal') {
7534
+ const whiteSpaceProfile = getWhiteSpaceProfile(whiteSpace);
7535
+ const normalized = whiteSpaceProfile.mode === 'pre-wrap' ? normalizeWhitespacePreWrap(text) : normalizeWhitespaceNormal(text);
7536
7536
  if (normalized.length === 0) {
7537
7537
  return {
7538
7538
  normalized,
@@ -7544,103 +7544,103 @@
7544
7544
  starts: []
7545
7545
  };
7546
7546
  }
7547
- const segmentation = wordBreak === 'keep-all' ? mergeKeepAllTextSegments(buildMergedSegmentation$1(normalized, profile, whiteSpaceProfile)) : buildMergedSegmentation$1(normalized, profile, whiteSpaceProfile);
7547
+ const segmentation = wordBreak === 'keep-all' ? mergeKeepAllTextSegments(buildMergedSegmentation(normalized, profile, whiteSpaceProfile)) : buildMergedSegmentation(normalized, profile, whiteSpaceProfile);
7548
7548
  return {
7549
7549
  normalized,
7550
- chunks: compileAnalysisChunks$1(segmentation, whiteSpaceProfile),
7550
+ chunks: compileAnalysisChunks(segmentation, whiteSpaceProfile),
7551
7551
  ...segmentation
7552
7552
  };
7553
7553
  }
7554
7554
 
7555
- let measureContext$1 = null;
7556
- const segmentMetricCaches$1 = new Map();
7557
- let cachedEngineProfile$1 = null;
7555
+ let measureContext = null;
7556
+ const segmentMetricCaches = new Map();
7557
+ let cachedEngineProfile = null;
7558
7558
  // Safari's prefix-fit policy is useful for ordinary word-sized runs, but letting
7559
7559
  // it measure every growing prefix of a giant segment recreates a pathological
7560
7560
  // superlinear prepare-time path. Past this size, switch to the cheaper
7561
7561
  // pair-context model and keep the public behavior linear.
7562
7562
  const MAX_PREFIX_FIT_GRAPHEMES = 96;
7563
- const emojiPresentationRe$1 = /\p{Emoji_Presentation}/u;
7564
- const maybeEmojiRe$1 = /[\p{Emoji_Presentation}\p{Extended_Pictographic}\p{Regional_Indicator}\uFE0F\u20E3]/u;
7565
- let sharedGraphemeSegmenter$3 = null;
7566
- const emojiCorrectionCache$1 = new Map();
7567
- function getMeasureContext$1() {
7568
- if (measureContext$1 !== null) return measureContext$1;
7563
+ const emojiPresentationRe = /\p{Emoji_Presentation}/u;
7564
+ const maybeEmojiRe = /[\p{Emoji_Presentation}\p{Extended_Pictographic}\p{Regional_Indicator}\uFE0F\u20E3]/u;
7565
+ let sharedGraphemeSegmenter$1 = null;
7566
+ const emojiCorrectionCache = new Map();
7567
+ function getMeasureContext() {
7568
+ if (measureContext !== null) return measureContext;
7569
7569
  if (typeof OffscreenCanvas !== 'undefined') {
7570
- measureContext$1 = new OffscreenCanvas(1, 1).getContext('2d');
7571
- return measureContext$1;
7570
+ measureContext = new OffscreenCanvas(1, 1).getContext('2d');
7571
+ return measureContext;
7572
7572
  }
7573
7573
  if (typeof document !== 'undefined') {
7574
- measureContext$1 = document.createElement('canvas').getContext('2d');
7575
- return measureContext$1;
7574
+ measureContext = document.createElement('canvas').getContext('2d');
7575
+ return measureContext;
7576
7576
  }
7577
7577
  throw new Error('Text measurement requires OffscreenCanvas or a DOM canvas context.');
7578
7578
  }
7579
- function getSegmentMetricCache$1(font) {
7580
- let cache = segmentMetricCaches$1.get(font);
7579
+ function getSegmentMetricCache(font) {
7580
+ let cache = segmentMetricCaches.get(font);
7581
7581
  if (!cache) {
7582
7582
  cache = new Map();
7583
- segmentMetricCaches$1.set(font, cache);
7583
+ segmentMetricCaches.set(font, cache);
7584
7584
  }
7585
7585
  return cache;
7586
7586
  }
7587
- function getSegmentMetrics$1(seg, cache) {
7587
+ function getSegmentMetrics(seg, cache) {
7588
7588
  let metrics = cache.get(seg);
7589
7589
  if (metrics === undefined) {
7590
- const ctx = getMeasureContext$1();
7590
+ const ctx = getMeasureContext();
7591
7591
  metrics = {
7592
7592
  width: ctx.measureText(seg).width,
7593
- containsCJK: isCJK$1(seg)
7593
+ containsCJK: isCJK(seg)
7594
7594
  };
7595
7595
  cache.set(seg, metrics);
7596
7596
  }
7597
7597
  return metrics;
7598
7598
  }
7599
- function getEngineProfile$1() {
7600
- if (cachedEngineProfile$1 !== null) return cachedEngineProfile$1;
7599
+ function getEngineProfile() {
7600
+ if (cachedEngineProfile !== null) return cachedEngineProfile;
7601
7601
  if (typeof navigator === 'undefined') {
7602
- cachedEngineProfile$1 = {
7602
+ cachedEngineProfile = {
7603
7603
  lineFitEpsilon: 0.005,
7604
7604
  carryCJKAfterClosingQuote: false,
7605
7605
  preferPrefixWidthsForBreakableRuns: false,
7606
7606
  preferEarlySoftHyphenBreak: false
7607
7607
  };
7608
- return cachedEngineProfile$1;
7608
+ return cachedEngineProfile;
7609
7609
  }
7610
7610
  const ua = navigator.userAgent;
7611
7611
  const vendor = navigator.vendor;
7612
7612
  const isSafari = vendor === 'Apple Computer, Inc.' && ua.includes('Safari/') && !ua.includes('Chrome/') && !ua.includes('Chromium/') && !ua.includes('CriOS/') && !ua.includes('FxiOS/') && !ua.includes('EdgiOS/');
7613
7613
  const isChromium = ua.includes('Chrome/') || ua.includes('Chromium/') || ua.includes('CriOS/') || ua.includes('Edg/');
7614
- cachedEngineProfile$1 = {
7614
+ cachedEngineProfile = {
7615
7615
  lineFitEpsilon: isSafari ? 1 / 64 : 0.005,
7616
7616
  carryCJKAfterClosingQuote: isChromium,
7617
7617
  preferPrefixWidthsForBreakableRuns: isSafari,
7618
7618
  preferEarlySoftHyphenBreak: isSafari
7619
7619
  };
7620
- return cachedEngineProfile$1;
7620
+ return cachedEngineProfile;
7621
7621
  }
7622
- function parseFontSize$1(font) {
7622
+ function parseFontSize(font) {
7623
7623
  const m = font.match(/(\d+(?:\.\d+)?)\s*px/);
7624
7624
  return m ? parseFloat(m[1]) : 16;
7625
7625
  }
7626
- function getSharedGraphemeSegmenter$3() {
7627
- if (sharedGraphemeSegmenter$3 === null) {
7628
- sharedGraphemeSegmenter$3 = new Intl.Segmenter(undefined, {
7626
+ function getSharedGraphemeSegmenter$1() {
7627
+ if (sharedGraphemeSegmenter$1 === null) {
7628
+ sharedGraphemeSegmenter$1 = new Intl.Segmenter(undefined, {
7629
7629
  granularity: 'grapheme'
7630
7630
  });
7631
7631
  }
7632
- return sharedGraphemeSegmenter$3;
7632
+ return sharedGraphemeSegmenter$1;
7633
7633
  }
7634
- function isEmojiGrapheme$1(g) {
7635
- return emojiPresentationRe$1.test(g) || g.includes('\uFE0F');
7634
+ function isEmojiGrapheme(g) {
7635
+ return emojiPresentationRe.test(g) || g.includes('\uFE0F');
7636
7636
  }
7637
- function textMayContainEmoji$1(text) {
7638
- return maybeEmojiRe$1.test(text);
7637
+ function textMayContainEmoji(text) {
7638
+ return maybeEmojiRe.test(text);
7639
7639
  }
7640
- function getEmojiCorrection$1(font, fontSize) {
7641
- let correction = emojiCorrectionCache$1.get(font);
7640
+ function getEmojiCorrection(font, fontSize) {
7641
+ let correction = emojiCorrectionCache.get(font);
7642
7642
  if (correction !== undefined) return correction;
7643
- const ctx = getMeasureContext$1();
7643
+ const ctx = getMeasureContext();
7644
7644
  ctx.font = font;
7645
7645
  const canvasW = ctx.measureText('\u{1F600}').width;
7646
7646
  correction = 0;
@@ -7658,30 +7658,30 @@
7658
7658
  correction = canvasW - domW;
7659
7659
  }
7660
7660
  }
7661
- emojiCorrectionCache$1.set(font, correction);
7661
+ emojiCorrectionCache.set(font, correction);
7662
7662
  return correction;
7663
7663
  }
7664
- function countEmojiGraphemes$1(text) {
7664
+ function countEmojiGraphemes(text) {
7665
7665
  let count = 0;
7666
- const graphemeSegmenter = getSharedGraphemeSegmenter$3();
7666
+ const graphemeSegmenter = getSharedGraphemeSegmenter$1();
7667
7667
  for (const g of graphemeSegmenter.segment(text)){
7668
- if (isEmojiGrapheme$1(g.segment)) count++;
7668
+ if (isEmojiGrapheme(g.segment)) count++;
7669
7669
  }
7670
7670
  return count;
7671
7671
  }
7672
- function getEmojiCount$1(seg, metrics) {
7672
+ function getEmojiCount(seg, metrics) {
7673
7673
  if (metrics.emojiCount === undefined) {
7674
- metrics.emojiCount = countEmojiGraphemes$1(seg);
7674
+ metrics.emojiCount = countEmojiGraphemes(seg);
7675
7675
  }
7676
7676
  return metrics.emojiCount;
7677
7677
  }
7678
- function getCorrectedSegmentWidth$1(seg, metrics, emojiCorrection) {
7678
+ function getCorrectedSegmentWidth(seg, metrics, emojiCorrection) {
7679
7679
  if (emojiCorrection === 0) return metrics.width;
7680
- return metrics.width - getEmojiCount$1(seg, metrics) * emojiCorrection;
7680
+ return metrics.width - getEmojiCount(seg, metrics) * emojiCorrection;
7681
7681
  }
7682
7682
  function getSegmentBreakableFitAdvances(seg, metrics, cache, emojiCorrection, mode) {
7683
7683
  if (metrics.breakableFitAdvances !== undefined) return metrics.breakableFitAdvances;
7684
- const graphemeSegmenter = getSharedGraphemeSegmenter$3();
7684
+ const graphemeSegmenter = getSharedGraphemeSegmenter$1();
7685
7685
  const graphemes = [];
7686
7686
  for (const gs of graphemeSegmenter.segment(seg)){
7687
7687
  graphemes.push(gs.segment);
@@ -7693,8 +7693,8 @@
7693
7693
  if (mode === 'sum-graphemes') {
7694
7694
  const advances = [];
7695
7695
  for (const grapheme of graphemes){
7696
- const graphemeMetrics = getSegmentMetrics$1(grapheme, cache);
7697
- advances.push(getCorrectedSegmentWidth$1(grapheme, graphemeMetrics, emojiCorrection));
7696
+ const graphemeMetrics = getSegmentMetrics(grapheme, cache);
7697
+ advances.push(getCorrectedSegmentWidth(grapheme, graphemeMetrics, emojiCorrection));
7698
7698
  }
7699
7699
  metrics.breakableFitAdvances = advances;
7700
7700
  return metrics.breakableFitAdvances;
@@ -7704,14 +7704,14 @@
7704
7704
  let previousGrapheme = null;
7705
7705
  let previousWidth = 0;
7706
7706
  for (const grapheme of graphemes){
7707
- const graphemeMetrics = getSegmentMetrics$1(grapheme, cache);
7708
- const currentWidth = getCorrectedSegmentWidth$1(grapheme, graphemeMetrics, emojiCorrection);
7707
+ const graphemeMetrics = getSegmentMetrics(grapheme, cache);
7708
+ const currentWidth = getCorrectedSegmentWidth(grapheme, graphemeMetrics, emojiCorrection);
7709
7709
  if (previousGrapheme === null) {
7710
7710
  advances.push(currentWidth);
7711
7711
  } else {
7712
7712
  const pair = previousGrapheme + grapheme;
7713
- const pairMetrics = getSegmentMetrics$1(pair, cache);
7714
- advances.push(getCorrectedSegmentWidth$1(pair, pairMetrics, emojiCorrection) - previousWidth);
7713
+ const pairMetrics = getSegmentMetrics(pair, cache);
7714
+ advances.push(getCorrectedSegmentWidth(pair, pairMetrics, emojiCorrection) - previousWidth);
7715
7715
  }
7716
7716
  previousGrapheme = grapheme;
7717
7717
  previousWidth = currentWidth;
@@ -7724,20 +7724,20 @@
7724
7724
  let prefixWidth = 0;
7725
7725
  for (const grapheme of graphemes){
7726
7726
  prefix += grapheme;
7727
- const prefixMetrics = getSegmentMetrics$1(prefix, cache);
7728
- const nextPrefixWidth = getCorrectedSegmentWidth$1(prefix, prefixMetrics, emojiCorrection);
7727
+ const prefixMetrics = getSegmentMetrics(prefix, cache);
7728
+ const nextPrefixWidth = getCorrectedSegmentWidth(prefix, prefixMetrics, emojiCorrection);
7729
7729
  advances.push(nextPrefixWidth - prefixWidth);
7730
7730
  prefixWidth = nextPrefixWidth;
7731
7731
  }
7732
7732
  metrics.breakableFitAdvances = advances;
7733
7733
  return metrics.breakableFitAdvances;
7734
7734
  }
7735
- function getFontMeasurementState$1(font, needsEmojiCorrection) {
7736
- const ctx = getMeasureContext$1();
7735
+ function getFontMeasurementState(font, needsEmojiCorrection) {
7736
+ const ctx = getMeasureContext();
7737
7737
  ctx.font = font;
7738
- const cache = getSegmentMetricCache$1(font);
7739
- const fontSize = parseFontSize$1(font);
7740
- const emojiCorrection = needsEmojiCorrection ? getEmojiCorrection$1(font, fontSize) : 0;
7738
+ const cache = getSegmentMetricCache(font);
7739
+ const fontSize = parseFontSize(font);
7740
+ const emojiCorrection = needsEmojiCorrection ? getEmojiCorrection(font, fontSize) : 0;
7741
7741
  return {
7742
7742
  cache,
7743
7743
  fontSize,
@@ -7777,7 +7777,7 @@
7777
7777
  function walkPreparedLinesSimple(prepared, maxWidth, onLine) {
7778
7778
  const { widths, kinds, breakableFitAdvances } = prepared;
7779
7779
  if (widths.length === 0) return 0;
7780
- const engineProfile = getEngineProfile$1();
7780
+ const engineProfile = getEngineProfile();
7781
7781
  const lineFitEpsilon = engineProfile.lineFitEpsilon;
7782
7782
  const fitLimit = maxWidth + lineFitEpsilon;
7783
7783
  let lineCount = 0;
@@ -7914,7 +7914,7 @@
7914
7914
  }
7915
7915
  const { widths, lineEndFitAdvances, lineEndPaintAdvances, kinds, breakableFitAdvances, discretionaryHyphenWidth, tabStopAdvance, chunks } = prepared;
7916
7916
  if (widths.length === 0 || chunks.length === 0) return 0;
7917
- const engineProfile = getEngineProfile$1();
7917
+ const engineProfile = getEngineProfile();
7918
7918
  const lineFitEpsilon = engineProfile.lineFitEpsilon;
7919
7919
  const fitLimit = maxWidth + lineFitEpsilon;
7920
7920
  let lineCount = 0;
@@ -8153,20 +8153,20 @@
8153
8153
  // See RESEARCH.md "Discovery: system-ui font resolution mismatch".
8154
8154
  //
8155
8155
  // Based on Sebastian Markbage's text-layout research (github.com/chenglou/text-layout).
8156
- let sharedGraphemeSegmenter$2 = null;
8156
+ let sharedGraphemeSegmenter = null;
8157
8157
  // Rich-path only. Reuses grapheme splits while materializing multiple lines
8158
8158
  // from the same prepared handle, without pushing that cache into the API.
8159
8159
  let sharedLineTextCaches = new WeakMap();
8160
- function getSharedGraphemeSegmenter$2() {
8161
- if (sharedGraphemeSegmenter$2 === null) {
8162
- sharedGraphemeSegmenter$2 = new Intl.Segmenter(undefined, {
8160
+ function getSharedGraphemeSegmenter() {
8161
+ if (sharedGraphemeSegmenter === null) {
8162
+ sharedGraphemeSegmenter = new Intl.Segmenter(undefined, {
8163
8163
  granularity: 'grapheme'
8164
8164
  });
8165
8165
  }
8166
- return sharedGraphemeSegmenter$2;
8166
+ return sharedGraphemeSegmenter;
8167
8167
  }
8168
8168
  // --- Public API ---
8169
- function createEmptyPrepared$1(includeSegments) {
8169
+ function createEmptyPrepared(includeSegments) {
8170
8170
  {
8171
8171
  return {
8172
8172
  widths: [],
@@ -8207,28 +8207,28 @@
8207
8207
  ];
8208
8208
  unitStart = start;
8209
8209
  unitContainsCJK = graphemeContainsCJK;
8210
- unitEndsWithClosingQuote = endsWithClosingQuote$1(grapheme);
8211
- unitIsSingleKinsokuEnd = kinsokuEnd$1.has(grapheme);
8210
+ unitEndsWithClosingQuote = endsWithClosingQuote(grapheme);
8211
+ unitIsSingleKinsokuEnd = kinsokuEnd.has(grapheme);
8212
8212
  }
8213
8213
  function appendToUnit(grapheme, graphemeContainsCJK) {
8214
8214
  unitParts.push(grapheme);
8215
8215
  unitContainsCJK = unitContainsCJK || graphemeContainsCJK;
8216
- const graphemeEndsWithClosingQuote = endsWithClosingQuote$1(grapheme);
8217
- if (grapheme.length === 1 && leftStickyPunctuation$1.has(grapheme)) {
8216
+ const graphemeEndsWithClosingQuote = endsWithClosingQuote(grapheme);
8217
+ if (grapheme.length === 1 && leftStickyPunctuation.has(grapheme)) {
8218
8218
  unitEndsWithClosingQuote = unitEndsWithClosingQuote || graphemeEndsWithClosingQuote;
8219
8219
  } else {
8220
8220
  unitEndsWithClosingQuote = graphemeEndsWithClosingQuote;
8221
8221
  }
8222
8222
  unitIsSingleKinsokuEnd = false;
8223
8223
  }
8224
- for (const gs of getSharedGraphemeSegmenter$2().segment(segText)){
8224
+ for (const gs of getSharedGraphemeSegmenter().segment(segText)){
8225
8225
  const grapheme = gs.segment;
8226
- const graphemeContainsCJK = isCJK$1(grapheme);
8226
+ const graphemeContainsCJK = isCJK(grapheme);
8227
8227
  if (unitParts.length === 0) {
8228
8228
  startUnit(grapheme, gs.index, graphemeContainsCJK);
8229
8229
  continue;
8230
8230
  }
8231
- if (unitIsSingleKinsokuEnd || kinsokuStart$1.has(grapheme) || leftStickyPunctuation$1.has(grapheme) || engineProfile.carryCJKAfterClosingQuote && graphemeContainsCJK && unitEndsWithClosingQuote) {
8231
+ if (unitIsSingleKinsokuEnd || kinsokuStart.has(grapheme) || leftStickyPunctuation.has(grapheme) || engineProfile.carryCJKAfterClosingQuote && graphemeContainsCJK && unitEndsWithClosingQuote) {
8232
8232
  appendToUnit(grapheme, graphemeContainsCJK);
8233
8233
  continue;
8234
8234
  }
@@ -8242,13 +8242,49 @@
8242
8242
  pushUnit();
8243
8243
  return units;
8244
8244
  }
8245
- function measureAnalysis$1(analysis, font, includeSegments, wordBreak) {
8246
- const engineProfile = getEngineProfile$1();
8247
- const { cache, emojiCorrection } = getFontMeasurementState$1(font, textMayContainEmoji$1(analysis.normalized));
8248
- const discretionaryHyphenWidth = getCorrectedSegmentWidth$1('-', getSegmentMetrics$1('-', cache), emojiCorrection);
8249
- const spaceWidth = getCorrectedSegmentWidth$1(' ', getSegmentMetrics$1(' ', cache), emojiCorrection);
8245
+ function mergeKeepAllTextUnits(units) {
8246
+ if (units.length <= 1) return units;
8247
+ const merged = [];
8248
+ let currentTextParts = [
8249
+ units[0].text
8250
+ ];
8251
+ let currentStart = units[0].start;
8252
+ let currentContainsCJK = isCJK(units[0].text);
8253
+ let currentCanContinue = canContinueKeepAllTextRun(units[0].text);
8254
+ function flushCurrent() {
8255
+ merged.push({
8256
+ text: currentTextParts.length === 1 ? currentTextParts[0] : currentTextParts.join(''),
8257
+ start: currentStart
8258
+ });
8259
+ }
8260
+ for(let i = 1; i < units.length; i++){
8261
+ const next = units[i];
8262
+ const nextContainsCJK = isCJK(next.text);
8263
+ const nextCanContinue = canContinueKeepAllTextRun(next.text);
8264
+ if (currentContainsCJK && currentCanContinue) {
8265
+ currentTextParts.push(next.text);
8266
+ currentContainsCJK = currentContainsCJK || nextContainsCJK;
8267
+ currentCanContinue = nextCanContinue;
8268
+ continue;
8269
+ }
8270
+ flushCurrent();
8271
+ currentTextParts = [
8272
+ next.text
8273
+ ];
8274
+ currentStart = next.start;
8275
+ currentContainsCJK = nextContainsCJK;
8276
+ currentCanContinue = nextCanContinue;
8277
+ }
8278
+ flushCurrent();
8279
+ return merged;
8280
+ }
8281
+ function measureAnalysis(analysis, font, includeSegments, wordBreak) {
8282
+ const engineProfile = getEngineProfile();
8283
+ const { cache, emojiCorrection } = getFontMeasurementState(font, textMayContainEmoji(analysis.normalized));
8284
+ const discretionaryHyphenWidth = getCorrectedSegmentWidth('-', getSegmentMetrics('-', cache), emojiCorrection);
8285
+ const spaceWidth = getCorrectedSegmentWidth(' ', getSegmentMetrics(' ', cache), emojiCorrection);
8250
8286
  const tabStopAdvance = spaceWidth * 8;
8251
- if (analysis.len === 0) return createEmptyPrepared$1();
8287
+ if (analysis.len === 0) return createEmptyPrepared();
8252
8288
  const widths = [];
8253
8289
  const lineEndFitAdvances = [];
8254
8290
  const lineEndPaintAdvances = [];
@@ -8273,13 +8309,13 @@
8273
8309
  if (segments !== null) segments.push(text);
8274
8310
  }
8275
8311
  function pushMeasuredTextSegment(text, kind, start, wordLike, allowOverflowBreaks) {
8276
- const textMetrics = getSegmentMetrics$1(text, cache);
8277
- const width = getCorrectedSegmentWidth$1(text, textMetrics, emojiCorrection);
8312
+ const textMetrics = getSegmentMetrics(text, cache);
8313
+ const width = getCorrectedSegmentWidth(text, textMetrics, emojiCorrection);
8278
8314
  const lineEndFitAdvance = kind === 'space' || kind === 'preserved-space' || kind === 'zero-width-break' ? 0 : width;
8279
8315
  const lineEndPaintAdvance = kind === 'space' || kind === 'zero-width-break' ? 0 : width;
8280
8316
  if (allowOverflowBreaks && wordLike && text.length > 1) {
8281
8317
  let fitMode = 'sum-graphemes';
8282
- if (isNumericRunSegment$1(text)) {
8318
+ if (isNumericRunSegment(text)) {
8283
8319
  fitMode = 'pair-context';
8284
8320
  } else if (engineProfile.preferPrefixWidthsForBreakableRuns) {
8285
8321
  fitMode = 'segment-prefixes';
@@ -8308,20 +8344,20 @@
8308
8344
  pushMeasuredSegment(segText, 0, 0, 0, segKind, segStart, null);
8309
8345
  continue;
8310
8346
  }
8311
- const segMetrics = getSegmentMetrics$1(segText, cache);
8347
+ const segMetrics = getSegmentMetrics(segText, cache);
8312
8348
  if (segKind === 'text' && segMetrics.containsCJK) {
8313
8349
  const baseUnits = buildBaseCjkUnits(segText, engineProfile);
8314
- const measuredUnits = baseUnits;
8350
+ const measuredUnits = wordBreak === 'keep-all' ? mergeKeepAllTextUnits(baseUnits) : baseUnits;
8315
8351
  for(let i = 0; i < measuredUnits.length; i++){
8316
8352
  const unit = measuredUnits[i];
8317
- pushMeasuredTextSegment(unit.text, 'text', segStart + unit.start, segWordLike, !isCJK$1(unit.text));
8353
+ pushMeasuredTextSegment(unit.text, 'text', segStart + unit.start, segWordLike, wordBreak === 'keep-all' || !isCJK(unit.text));
8318
8354
  }
8319
8355
  continue;
8320
8356
  }
8321
8357
  pushMeasuredTextSegment(segText, segKind, segStart, segWordLike, true);
8322
8358
  }
8323
- const chunks = mapAnalysisChunksToPreparedChunks$1(analysis.chunks, preparedStartByAnalysisIndex, widths.length);
8324
- const segLevels = segStarts === null ? null : computeSegmentLevels$1(analysis.normalized, segStarts);
8359
+ const chunks = mapAnalysisChunksToPreparedChunks(analysis.chunks, preparedStartByAnalysisIndex, widths.length);
8360
+ const segLevels = segStarts === null ? null : computeSegmentLevels(analysis.normalized, segStarts);
8325
8361
  if (segments !== null) {
8326
8362
  return {
8327
8363
  widths,
@@ -8350,7 +8386,7 @@
8350
8386
  chunks
8351
8387
  };
8352
8388
  }
8353
- function mapAnalysisChunksToPreparedChunks$1(chunks, preparedStartByAnalysisIndex, preparedEndSegmentIndex) {
8389
+ function mapAnalysisChunksToPreparedChunks(chunks, preparedStartByAnalysisIndex, preparedEndSegmentIndex) {
8354
8390
  const preparedChunks = [];
8355
8391
  for(let i = 0; i < chunks.length; i++){
8356
8392
  const chunk = chunks[i];
@@ -8365,15 +8401,15 @@
8365
8401
  }
8366
8402
  return preparedChunks;
8367
8403
  }
8368
- function prepareInternal$1(text, font, includeSegments, options) {
8369
- const wordBreak = 'normal';
8370
- const analysis = analyzeText$1(text, getEngineProfile$1(), options?.whiteSpace, wordBreak);
8371
- return measureAnalysis$1(analysis, font, includeSegments);
8404
+ function prepareInternal(text, font, includeSegments, options) {
8405
+ const wordBreak = options?.wordBreak ?? 'normal';
8406
+ const analysis = analyzeText(text, getEngineProfile(), options?.whiteSpace, wordBreak);
8407
+ return measureAnalysis(analysis, font, includeSegments, wordBreak);
8372
8408
  }
8373
8409
  // Rich variant used by callers that need enough information to render the
8374
8410
  // laid-out lines themselves.
8375
- function prepareWithSegments$1(text, font, options) {
8376
- return prepareInternal$1(text, font, true, options);
8411
+ function prepareWithSegments(text, font, options) {
8412
+ return prepareInternal(text, font, true, options);
8377
8413
  }
8378
8414
  function getInternalPrepared(prepared) {
8379
8415
  return prepared;
@@ -8382,7 +8418,7 @@
8382
8418
  let graphemes = cache.get(segmentIndex);
8383
8419
  if (graphemes !== undefined) return graphemes;
8384
8420
  graphemes = [];
8385
- const graphemeSegmenter = getSharedGraphemeSegmenter$2();
8421
+ const graphemeSegmenter = getSharedGraphemeSegmenter();
8386
8422
  for (const gs of graphemeSegmenter.segment(segments[segmentIndex])){
8387
8423
  graphemes.push(gs.segment);
8388
8424
  }
@@ -8490,7 +8526,7 @@
8490
8526
  * @param {String} font CSS font shorthand
8491
8527
  */ function measureWidth(text, font) {
8492
8528
  if (!text) return 0;
8493
- var prepared = prepareWithSegments$1(text, font);
8529
+ var prepared = prepareWithSegments(text, font);
8494
8530
  var result = layoutWithLines(prepared, Infinity, 20);
8495
8531
  return result.lines.length ? result.lines[0].width : 0;
8496
8532
  }
@@ -26487,1771 +26523,6 @@
26487
26523
  });
26488
26524
  }
26489
26525
 
26490
- // Simplified bidi metadata helper for the rich prepareWithSegments() path,
26491
- // forked from pdf.js via Sebastian's text-layout. It classifies characters
26492
- // into bidi types, computes embedding levels, and maps them onto prepared
26493
- // segments for custom rendering. The line-breaking engine does not consume
26494
- // these levels.
26495
- const baseTypes = [
26496
- 'BN',
26497
- 'BN',
26498
- 'BN',
26499
- 'BN',
26500
- 'BN',
26501
- 'BN',
26502
- 'BN',
26503
- 'BN',
26504
- 'BN',
26505
- 'S',
26506
- 'B',
26507
- 'S',
26508
- 'WS',
26509
- 'B',
26510
- 'BN',
26511
- 'BN',
26512
- 'BN',
26513
- 'BN',
26514
- 'BN',
26515
- 'BN',
26516
- 'BN',
26517
- 'BN',
26518
- 'BN',
26519
- 'BN',
26520
- 'BN',
26521
- 'BN',
26522
- 'BN',
26523
- 'BN',
26524
- 'B',
26525
- 'B',
26526
- 'B',
26527
- 'S',
26528
- 'WS',
26529
- 'ON',
26530
- 'ON',
26531
- 'ET',
26532
- 'ET',
26533
- 'ET',
26534
- 'ON',
26535
- 'ON',
26536
- 'ON',
26537
- 'ON',
26538
- 'ON',
26539
- 'ON',
26540
- 'CS',
26541
- 'ON',
26542
- 'CS',
26543
- 'ON',
26544
- 'EN',
26545
- 'EN',
26546
- 'EN',
26547
- 'EN',
26548
- 'EN',
26549
- 'EN',
26550
- 'EN',
26551
- 'EN',
26552
- 'EN',
26553
- 'EN',
26554
- 'ON',
26555
- 'ON',
26556
- 'ON',
26557
- 'ON',
26558
- 'ON',
26559
- 'ON',
26560
- 'ON',
26561
- 'L',
26562
- 'L',
26563
- 'L',
26564
- 'L',
26565
- 'L',
26566
- 'L',
26567
- 'L',
26568
- 'L',
26569
- 'L',
26570
- 'L',
26571
- 'L',
26572
- 'L',
26573
- 'L',
26574
- 'L',
26575
- 'L',
26576
- 'L',
26577
- 'L',
26578
- 'L',
26579
- 'L',
26580
- 'L',
26581
- 'L',
26582
- 'L',
26583
- 'L',
26584
- 'L',
26585
- 'L',
26586
- 'L',
26587
- 'ON',
26588
- 'ON',
26589
- 'ON',
26590
- 'ON',
26591
- 'ON',
26592
- 'ON',
26593
- 'L',
26594
- 'L',
26595
- 'L',
26596
- 'L',
26597
- 'L',
26598
- 'L',
26599
- 'L',
26600
- 'L',
26601
- 'L',
26602
- 'L',
26603
- 'L',
26604
- 'L',
26605
- 'L',
26606
- 'L',
26607
- 'L',
26608
- 'L',
26609
- 'L',
26610
- 'L',
26611
- 'L',
26612
- 'L',
26613
- 'L',
26614
- 'L',
26615
- 'L',
26616
- 'L',
26617
- 'L',
26618
- 'L',
26619
- 'ON',
26620
- 'ON',
26621
- 'ON',
26622
- 'ON',
26623
- 'BN',
26624
- 'BN',
26625
- 'BN',
26626
- 'BN',
26627
- 'BN',
26628
- 'BN',
26629
- 'B',
26630
- 'BN',
26631
- 'BN',
26632
- 'BN',
26633
- 'BN',
26634
- 'BN',
26635
- 'BN',
26636
- 'BN',
26637
- 'BN',
26638
- 'BN',
26639
- 'BN',
26640
- 'BN',
26641
- 'BN',
26642
- 'BN',
26643
- 'BN',
26644
- 'BN',
26645
- 'BN',
26646
- 'BN',
26647
- 'BN',
26648
- 'BN',
26649
- 'BN',
26650
- 'BN',
26651
- 'BN',
26652
- 'BN',
26653
- 'BN',
26654
- 'BN',
26655
- 'BN',
26656
- 'CS',
26657
- 'ON',
26658
- 'ET',
26659
- 'ET',
26660
- 'ET',
26661
- 'ET',
26662
- 'ON',
26663
- 'ON',
26664
- 'ON',
26665
- 'ON',
26666
- 'L',
26667
- 'ON',
26668
- 'ON',
26669
- 'ON',
26670
- 'ON',
26671
- 'ON',
26672
- 'ET',
26673
- 'ET',
26674
- 'EN',
26675
- 'EN',
26676
- 'ON',
26677
- 'L',
26678
- 'ON',
26679
- 'ON',
26680
- 'ON',
26681
- 'EN',
26682
- 'L',
26683
- 'ON',
26684
- 'ON',
26685
- 'ON',
26686
- 'ON',
26687
- 'ON',
26688
- 'L',
26689
- 'L',
26690
- 'L',
26691
- 'L',
26692
- 'L',
26693
- 'L',
26694
- 'L',
26695
- 'L',
26696
- 'L',
26697
- 'L',
26698
- 'L',
26699
- 'L',
26700
- 'L',
26701
- 'L',
26702
- 'L',
26703
- 'L',
26704
- 'L',
26705
- 'L',
26706
- 'L',
26707
- 'L',
26708
- 'L',
26709
- 'L',
26710
- 'L',
26711
- 'ON',
26712
- 'L',
26713
- 'L',
26714
- 'L',
26715
- 'L',
26716
- 'L',
26717
- 'L',
26718
- 'L',
26719
- 'L',
26720
- 'L',
26721
- 'L',
26722
- 'L',
26723
- 'L',
26724
- 'L',
26725
- 'L',
26726
- 'L',
26727
- 'L',
26728
- 'L',
26729
- 'L',
26730
- 'L',
26731
- 'L',
26732
- 'L',
26733
- 'L',
26734
- 'L',
26735
- 'L',
26736
- 'L',
26737
- 'L',
26738
- 'L',
26739
- 'L',
26740
- 'L',
26741
- 'L',
26742
- 'L',
26743
- 'ON',
26744
- 'L',
26745
- 'L',
26746
- 'L',
26747
- 'L',
26748
- 'L',
26749
- 'L',
26750
- 'L',
26751
- 'L'
26752
- ];
26753
- const arabicTypes = [
26754
- 'AL',
26755
- 'AL',
26756
- 'AL',
26757
- 'AL',
26758
- 'AL',
26759
- 'AL',
26760
- 'AL',
26761
- 'AL',
26762
- 'AL',
26763
- 'AL',
26764
- 'AL',
26765
- 'AL',
26766
- 'CS',
26767
- 'AL',
26768
- 'ON',
26769
- 'ON',
26770
- 'NSM',
26771
- 'NSM',
26772
- 'NSM',
26773
- 'NSM',
26774
- 'NSM',
26775
- 'NSM',
26776
- 'AL',
26777
- 'AL',
26778
- 'AL',
26779
- 'AL',
26780
- 'AL',
26781
- 'AL',
26782
- 'AL',
26783
- 'AL',
26784
- 'AL',
26785
- 'AL',
26786
- 'AL',
26787
- 'AL',
26788
- 'AL',
26789
- 'AL',
26790
- 'AL',
26791
- 'AL',
26792
- 'AL',
26793
- 'AL',
26794
- 'AL',
26795
- 'AL',
26796
- 'AL',
26797
- 'AL',
26798
- 'AL',
26799
- 'AL',
26800
- 'AL',
26801
- 'AL',
26802
- 'AL',
26803
- 'AL',
26804
- 'AL',
26805
- 'AL',
26806
- 'AL',
26807
- 'AL',
26808
- 'AL',
26809
- 'AL',
26810
- 'AL',
26811
- 'AL',
26812
- 'AL',
26813
- 'AL',
26814
- 'AL',
26815
- 'AL',
26816
- 'AL',
26817
- 'AL',
26818
- 'AL',
26819
- 'AL',
26820
- 'AL',
26821
- 'AL',
26822
- 'AL',
26823
- 'AL',
26824
- 'AL',
26825
- 'AL',
26826
- 'AL',
26827
- 'AL',
26828
- 'AL',
26829
- 'NSM',
26830
- 'NSM',
26831
- 'NSM',
26832
- 'NSM',
26833
- 'NSM',
26834
- 'NSM',
26835
- 'NSM',
26836
- 'NSM',
26837
- 'NSM',
26838
- 'NSM',
26839
- 'NSM',
26840
- 'NSM',
26841
- 'NSM',
26842
- 'NSM',
26843
- 'AL',
26844
- 'AL',
26845
- 'AL',
26846
- 'AL',
26847
- 'AL',
26848
- 'AL',
26849
- 'AL',
26850
- 'AN',
26851
- 'AN',
26852
- 'AN',
26853
- 'AN',
26854
- 'AN',
26855
- 'AN',
26856
- 'AN',
26857
- 'AN',
26858
- 'AN',
26859
- 'AN',
26860
- 'ET',
26861
- 'AN',
26862
- 'AN',
26863
- 'AL',
26864
- 'AL',
26865
- 'AL',
26866
- 'NSM',
26867
- 'AL',
26868
- 'AL',
26869
- 'AL',
26870
- 'AL',
26871
- 'AL',
26872
- 'AL',
26873
- 'AL',
26874
- 'AL',
26875
- 'AL',
26876
- 'AL',
26877
- 'AL',
26878
- 'AL',
26879
- 'AL',
26880
- 'AL',
26881
- 'AL',
26882
- 'AL',
26883
- 'AL',
26884
- 'AL',
26885
- 'AL',
26886
- 'AL',
26887
- 'AL',
26888
- 'AL',
26889
- 'AL',
26890
- 'AL',
26891
- 'AL',
26892
- 'AL',
26893
- 'AL',
26894
- 'AL',
26895
- 'AL',
26896
- 'AL',
26897
- 'AL',
26898
- 'AL',
26899
- 'AL',
26900
- 'AL',
26901
- 'AL',
26902
- 'AL',
26903
- 'AL',
26904
- 'AL',
26905
- 'AL',
26906
- 'AL',
26907
- 'AL',
26908
- 'AL',
26909
- 'AL',
26910
- 'AL',
26911
- 'AL',
26912
- 'AL',
26913
- 'AL',
26914
- 'AL',
26915
- 'AL',
26916
- 'AL',
26917
- 'AL',
26918
- 'AL',
26919
- 'AL',
26920
- 'AL',
26921
- 'AL',
26922
- 'AL',
26923
- 'AL',
26924
- 'AL',
26925
- 'AL',
26926
- 'AL',
26927
- 'AL',
26928
- 'AL',
26929
- 'AL',
26930
- 'AL',
26931
- 'AL',
26932
- 'AL',
26933
- 'AL',
26934
- 'AL',
26935
- 'AL',
26936
- 'AL',
26937
- 'AL',
26938
- 'AL',
26939
- 'AL',
26940
- 'AL',
26941
- 'AL',
26942
- 'AL',
26943
- 'AL',
26944
- 'AL',
26945
- 'AL',
26946
- 'AL',
26947
- 'AL',
26948
- 'AL',
26949
- 'AL',
26950
- 'AL',
26951
- 'AL',
26952
- 'AL',
26953
- 'AL',
26954
- 'AL',
26955
- 'AL',
26956
- 'AL',
26957
- 'AL',
26958
- 'AL',
26959
- 'AL',
26960
- 'AL',
26961
- 'AL',
26962
- 'AL',
26963
- 'AL',
26964
- 'AL',
26965
- 'AL',
26966
- 'AL',
26967
- 'AL',
26968
- 'NSM',
26969
- 'NSM',
26970
- 'NSM',
26971
- 'NSM',
26972
- 'NSM',
26973
- 'NSM',
26974
- 'NSM',
26975
- 'NSM',
26976
- 'NSM',
26977
- 'NSM',
26978
- 'NSM',
26979
- 'NSM',
26980
- 'NSM',
26981
- 'NSM',
26982
- 'NSM',
26983
- 'NSM',
26984
- 'NSM',
26985
- 'NSM',
26986
- 'NSM',
26987
- 'ON',
26988
- 'NSM',
26989
- 'NSM',
26990
- 'NSM',
26991
- 'NSM',
26992
- 'AL',
26993
- 'AL',
26994
- 'AL',
26995
- 'AL',
26996
- 'AL',
26997
- 'AL',
26998
- 'AL',
26999
- 'AL',
27000
- 'AL',
27001
- 'AL',
27002
- 'AL',
27003
- 'AL',
27004
- 'AL',
27005
- 'AL',
27006
- 'AL',
27007
- 'AL',
27008
- 'AL',
27009
- 'AL'
27010
- ];
27011
- function classifyChar(charCode) {
27012
- if (charCode <= 0x00ff) return baseTypes[charCode];
27013
- if (0x0590 <= charCode && charCode <= 0x05f4) return 'R';
27014
- if (0x0600 <= charCode && charCode <= 0x06ff) return arabicTypes[charCode & 0xff];
27015
- if (0x0700 <= charCode && charCode <= 0x08AC) return 'AL';
27016
- return 'L';
27017
- }
27018
- function computeBidiLevels(str) {
27019
- const len = str.length;
27020
- if (len === 0) return null;
27021
- // eslint-disable-next-line unicorn/no-new-array
27022
- const types = new Array(len);
27023
- let numBidi = 0;
27024
- for(let i = 0; i < len; i++){
27025
- const t = classifyChar(str.charCodeAt(i));
27026
- if (t === 'R' || t === 'AL' || t === 'AN') numBidi++;
27027
- types[i] = t;
27028
- }
27029
- if (numBidi === 0) return null;
27030
- const startLevel = len / numBidi < 0.3 ? 0 : 1;
27031
- const levels = new Int8Array(len);
27032
- for(let i = 0; i < len; i++)levels[i] = startLevel;
27033
- const e = startLevel & 1 ? 'R' : 'L';
27034
- const sor = e;
27035
- // W1-W7
27036
- let lastType = sor;
27037
- for(let i = 0; i < len; i++){
27038
- if (types[i] === 'NSM') types[i] = lastType;
27039
- else lastType = types[i];
27040
- }
27041
- lastType = sor;
27042
- for(let i = 0; i < len; i++){
27043
- const t = types[i];
27044
- if (t === 'EN') types[i] = lastType === 'AL' ? 'AN' : 'EN';
27045
- else if (t === 'R' || t === 'L' || t === 'AL') lastType = t;
27046
- }
27047
- for(let i = 0; i < len; i++){
27048
- if (types[i] === 'AL') types[i] = 'R';
27049
- }
27050
- for(let i = 1; i < len - 1; i++){
27051
- if (types[i] === 'ES' && types[i - 1] === 'EN' && types[i + 1] === 'EN') {
27052
- types[i] = 'EN';
27053
- }
27054
- if (types[i] === 'CS' && (types[i - 1] === 'EN' || types[i - 1] === 'AN') && types[i + 1] === types[i - 1]) {
27055
- types[i] = types[i - 1];
27056
- }
27057
- }
27058
- for(let i = 0; i < len; i++){
27059
- if (types[i] !== 'EN') continue;
27060
- let j;
27061
- for(j = i - 1; j >= 0 && types[j] === 'ET'; j--)types[j] = 'EN';
27062
- for(j = i + 1; j < len && types[j] === 'ET'; j++)types[j] = 'EN';
27063
- }
27064
- for(let i = 0; i < len; i++){
27065
- const t = types[i];
27066
- if (t === 'WS' || t === 'ES' || t === 'ET' || t === 'CS') types[i] = 'ON';
27067
- }
27068
- lastType = sor;
27069
- for(let i = 0; i < len; i++){
27070
- const t = types[i];
27071
- if (t === 'EN') types[i] = lastType === 'L' ? 'L' : 'EN';
27072
- else if (t === 'R' || t === 'L') lastType = t;
27073
- }
27074
- // N1-N2
27075
- for(let i = 0; i < len; i++){
27076
- if (types[i] !== 'ON') continue;
27077
- let end = i + 1;
27078
- while(end < len && types[end] === 'ON')end++;
27079
- const before = i > 0 ? types[i - 1] : sor;
27080
- const after = end < len ? types[end] : sor;
27081
- const bDir = before !== 'L' ? 'R' : 'L';
27082
- const aDir = after !== 'L' ? 'R' : 'L';
27083
- if (bDir === aDir) {
27084
- for(let j = i; j < end; j++)types[j] = bDir;
27085
- }
27086
- i = end - 1;
27087
- }
27088
- for(let i = 0; i < len; i++){
27089
- if (types[i] === 'ON') types[i] = e;
27090
- }
27091
- // I1-I2
27092
- for(let i = 0; i < len; i++){
27093
- const t = types[i];
27094
- if ((levels[i] & 1) === 0) {
27095
- if (t === 'R') levels[i]++;
27096
- else if (t === 'AN' || t === 'EN') levels[i] += 2;
27097
- } else if (t === 'L' || t === 'AN' || t === 'EN') {
27098
- levels[i]++;
27099
- }
27100
- }
27101
- return levels;
27102
- }
27103
- function computeSegmentLevels(normalized, segStarts) {
27104
- const bidiLevels = computeBidiLevels(normalized);
27105
- if (bidiLevels === null) return null;
27106
- const segLevels = new Int8Array(segStarts.length);
27107
- for(let i = 0; i < segStarts.length; i++){
27108
- segLevels[i] = bidiLevels[segStarts[i]];
27109
- }
27110
- return segLevels;
27111
- }
27112
-
27113
- const collapsibleWhitespaceRunRe = /[ \t\n\r\f]+/g;
27114
- const needsWhitespaceNormalizationRe = /[\t\n\r\f]| {2,}|^ | $/;
27115
- function getWhiteSpaceProfile(whiteSpace) {
27116
- const mode = whiteSpace ?? 'normal';
27117
- return mode === 'pre-wrap' ? {
27118
- mode,
27119
- preserveOrdinarySpaces: true,
27120
- preserveHardBreaks: true
27121
- } : {
27122
- mode,
27123
- preserveOrdinarySpaces: false,
27124
- preserveHardBreaks: false
27125
- };
27126
- }
27127
- function normalizeWhitespaceNormal(text) {
27128
- if (!needsWhitespaceNormalizationRe.test(text)) return text;
27129
- let normalized = text.replace(collapsibleWhitespaceRunRe, ' ');
27130
- if (normalized.charCodeAt(0) === 0x20) {
27131
- normalized = normalized.slice(1);
27132
- }
27133
- if (normalized.length > 0 && normalized.charCodeAt(normalized.length - 1) === 0x20) {
27134
- normalized = normalized.slice(0, -1);
27135
- }
27136
- return normalized;
27137
- }
27138
- function normalizeWhitespacePreWrap(text) {
27139
- if (!/[\r\f]/.test(text)) return text.replace(/\r\n/g, '\n');
27140
- return text.replace(/\r\n/g, '\n').replace(/[\r\f]/g, '\n');
27141
- }
27142
- let sharedWordSegmenter = null;
27143
- let segmenterLocale;
27144
- function getSharedWordSegmenter() {
27145
- if (sharedWordSegmenter === null) {
27146
- sharedWordSegmenter = new Intl.Segmenter(segmenterLocale, {
27147
- granularity: 'word'
27148
- });
27149
- }
27150
- return sharedWordSegmenter;
27151
- }
27152
- const arabicScriptRe = /\p{Script=Arabic}/u;
27153
- const combiningMarkRe = /\p{M}/u;
27154
- const decimalDigitRe = /\p{Nd}/u;
27155
- function containsArabicScript(text) {
27156
- return arabicScriptRe.test(text);
27157
- }
27158
- function isCJK(s) {
27159
- for (const ch of s){
27160
- const c = ch.codePointAt(0);
27161
- if (c >= 0x4E00 && c <= 0x9FFF || c >= 0x3400 && c <= 0x4DBF || c >= 0x20000 && c <= 0x2A6DF || c >= 0x2A700 && c <= 0x2B73F || c >= 0x2B740 && c <= 0x2B81F || c >= 0x2B820 && c <= 0x2CEAF || c >= 0x2CEB0 && c <= 0x2EBEF || c >= 0x30000 && c <= 0x3134F || c >= 0xF900 && c <= 0xFAFF || c >= 0x2F800 && c <= 0x2FA1F || c >= 0x3000 && c <= 0x303F || c >= 0x3040 && c <= 0x309F || c >= 0x30A0 && c <= 0x30FF || c >= 0xAC00 && c <= 0xD7AF || c >= 0xFF00 && c <= 0xFFEF) {
27162
- return true;
27163
- }
27164
- }
27165
- return false;
27166
- }
27167
- const kinsokuStart = new Set([
27168
- '\uFF0C',
27169
- '\uFF0E',
27170
- '\uFF01',
27171
- '\uFF1A',
27172
- '\uFF1B',
27173
- '\uFF1F',
27174
- '\u3001',
27175
- '\u3002',
27176
- '\u30FB',
27177
- '\uFF09',
27178
- '\u3015',
27179
- '\u3009',
27180
- '\u300B',
27181
- '\u300D',
27182
- '\u300F',
27183
- '\u3011',
27184
- '\u3017',
27185
- '\u3019',
27186
- '\u301B',
27187
- '\u30FC',
27188
- '\u3005',
27189
- '\u303B',
27190
- '\u309D',
27191
- '\u309E',
27192
- '\u30FD',
27193
- '\u30FE'
27194
- ]);
27195
- const kinsokuEnd = new Set([
27196
- '"',
27197
- '(',
27198
- '[',
27199
- '{',
27200
- '“',
27201
- '‘',
27202
- '«',
27203
- '‹',
27204
- '\uFF08',
27205
- '\u3014',
27206
- '\u3008',
27207
- '\u300A',
27208
- '\u300C',
27209
- '\u300E',
27210
- '\u3010',
27211
- '\u3016',
27212
- '\u3018',
27213
- '\u301A'
27214
- ]);
27215
- const forwardStickyGlue = new Set([
27216
- "'",
27217
- '’'
27218
- ]);
27219
- const leftStickyPunctuation = new Set([
27220
- '.',
27221
- ',',
27222
- '!',
27223
- '?',
27224
- ':',
27225
- ';',
27226
- '\u060C',
27227
- '\u061B',
27228
- '\u061F',
27229
- '\u0964',
27230
- '\u0965',
27231
- '\u104A',
27232
- '\u104B',
27233
- '\u104C',
27234
- '\u104D',
27235
- '\u104F',
27236
- ')',
27237
- ']',
27238
- '}',
27239
- '%',
27240
- '"',
27241
- '”',
27242
- '’',
27243
- '»',
27244
- '›',
27245
- '…'
27246
- ]);
27247
- const arabicNoSpaceTrailingPunctuation = new Set([
27248
- ':',
27249
- '.',
27250
- '\u060C',
27251
- '\u061B'
27252
- ]);
27253
- const myanmarMedialGlue = new Set([
27254
- '\u104F'
27255
- ]);
27256
- const closingQuoteChars = new Set([
27257
- '”',
27258
- '’',
27259
- '»',
27260
- '›',
27261
- '\u300D',
27262
- '\u300F',
27263
- '\u3011',
27264
- '\u300B',
27265
- '\u3009',
27266
- '\u3015',
27267
- '\uFF09'
27268
- ]);
27269
- function isLeftStickyPunctuationSegment(segment) {
27270
- if (isEscapedQuoteClusterSegment(segment)) return true;
27271
- let sawPunctuation = false;
27272
- for (const ch of segment){
27273
- if (leftStickyPunctuation.has(ch)) {
27274
- sawPunctuation = true;
27275
- continue;
27276
- }
27277
- if (sawPunctuation && combiningMarkRe.test(ch)) continue;
27278
- return false;
27279
- }
27280
- return sawPunctuation;
27281
- }
27282
- function isCJKLineStartProhibitedSegment(segment) {
27283
- for (const ch of segment){
27284
- if (!kinsokuStart.has(ch) && !leftStickyPunctuation.has(ch)) return false;
27285
- }
27286
- return segment.length > 0;
27287
- }
27288
- function isForwardStickyClusterSegment(segment) {
27289
- if (isEscapedQuoteClusterSegment(segment)) return true;
27290
- for (const ch of segment){
27291
- if (!kinsokuEnd.has(ch) && !forwardStickyGlue.has(ch) && !combiningMarkRe.test(ch)) return false;
27292
- }
27293
- return segment.length > 0;
27294
- }
27295
- function isEscapedQuoteClusterSegment(segment) {
27296
- let sawQuote = false;
27297
- for (const ch of segment){
27298
- if (ch === '\\' || combiningMarkRe.test(ch)) continue;
27299
- if (kinsokuEnd.has(ch) || leftStickyPunctuation.has(ch) || forwardStickyGlue.has(ch)) {
27300
- sawQuote = true;
27301
- continue;
27302
- }
27303
- return false;
27304
- }
27305
- return sawQuote;
27306
- }
27307
- function splitTrailingForwardStickyCluster(text) {
27308
- const chars = Array.from(text);
27309
- let splitIndex = chars.length;
27310
- while(splitIndex > 0){
27311
- const ch = chars[splitIndex - 1];
27312
- if (combiningMarkRe.test(ch)) {
27313
- splitIndex--;
27314
- continue;
27315
- }
27316
- if (kinsokuEnd.has(ch) || forwardStickyGlue.has(ch)) {
27317
- splitIndex--;
27318
- continue;
27319
- }
27320
- break;
27321
- }
27322
- if (splitIndex <= 0 || splitIndex === chars.length) return null;
27323
- return {
27324
- head: chars.slice(0, splitIndex).join(''),
27325
- tail: chars.slice(splitIndex).join('')
27326
- };
27327
- }
27328
- function isRepeatedSingleCharRun(segment, ch) {
27329
- if (segment.length === 0) return false;
27330
- for (const part of segment){
27331
- if (part !== ch) return false;
27332
- }
27333
- return true;
27334
- }
27335
- function endsWithArabicNoSpacePunctuation(segment) {
27336
- if (!containsArabicScript(segment) || segment.length === 0) return false;
27337
- return arabicNoSpaceTrailingPunctuation.has(segment[segment.length - 1]);
27338
- }
27339
- function endsWithMyanmarMedialGlue(segment) {
27340
- if (segment.length === 0) return false;
27341
- return myanmarMedialGlue.has(segment[segment.length - 1]);
27342
- }
27343
- function splitLeadingSpaceAndMarks(segment) {
27344
- if (segment.length < 2 || segment[0] !== ' ') return null;
27345
- const marks = segment.slice(1);
27346
- if (/^\p{M}+$/u.test(marks)) {
27347
- return {
27348
- space: ' ',
27349
- marks
27350
- };
27351
- }
27352
- return null;
27353
- }
27354
- function endsWithClosingQuote(text) {
27355
- for(let i = text.length - 1; i >= 0; i--){
27356
- const ch = text[i];
27357
- if (closingQuoteChars.has(ch)) return true;
27358
- if (!leftStickyPunctuation.has(ch)) return false;
27359
- }
27360
- return false;
27361
- }
27362
- function classifySegmentBreakChar(ch, whiteSpaceProfile) {
27363
- if (whiteSpaceProfile.preserveOrdinarySpaces || whiteSpaceProfile.preserveHardBreaks) {
27364
- if (ch === ' ') return 'preserved-space';
27365
- if (ch === '\t') return 'tab';
27366
- if (whiteSpaceProfile.preserveHardBreaks && ch === '\n') return 'hard-break';
27367
- }
27368
- if (ch === ' ') return 'space';
27369
- if (ch === '\u00A0' || ch === '\u202F' || ch === '\u2060' || ch === '\uFEFF') {
27370
- return 'glue';
27371
- }
27372
- if (ch === '\u200B') return 'zero-width-break';
27373
- if (ch === '\u00AD') return 'soft-hyphen';
27374
- return 'text';
27375
- }
27376
- function splitSegmentByBreakKind(segment, isWordLike, start, whiteSpaceProfile) {
27377
- const pieces = [];
27378
- let currentKind = null;
27379
- let currentText = '';
27380
- let currentStart = start;
27381
- let currentWordLike = false;
27382
- let offset = 0;
27383
- for (const ch of segment){
27384
- const kind = classifySegmentBreakChar(ch, whiteSpaceProfile);
27385
- const wordLike = kind === 'text' && isWordLike;
27386
- if (currentKind !== null && kind === currentKind && wordLike === currentWordLike) {
27387
- currentText += ch;
27388
- offset += ch.length;
27389
- continue;
27390
- }
27391
- if (currentKind !== null) {
27392
- pieces.push({
27393
- text: currentText,
27394
- isWordLike: currentWordLike,
27395
- kind: currentKind,
27396
- start: currentStart
27397
- });
27398
- }
27399
- currentKind = kind;
27400
- currentText = ch;
27401
- currentStart = start + offset;
27402
- currentWordLike = wordLike;
27403
- offset += ch.length;
27404
- }
27405
- if (currentKind !== null) {
27406
- pieces.push({
27407
- text: currentText,
27408
- isWordLike: currentWordLike,
27409
- kind: currentKind,
27410
- start: currentStart
27411
- });
27412
- }
27413
- return pieces;
27414
- }
27415
- function isTextRunBoundary(kind) {
27416
- return kind === 'space' || kind === 'preserved-space' || kind === 'zero-width-break' || kind === 'hard-break';
27417
- }
27418
- const urlSchemeSegmentRe = /^[A-Za-z][A-Za-z0-9+.-]*:$/;
27419
- function isUrlLikeRunStart(segmentation, index) {
27420
- const text = segmentation.texts[index];
27421
- if (text.startsWith('www.')) return true;
27422
- return urlSchemeSegmentRe.test(text) && index + 1 < segmentation.len && segmentation.kinds[index + 1] === 'text' && segmentation.texts[index + 1] === '//';
27423
- }
27424
- function isUrlQueryBoundarySegment(text) {
27425
- return text.includes('?') && (text.includes('://') || text.startsWith('www.'));
27426
- }
27427
- function mergeUrlLikeRuns(segmentation) {
27428
- const texts = segmentation.texts.slice();
27429
- const isWordLike = segmentation.isWordLike.slice();
27430
- const kinds = segmentation.kinds.slice();
27431
- const starts = segmentation.starts.slice();
27432
- for(let i = 0; i < segmentation.len; i++){
27433
- if (kinds[i] !== 'text' || !isUrlLikeRunStart(segmentation, i)) continue;
27434
- let j = i + 1;
27435
- while(j < segmentation.len && !isTextRunBoundary(kinds[j])){
27436
- texts[i] += texts[j];
27437
- isWordLike[i] = true;
27438
- const endsQueryPrefix = texts[j].includes('?');
27439
- kinds[j] = 'text';
27440
- texts[j] = '';
27441
- j++;
27442
- if (endsQueryPrefix) break;
27443
- }
27444
- }
27445
- let compactLen = 0;
27446
- for(let read = 0; read < texts.length; read++){
27447
- const text = texts[read];
27448
- if (text.length === 0) continue;
27449
- if (compactLen !== read) {
27450
- texts[compactLen] = text;
27451
- isWordLike[compactLen] = isWordLike[read];
27452
- kinds[compactLen] = kinds[read];
27453
- starts[compactLen] = starts[read];
27454
- }
27455
- compactLen++;
27456
- }
27457
- texts.length = compactLen;
27458
- isWordLike.length = compactLen;
27459
- kinds.length = compactLen;
27460
- starts.length = compactLen;
27461
- return {
27462
- len: compactLen,
27463
- texts,
27464
- isWordLike,
27465
- kinds,
27466
- starts
27467
- };
27468
- }
27469
- function mergeUrlQueryRuns(segmentation) {
27470
- const texts = [];
27471
- const isWordLike = [];
27472
- const kinds = [];
27473
- const starts = [];
27474
- for(let i = 0; i < segmentation.len; i++){
27475
- const text = segmentation.texts[i];
27476
- texts.push(text);
27477
- isWordLike.push(segmentation.isWordLike[i]);
27478
- kinds.push(segmentation.kinds[i]);
27479
- starts.push(segmentation.starts[i]);
27480
- if (!isUrlQueryBoundarySegment(text)) continue;
27481
- const nextIndex = i + 1;
27482
- if (nextIndex >= segmentation.len || isTextRunBoundary(segmentation.kinds[nextIndex])) {
27483
- continue;
27484
- }
27485
- let queryText = '';
27486
- const queryStart = segmentation.starts[nextIndex];
27487
- let j = nextIndex;
27488
- while(j < segmentation.len && !isTextRunBoundary(segmentation.kinds[j])){
27489
- queryText += segmentation.texts[j];
27490
- j++;
27491
- }
27492
- if (queryText.length > 0) {
27493
- texts.push(queryText);
27494
- isWordLike.push(true);
27495
- kinds.push('text');
27496
- starts.push(queryStart);
27497
- i = j - 1;
27498
- }
27499
- }
27500
- return {
27501
- len: texts.length,
27502
- texts,
27503
- isWordLike,
27504
- kinds,
27505
- starts
27506
- };
27507
- }
27508
- const numericJoinerChars = new Set([
27509
- ':',
27510
- '-',
27511
- '/',
27512
- '×',
27513
- ',',
27514
- '.',
27515
- '+',
27516
- '\u2013',
27517
- '\u2014'
27518
- ]);
27519
- const asciiPunctuationChainSegmentRe = /^[A-Za-z0-9_]+[,:;]*$/;
27520
- const asciiPunctuationChainTrailingJoinersRe = /[,:;]+$/;
27521
- function segmentContainsDecimalDigit(text) {
27522
- for (const ch of text){
27523
- if (decimalDigitRe.test(ch)) return true;
27524
- }
27525
- return false;
27526
- }
27527
- function isNumericRunSegment(text) {
27528
- if (text.length === 0) return false;
27529
- for (const ch of text){
27530
- if (decimalDigitRe.test(ch) || numericJoinerChars.has(ch)) continue;
27531
- return false;
27532
- }
27533
- return true;
27534
- }
27535
- function mergeNumericRuns(segmentation) {
27536
- const texts = [];
27537
- const isWordLike = [];
27538
- const kinds = [];
27539
- const starts = [];
27540
- for(let i = 0; i < segmentation.len; i++){
27541
- const text = segmentation.texts[i];
27542
- const kind = segmentation.kinds[i];
27543
- if (kind === 'text' && isNumericRunSegment(text) && segmentContainsDecimalDigit(text)) {
27544
- let mergedText = text;
27545
- let j = i + 1;
27546
- while(j < segmentation.len && segmentation.kinds[j] === 'text' && isNumericRunSegment(segmentation.texts[j])){
27547
- mergedText += segmentation.texts[j];
27548
- j++;
27549
- }
27550
- texts.push(mergedText);
27551
- isWordLike.push(true);
27552
- kinds.push('text');
27553
- starts.push(segmentation.starts[i]);
27554
- i = j - 1;
27555
- continue;
27556
- }
27557
- texts.push(text);
27558
- isWordLike.push(segmentation.isWordLike[i]);
27559
- kinds.push(kind);
27560
- starts.push(segmentation.starts[i]);
27561
- }
27562
- return {
27563
- len: texts.length,
27564
- texts,
27565
- isWordLike,
27566
- kinds,
27567
- starts
27568
- };
27569
- }
27570
- function mergeAsciiPunctuationChains(segmentation) {
27571
- const texts = [];
27572
- const isWordLike = [];
27573
- const kinds = [];
27574
- const starts = [];
27575
- for(let i = 0; i < segmentation.len; i++){
27576
- const text = segmentation.texts[i];
27577
- const kind = segmentation.kinds[i];
27578
- const wordLike = segmentation.isWordLike[i];
27579
- if (kind === 'text' && wordLike && asciiPunctuationChainSegmentRe.test(text)) {
27580
- let mergedText = text;
27581
- let j = i + 1;
27582
- while(asciiPunctuationChainTrailingJoinersRe.test(mergedText) && j < segmentation.len && segmentation.kinds[j] === 'text' && segmentation.isWordLike[j] && asciiPunctuationChainSegmentRe.test(segmentation.texts[j])){
27583
- mergedText += segmentation.texts[j];
27584
- j++;
27585
- }
27586
- texts.push(mergedText);
27587
- isWordLike.push(true);
27588
- kinds.push('text');
27589
- starts.push(segmentation.starts[i]);
27590
- i = j - 1;
27591
- continue;
27592
- }
27593
- texts.push(text);
27594
- isWordLike.push(wordLike);
27595
- kinds.push(kind);
27596
- starts.push(segmentation.starts[i]);
27597
- }
27598
- return {
27599
- len: texts.length,
27600
- texts,
27601
- isWordLike,
27602
- kinds,
27603
- starts
27604
- };
27605
- }
27606
- function splitHyphenatedNumericRuns(segmentation) {
27607
- const texts = [];
27608
- const isWordLike = [];
27609
- const kinds = [];
27610
- const starts = [];
27611
- for(let i = 0; i < segmentation.len; i++){
27612
- const text = segmentation.texts[i];
27613
- if (segmentation.kinds[i] === 'text' && text.includes('-')) {
27614
- const parts = text.split('-');
27615
- let shouldSplit = parts.length > 1;
27616
- for(let j = 0; j < parts.length; j++){
27617
- const part = parts[j];
27618
- if (!shouldSplit) break;
27619
- if (part.length === 0 || !segmentContainsDecimalDigit(part) || !isNumericRunSegment(part)) {
27620
- shouldSplit = false;
27621
- }
27622
- }
27623
- if (shouldSplit) {
27624
- let offset = 0;
27625
- for(let j = 0; j < parts.length; j++){
27626
- const part = parts[j];
27627
- const splitText = j < parts.length - 1 ? `${part}-` : part;
27628
- texts.push(splitText);
27629
- isWordLike.push(true);
27630
- kinds.push('text');
27631
- starts.push(segmentation.starts[i] + offset);
27632
- offset += splitText.length;
27633
- }
27634
- continue;
27635
- }
27636
- }
27637
- texts.push(text);
27638
- isWordLike.push(segmentation.isWordLike[i]);
27639
- kinds.push(segmentation.kinds[i]);
27640
- starts.push(segmentation.starts[i]);
27641
- }
27642
- return {
27643
- len: texts.length,
27644
- texts,
27645
- isWordLike,
27646
- kinds,
27647
- starts
27648
- };
27649
- }
27650
- function mergeGlueConnectedTextRuns(segmentation) {
27651
- const texts = [];
27652
- const isWordLike = [];
27653
- const kinds = [];
27654
- const starts = [];
27655
- let read = 0;
27656
- while(read < segmentation.len){
27657
- let text = segmentation.texts[read];
27658
- let wordLike = segmentation.isWordLike[read];
27659
- let kind = segmentation.kinds[read];
27660
- let start = segmentation.starts[read];
27661
- if (kind === 'glue') {
27662
- let glueText = text;
27663
- const glueStart = start;
27664
- read++;
27665
- while(read < segmentation.len && segmentation.kinds[read] === 'glue'){
27666
- glueText += segmentation.texts[read];
27667
- read++;
27668
- }
27669
- if (read < segmentation.len && segmentation.kinds[read] === 'text') {
27670
- text = glueText + segmentation.texts[read];
27671
- wordLike = segmentation.isWordLike[read];
27672
- kind = 'text';
27673
- start = glueStart;
27674
- read++;
27675
- } else {
27676
- texts.push(glueText);
27677
- isWordLike.push(false);
27678
- kinds.push('glue');
27679
- starts.push(glueStart);
27680
- continue;
27681
- }
27682
- } else {
27683
- read++;
27684
- }
27685
- if (kind === 'text') {
27686
- while(read < segmentation.len && segmentation.kinds[read] === 'glue'){
27687
- let glueText = '';
27688
- while(read < segmentation.len && segmentation.kinds[read] === 'glue'){
27689
- glueText += segmentation.texts[read];
27690
- read++;
27691
- }
27692
- if (read < segmentation.len && segmentation.kinds[read] === 'text') {
27693
- text += glueText + segmentation.texts[read];
27694
- wordLike = wordLike || segmentation.isWordLike[read];
27695
- read++;
27696
- continue;
27697
- }
27698
- text += glueText;
27699
- }
27700
- }
27701
- texts.push(text);
27702
- isWordLike.push(wordLike);
27703
- kinds.push(kind);
27704
- starts.push(start);
27705
- }
27706
- return {
27707
- len: texts.length,
27708
- texts,
27709
- isWordLike,
27710
- kinds,
27711
- starts
27712
- };
27713
- }
27714
- function carryTrailingForwardStickyAcrossCJKBoundary(segmentation) {
27715
- const texts = segmentation.texts.slice();
27716
- const isWordLike = segmentation.isWordLike.slice();
27717
- const kinds = segmentation.kinds.slice();
27718
- const starts = segmentation.starts.slice();
27719
- for(let i = 0; i < texts.length - 1; i++){
27720
- if (kinds[i] !== 'text' || kinds[i + 1] !== 'text') continue;
27721
- if (!isCJK(texts[i]) || !isCJK(texts[i + 1])) continue;
27722
- const split = splitTrailingForwardStickyCluster(texts[i]);
27723
- if (split === null) continue;
27724
- texts[i] = split.head;
27725
- texts[i + 1] = split.tail + texts[i + 1];
27726
- starts[i + 1] = starts[i] + split.head.length;
27727
- }
27728
- return {
27729
- len: texts.length,
27730
- texts,
27731
- isWordLike,
27732
- kinds,
27733
- starts
27734
- };
27735
- }
27736
- function buildMergedSegmentation(normalized, profile, whiteSpaceProfile) {
27737
- const wordSegmenter = getSharedWordSegmenter();
27738
- let mergedLen = 0;
27739
- const mergedTexts = [];
27740
- const mergedWordLike = [];
27741
- const mergedKinds = [];
27742
- const mergedStarts = [];
27743
- for (const s of wordSegmenter.segment(normalized)){
27744
- for (const piece of splitSegmentByBreakKind(s.segment, s.isWordLike ?? false, s.index, whiteSpaceProfile)){
27745
- const isText = piece.kind === 'text';
27746
- if (profile.carryCJKAfterClosingQuote && isText && mergedLen > 0 && mergedKinds[mergedLen - 1] === 'text' && isCJK(piece.text) && isCJK(mergedTexts[mergedLen - 1]) && endsWithClosingQuote(mergedTexts[mergedLen - 1])) {
27747
- mergedTexts[mergedLen - 1] += piece.text;
27748
- mergedWordLike[mergedLen - 1] = mergedWordLike[mergedLen - 1] || piece.isWordLike;
27749
- } else if (isText && mergedLen > 0 && mergedKinds[mergedLen - 1] === 'text' && isCJKLineStartProhibitedSegment(piece.text) && isCJK(mergedTexts[mergedLen - 1])) {
27750
- mergedTexts[mergedLen - 1] += piece.text;
27751
- mergedWordLike[mergedLen - 1] = mergedWordLike[mergedLen - 1] || piece.isWordLike;
27752
- } else if (isText && mergedLen > 0 && mergedKinds[mergedLen - 1] === 'text' && endsWithMyanmarMedialGlue(mergedTexts[mergedLen - 1])) {
27753
- mergedTexts[mergedLen - 1] += piece.text;
27754
- mergedWordLike[mergedLen - 1] = mergedWordLike[mergedLen - 1] || piece.isWordLike;
27755
- } else if (isText && mergedLen > 0 && mergedKinds[mergedLen - 1] === 'text' && piece.isWordLike && containsArabicScript(piece.text) && endsWithArabicNoSpacePunctuation(mergedTexts[mergedLen - 1])) {
27756
- mergedTexts[mergedLen - 1] += piece.text;
27757
- mergedWordLike[mergedLen - 1] = true;
27758
- } else if (isText && !piece.isWordLike && mergedLen > 0 && mergedKinds[mergedLen - 1] === 'text' && piece.text.length === 1 && piece.text !== '-' && piece.text !== '—' && isRepeatedSingleCharRun(mergedTexts[mergedLen - 1], piece.text)) {
27759
- mergedTexts[mergedLen - 1] += piece.text;
27760
- } else if (isText && !piece.isWordLike && mergedLen > 0 && mergedKinds[mergedLen - 1] === 'text' && (isLeftStickyPunctuationSegment(piece.text) || piece.text === '-' && mergedWordLike[mergedLen - 1])) {
27761
- mergedTexts[mergedLen - 1] += piece.text;
27762
- } else {
27763
- mergedTexts[mergedLen] = piece.text;
27764
- mergedWordLike[mergedLen] = piece.isWordLike;
27765
- mergedKinds[mergedLen] = piece.kind;
27766
- mergedStarts[mergedLen] = piece.start;
27767
- mergedLen++;
27768
- }
27769
- }
27770
- }
27771
- for(let i = 1; i < mergedLen; i++){
27772
- if (mergedKinds[i] === 'text' && !mergedWordLike[i] && isEscapedQuoteClusterSegment(mergedTexts[i]) && mergedKinds[i - 1] === 'text') {
27773
- mergedTexts[i - 1] += mergedTexts[i];
27774
- mergedWordLike[i - 1] = mergedWordLike[i - 1] || mergedWordLike[i];
27775
- mergedTexts[i] = '';
27776
- }
27777
- }
27778
- for(let i = mergedLen - 2; i >= 0; i--){
27779
- if (mergedKinds[i] === 'text' && !mergedWordLike[i] && isForwardStickyClusterSegment(mergedTexts[i])) {
27780
- let j = i + 1;
27781
- while(j < mergedLen && mergedTexts[j] === '')j++;
27782
- if (j < mergedLen && mergedKinds[j] === 'text') {
27783
- mergedTexts[j] = mergedTexts[i] + mergedTexts[j];
27784
- mergedStarts[j] = mergedStarts[i];
27785
- mergedTexts[i] = '';
27786
- }
27787
- }
27788
- }
27789
- let compactLen = 0;
27790
- for(let read = 0; read < mergedLen; read++){
27791
- const text = mergedTexts[read];
27792
- if (text.length === 0) continue;
27793
- if (compactLen !== read) {
27794
- mergedTexts[compactLen] = text;
27795
- mergedWordLike[compactLen] = mergedWordLike[read];
27796
- mergedKinds[compactLen] = mergedKinds[read];
27797
- mergedStarts[compactLen] = mergedStarts[read];
27798
- }
27799
- compactLen++;
27800
- }
27801
- mergedTexts.length = compactLen;
27802
- mergedWordLike.length = compactLen;
27803
- mergedKinds.length = compactLen;
27804
- mergedStarts.length = compactLen;
27805
- const compacted = mergeGlueConnectedTextRuns({
27806
- len: compactLen,
27807
- texts: mergedTexts,
27808
- isWordLike: mergedWordLike,
27809
- kinds: mergedKinds,
27810
- starts: mergedStarts
27811
- });
27812
- const withMergedUrls = carryTrailingForwardStickyAcrossCJKBoundary(mergeAsciiPunctuationChains(splitHyphenatedNumericRuns(mergeNumericRuns(mergeUrlQueryRuns(mergeUrlLikeRuns(compacted))))));
27813
- for(let i = 0; i < withMergedUrls.len - 1; i++){
27814
- const split = splitLeadingSpaceAndMarks(withMergedUrls.texts[i]);
27815
- if (split === null) continue;
27816
- if (withMergedUrls.kinds[i] !== 'space' && withMergedUrls.kinds[i] !== 'preserved-space' || withMergedUrls.kinds[i + 1] !== 'text' || !containsArabicScript(withMergedUrls.texts[i + 1])) {
27817
- continue;
27818
- }
27819
- withMergedUrls.texts[i] = split.space;
27820
- withMergedUrls.isWordLike[i] = false;
27821
- withMergedUrls.kinds[i] = withMergedUrls.kinds[i] === 'preserved-space' ? 'preserved-space' : 'space';
27822
- withMergedUrls.texts[i + 1] = split.marks + withMergedUrls.texts[i + 1];
27823
- withMergedUrls.starts[i + 1] = withMergedUrls.starts[i] + split.space.length;
27824
- }
27825
- return withMergedUrls;
27826
- }
27827
- function compileAnalysisChunks(segmentation, whiteSpaceProfile) {
27828
- if (segmentation.len === 0) return [];
27829
- if (!whiteSpaceProfile.preserveHardBreaks) {
27830
- return [
27831
- {
27832
- startSegmentIndex: 0,
27833
- endSegmentIndex: segmentation.len,
27834
- consumedEndSegmentIndex: segmentation.len
27835
- }
27836
- ];
27837
- }
27838
- const chunks = [];
27839
- let startSegmentIndex = 0;
27840
- for(let i = 0; i < segmentation.len; i++){
27841
- if (segmentation.kinds[i] !== 'hard-break') continue;
27842
- chunks.push({
27843
- startSegmentIndex,
27844
- endSegmentIndex: i,
27845
- consumedEndSegmentIndex: i + 1
27846
- });
27847
- startSegmentIndex = i + 1;
27848
- }
27849
- if (startSegmentIndex < segmentation.len) {
27850
- chunks.push({
27851
- startSegmentIndex,
27852
- endSegmentIndex: segmentation.len,
27853
- consumedEndSegmentIndex: segmentation.len
27854
- });
27855
- }
27856
- return chunks;
27857
- }
27858
- function analyzeText(text, profile, whiteSpace = 'normal') {
27859
- const whiteSpaceProfile = getWhiteSpaceProfile(whiteSpace);
27860
- const normalized = whiteSpaceProfile.mode === 'pre-wrap' ? normalizeWhitespacePreWrap(text) : normalizeWhitespaceNormal(text);
27861
- if (normalized.length === 0) {
27862
- return {
27863
- normalized,
27864
- chunks: [],
27865
- len: 0,
27866
- texts: [],
27867
- isWordLike: [],
27868
- kinds: [],
27869
- starts: []
27870
- };
27871
- }
27872
- const segmentation = buildMergedSegmentation(normalized, profile, whiteSpaceProfile);
27873
- return {
27874
- normalized,
27875
- chunks: compileAnalysisChunks(segmentation, whiteSpaceProfile),
27876
- ...segmentation
27877
- };
27878
- }
27879
-
27880
- let measureContext = null;
27881
- const segmentMetricCaches = new Map();
27882
- let cachedEngineProfile = null;
27883
- const emojiPresentationRe = /\p{Emoji_Presentation}/u;
27884
- const maybeEmojiRe = /[\p{Emoji_Presentation}\p{Extended_Pictographic}\p{Regional_Indicator}\uFE0F\u20E3]/u;
27885
- let sharedGraphemeSegmenter$1 = null;
27886
- const emojiCorrectionCache = new Map();
27887
- function getMeasureContext() {
27888
- if (measureContext !== null) return measureContext;
27889
- if (typeof OffscreenCanvas !== 'undefined') {
27890
- measureContext = new OffscreenCanvas(1, 1).getContext('2d');
27891
- return measureContext;
27892
- }
27893
- if (typeof document !== 'undefined') {
27894
- measureContext = document.createElement('canvas').getContext('2d');
27895
- return measureContext;
27896
- }
27897
- throw new Error('Text measurement requires OffscreenCanvas or a DOM canvas context.');
27898
- }
27899
- function getSegmentMetricCache(font) {
27900
- let cache = segmentMetricCaches.get(font);
27901
- if (!cache) {
27902
- cache = new Map();
27903
- segmentMetricCaches.set(font, cache);
27904
- }
27905
- return cache;
27906
- }
27907
- function getSegmentMetrics(seg, cache) {
27908
- let metrics = cache.get(seg);
27909
- if (metrics === undefined) {
27910
- const ctx = getMeasureContext();
27911
- metrics = {
27912
- width: ctx.measureText(seg).width,
27913
- containsCJK: isCJK(seg)
27914
- };
27915
- cache.set(seg, metrics);
27916
- }
27917
- return metrics;
27918
- }
27919
- function getEngineProfile() {
27920
- if (cachedEngineProfile !== null) return cachedEngineProfile;
27921
- if (typeof navigator === 'undefined') {
27922
- cachedEngineProfile = {
27923
- lineFitEpsilon: 0.005,
27924
- carryCJKAfterClosingQuote: false,
27925
- preferPrefixWidthsForBreakableRuns: false,
27926
- preferEarlySoftHyphenBreak: false
27927
- };
27928
- return cachedEngineProfile;
27929
- }
27930
- const ua = navigator.userAgent;
27931
- const vendor = navigator.vendor;
27932
- const isSafari = vendor === 'Apple Computer, Inc.' && ua.includes('Safari/') && !ua.includes('Chrome/') && !ua.includes('Chromium/') && !ua.includes('CriOS/') && !ua.includes('FxiOS/') && !ua.includes('EdgiOS/');
27933
- const isChromium = ua.includes('Chrome/') || ua.includes('Chromium/') || ua.includes('CriOS/') || ua.includes('Edg/');
27934
- cachedEngineProfile = {
27935
- lineFitEpsilon: isSafari ? 1 / 64 : 0.005,
27936
- carryCJKAfterClosingQuote: isChromium,
27937
- preferPrefixWidthsForBreakableRuns: isSafari,
27938
- preferEarlySoftHyphenBreak: isSafari
27939
- };
27940
- return cachedEngineProfile;
27941
- }
27942
- function parseFontSize(font) {
27943
- const m = font.match(/(\d+(?:\.\d+)?)\s*px/);
27944
- return m ? parseFloat(m[1]) : 16;
27945
- }
27946
- function getSharedGraphemeSegmenter$1() {
27947
- if (sharedGraphemeSegmenter$1 === null) {
27948
- sharedGraphemeSegmenter$1 = new Intl.Segmenter(undefined, {
27949
- granularity: 'grapheme'
27950
- });
27951
- }
27952
- return sharedGraphemeSegmenter$1;
27953
- }
27954
- function isEmojiGrapheme(g) {
27955
- return emojiPresentationRe.test(g) || g.includes('\uFE0F');
27956
- }
27957
- function textMayContainEmoji(text) {
27958
- return maybeEmojiRe.test(text);
27959
- }
27960
- function getEmojiCorrection(font, fontSize) {
27961
- let correction = emojiCorrectionCache.get(font);
27962
- if (correction !== undefined) return correction;
27963
- const ctx = getMeasureContext();
27964
- ctx.font = font;
27965
- const canvasW = ctx.measureText('\u{1F600}').width;
27966
- correction = 0;
27967
- if (canvasW > fontSize + 0.5 && typeof document !== 'undefined' && document.body !== null) {
27968
- const span = document.createElement('span');
27969
- span.style.font = font;
27970
- span.style.display = 'inline-block';
27971
- span.style.visibility = 'hidden';
27972
- span.style.position = 'absolute';
27973
- span.textContent = '\u{1F600}';
27974
- document.body.appendChild(span);
27975
- const domW = span.getBoundingClientRect().width;
27976
- document.body.removeChild(span);
27977
- if (canvasW - domW > 0.5) {
27978
- correction = canvasW - domW;
27979
- }
27980
- }
27981
- emojiCorrectionCache.set(font, correction);
27982
- return correction;
27983
- }
27984
- function countEmojiGraphemes(text) {
27985
- let count = 0;
27986
- const graphemeSegmenter = getSharedGraphemeSegmenter$1();
27987
- for (const g of graphemeSegmenter.segment(text)){
27988
- if (isEmojiGrapheme(g.segment)) count++;
27989
- }
27990
- return count;
27991
- }
27992
- function getEmojiCount(seg, metrics) {
27993
- if (metrics.emojiCount === undefined) {
27994
- metrics.emojiCount = countEmojiGraphemes(seg);
27995
- }
27996
- return metrics.emojiCount;
27997
- }
27998
- function getCorrectedSegmentWidth(seg, metrics, emojiCorrection) {
27999
- if (emojiCorrection === 0) return metrics.width;
28000
- return metrics.width - getEmojiCount(seg, metrics) * emojiCorrection;
28001
- }
28002
- function getSegmentGraphemeWidths(seg, metrics, cache, emojiCorrection) {
28003
- if (metrics.graphemeWidths !== undefined) return metrics.graphemeWidths;
28004
- const widths = [];
28005
- const graphemeSegmenter = getSharedGraphemeSegmenter$1();
28006
- for (const gs of graphemeSegmenter.segment(seg)){
28007
- const graphemeMetrics = getSegmentMetrics(gs.segment, cache);
28008
- widths.push(getCorrectedSegmentWidth(gs.segment, graphemeMetrics, emojiCorrection));
28009
- }
28010
- metrics.graphemeWidths = widths.length > 1 ? widths : null;
28011
- return metrics.graphemeWidths;
28012
- }
28013
- function getSegmentGraphemePrefixWidths(seg, metrics, cache, emojiCorrection) {
28014
- if (metrics.graphemePrefixWidths !== undefined) return metrics.graphemePrefixWidths;
28015
- const prefixWidths = [];
28016
- const graphemeSegmenter = getSharedGraphemeSegmenter$1();
28017
- let prefix = '';
28018
- for (const gs of graphemeSegmenter.segment(seg)){
28019
- prefix += gs.segment;
28020
- const prefixMetrics = getSegmentMetrics(prefix, cache);
28021
- prefixWidths.push(getCorrectedSegmentWidth(prefix, prefixMetrics, emojiCorrection));
28022
- }
28023
- metrics.graphemePrefixWidths = prefixWidths.length > 1 ? prefixWidths : null;
28024
- return metrics.graphemePrefixWidths;
28025
- }
28026
- function getFontMeasurementState(font, needsEmojiCorrection) {
28027
- const ctx = getMeasureContext();
28028
- ctx.font = font;
28029
- const cache = getSegmentMetricCache(font);
28030
- const fontSize = parseFontSize(font);
28031
- const emojiCorrection = needsEmojiCorrection ? getEmojiCorrection(font, fontSize) : 0;
28032
- return {
28033
- cache,
28034
- fontSize,
28035
- emojiCorrection
28036
- };
28037
- }
28038
-
28039
- // Text measurement for browser environments using canvas measureText.
28040
- //
28041
- // Problem: DOM-based text measurement (getBoundingClientRect, offsetHeight)
28042
- // forces synchronous layout reflow. When components independently measure text,
28043
- // each measurement triggers a reflow of the entire document. This creates
28044
- // read/write interleaving that can cost 30ms+ per frame for 500 text blocks.
28045
- //
28046
- // Solution: two-phase measurement centered around canvas measureText.
28047
- // prepare(text, font) — segments text via Intl.Segmenter, measures each word
28048
- // via canvas, caches widths, and does one cached DOM calibration read per
28049
- // font when emoji correction is needed. Call once when text first appears.
28050
- // layout(prepared, maxWidth, lineHeight) — walks cached word widths with pure
28051
- // arithmetic to count lines and compute height. Call on every resize.
28052
- // ~0.0002ms per text.
28053
- //
28054
- // i18n: Intl.Segmenter handles CJK (per-character breaking), Thai, Arabic, etc.
28055
- // Bidi: simplified rich-path metadata for mixed LTR/RTL custom rendering.
28056
- // Punctuation merging: "better." measured as one unit (matches CSS behavior).
28057
- // Trailing whitespace: hangs past line edge without triggering breaks (CSS behavior).
28058
- // overflow-wrap: pre-measured grapheme widths enable character-level word breaking.
28059
- //
28060
- // Emoji correction: Chrome/Firefox canvas measures emoji wider than DOM at font
28061
- // sizes <24px on macOS (Apple Color Emoji). The inflation is constant per emoji
28062
- // grapheme at a given size, font-independent. Auto-detected by comparing canvas
28063
- // vs actual DOM emoji width (one cached DOM read per font). Safari canvas and
28064
- // DOM agree (both wider than fontSize), so correction = 0 there.
28065
- //
28066
- // Limitations:
28067
- // - system-ui font: canvas resolves to different optical variants than DOM on macOS.
28068
- // Use named fonts (Helvetica, Inter, etc.) for guaranteed accuracy.
28069
- // See RESEARCH.md "Discovery: system-ui font resolution mismatch".
28070
- //
28071
- // Based on Sebastian Markbage's text-layout research (github.com/chenglou/text-layout).
28072
- let sharedGraphemeSegmenter = null;
28073
- function getSharedGraphemeSegmenter() {
28074
- if (sharedGraphemeSegmenter === null) {
28075
- sharedGraphemeSegmenter = new Intl.Segmenter(undefined, {
28076
- granularity: 'grapheme'
28077
- });
28078
- }
28079
- return sharedGraphemeSegmenter;
28080
- }
28081
- // --- Public API ---
28082
- function createEmptyPrepared(includeSegments) {
28083
- {
28084
- return {
28085
- widths: [],
28086
- lineEndFitAdvances: [],
28087
- lineEndPaintAdvances: [],
28088
- kinds: [],
28089
- simpleLineWalkFastPath: true,
28090
- segLevels: null,
28091
- breakableWidths: [],
28092
- breakablePrefixWidths: [],
28093
- discretionaryHyphenWidth: 0,
28094
- tabStopAdvance: 0,
28095
- chunks: [],
28096
- segments: []
28097
- };
28098
- }
28099
- }
28100
- function measureAnalysis(analysis, font, includeSegments) {
28101
- const graphemeSegmenter = getSharedGraphemeSegmenter();
28102
- const engineProfile = getEngineProfile();
28103
- const { cache, emojiCorrection } = getFontMeasurementState(font, textMayContainEmoji(analysis.normalized));
28104
- const discretionaryHyphenWidth = getCorrectedSegmentWidth('-', getSegmentMetrics('-', cache), emojiCorrection);
28105
- const spaceWidth = getCorrectedSegmentWidth(' ', getSegmentMetrics(' ', cache), emojiCorrection);
28106
- const tabStopAdvance = spaceWidth * 8;
28107
- if (analysis.len === 0) return createEmptyPrepared();
28108
- const widths = [];
28109
- const lineEndFitAdvances = [];
28110
- const lineEndPaintAdvances = [];
28111
- const kinds = [];
28112
- let simpleLineWalkFastPath = analysis.chunks.length <= 1;
28113
- const segStarts = includeSegments ? [] : null;
28114
- const breakableWidths = [];
28115
- const breakablePrefixWidths = [];
28116
- const segments = includeSegments ? [] : null;
28117
- const preparedStartByAnalysisIndex = Array.from({
28118
- length: analysis.len
28119
- });
28120
- const preparedEndByAnalysisIndex = Array.from({
28121
- length: analysis.len
28122
- });
28123
- function pushMeasuredSegment(text, width, lineEndFitAdvance, lineEndPaintAdvance, kind, start, breakable, breakablePrefix) {
28124
- if (kind !== 'text' && kind !== 'space' && kind !== 'zero-width-break') {
28125
- simpleLineWalkFastPath = false;
28126
- }
28127
- widths.push(width);
28128
- lineEndFitAdvances.push(lineEndFitAdvance);
28129
- lineEndPaintAdvances.push(lineEndPaintAdvance);
28130
- kinds.push(kind);
28131
- segStarts?.push(start);
28132
- breakableWidths.push(breakable);
28133
- breakablePrefixWidths.push(breakablePrefix);
28134
- if (segments !== null) segments.push(text);
28135
- }
28136
- for(let mi = 0; mi < analysis.len; mi++){
28137
- preparedStartByAnalysisIndex[mi] = widths.length;
28138
- const segText = analysis.texts[mi];
28139
- const segWordLike = analysis.isWordLike[mi];
28140
- const segKind = analysis.kinds[mi];
28141
- const segStart = analysis.starts[mi];
28142
- if (segKind === 'soft-hyphen') {
28143
- pushMeasuredSegment(segText, 0, discretionaryHyphenWidth, discretionaryHyphenWidth, segKind, segStart, null, null);
28144
- preparedEndByAnalysisIndex[mi] = widths.length;
28145
- continue;
28146
- }
28147
- if (segKind === 'hard-break') {
28148
- pushMeasuredSegment(segText, 0, 0, 0, segKind, segStart, null, null);
28149
- preparedEndByAnalysisIndex[mi] = widths.length;
28150
- continue;
28151
- }
28152
- if (segKind === 'tab') {
28153
- pushMeasuredSegment(segText, 0, 0, 0, segKind, segStart, null, null);
28154
- preparedEndByAnalysisIndex[mi] = widths.length;
28155
- continue;
28156
- }
28157
- const segMetrics = getSegmentMetrics(segText, cache);
28158
- if (segKind === 'text' && segMetrics.containsCJK) {
28159
- let unitText = '';
28160
- let unitStart = 0;
28161
- for (const gs of graphemeSegmenter.segment(segText)){
28162
- const grapheme = gs.segment;
28163
- if (unitText.length === 0) {
28164
- unitText = grapheme;
28165
- unitStart = gs.index;
28166
- continue;
28167
- }
28168
- if (kinsokuEnd.has(unitText) || kinsokuStart.has(grapheme) || leftStickyPunctuation.has(grapheme) || engineProfile.carryCJKAfterClosingQuote && isCJK(grapheme) && endsWithClosingQuote(unitText)) {
28169
- unitText += grapheme;
28170
- continue;
28171
- }
28172
- const unitMetrics = getSegmentMetrics(unitText, cache);
28173
- const w = getCorrectedSegmentWidth(unitText, unitMetrics, emojiCorrection);
28174
- pushMeasuredSegment(unitText, w, w, w, 'text', segStart + unitStart, null, null);
28175
- unitText = grapheme;
28176
- unitStart = gs.index;
28177
- }
28178
- if (unitText.length > 0) {
28179
- const unitMetrics = getSegmentMetrics(unitText, cache);
28180
- const w = getCorrectedSegmentWidth(unitText, unitMetrics, emojiCorrection);
28181
- pushMeasuredSegment(unitText, w, w, w, 'text', segStart + unitStart, null, null);
28182
- }
28183
- preparedEndByAnalysisIndex[mi] = widths.length;
28184
- continue;
28185
- }
28186
- const w = getCorrectedSegmentWidth(segText, segMetrics, emojiCorrection);
28187
- const lineEndFitAdvance = segKind === 'space' || segKind === 'preserved-space' || segKind === 'zero-width-break' ? 0 : w;
28188
- const lineEndPaintAdvance = segKind === 'space' || segKind === 'zero-width-break' ? 0 : w;
28189
- if (segWordLike && segText.length > 1) {
28190
- const graphemeWidths = getSegmentGraphemeWidths(segText, segMetrics, cache, emojiCorrection);
28191
- const graphemePrefixWidths = engineProfile.preferPrefixWidthsForBreakableRuns ? getSegmentGraphemePrefixWidths(segText, segMetrics, cache, emojiCorrection) : null;
28192
- pushMeasuredSegment(segText, w, lineEndFitAdvance, lineEndPaintAdvance, segKind, segStart, graphemeWidths, graphemePrefixWidths);
28193
- } else {
28194
- pushMeasuredSegment(segText, w, lineEndFitAdvance, lineEndPaintAdvance, segKind, segStart, null, null);
28195
- }
28196
- preparedEndByAnalysisIndex[mi] = widths.length;
28197
- }
28198
- const chunks = mapAnalysisChunksToPreparedChunks(analysis.chunks, preparedStartByAnalysisIndex, preparedEndByAnalysisIndex);
28199
- const segLevels = segStarts === null ? null : computeSegmentLevels(analysis.normalized, segStarts);
28200
- if (segments !== null) {
28201
- return {
28202
- widths,
28203
- lineEndFitAdvances,
28204
- lineEndPaintAdvances,
28205
- kinds,
28206
- simpleLineWalkFastPath,
28207
- segLevels,
28208
- breakableWidths,
28209
- breakablePrefixWidths,
28210
- discretionaryHyphenWidth,
28211
- tabStopAdvance,
28212
- chunks,
28213
- segments
28214
- };
28215
- }
28216
- return {
28217
- widths,
28218
- lineEndFitAdvances,
28219
- lineEndPaintAdvances,
28220
- kinds,
28221
- simpleLineWalkFastPath,
28222
- segLevels,
28223
- breakableWidths,
28224
- breakablePrefixWidths,
28225
- discretionaryHyphenWidth,
28226
- tabStopAdvance,
28227
- chunks
28228
- };
28229
- }
28230
- function mapAnalysisChunksToPreparedChunks(chunks, preparedStartByAnalysisIndex, preparedEndByAnalysisIndex) {
28231
- const preparedChunks = [];
28232
- for(let i = 0; i < chunks.length; i++){
28233
- const chunk = chunks[i];
28234
- const startSegmentIndex = chunk.startSegmentIndex < preparedStartByAnalysisIndex.length ? preparedStartByAnalysisIndex[chunk.startSegmentIndex] : preparedEndByAnalysisIndex[preparedEndByAnalysisIndex.length - 1] ?? 0;
28235
- const endSegmentIndex = chunk.endSegmentIndex < preparedStartByAnalysisIndex.length ? preparedStartByAnalysisIndex[chunk.endSegmentIndex] : preparedEndByAnalysisIndex[preparedEndByAnalysisIndex.length - 1] ?? 0;
28236
- const consumedEndSegmentIndex = chunk.consumedEndSegmentIndex < preparedStartByAnalysisIndex.length ? preparedStartByAnalysisIndex[chunk.consumedEndSegmentIndex] : preparedEndByAnalysisIndex[preparedEndByAnalysisIndex.length - 1] ?? 0;
28237
- preparedChunks.push({
28238
- startSegmentIndex,
28239
- endSegmentIndex,
28240
- consumedEndSegmentIndex
28241
- });
28242
- }
28243
- return preparedChunks;
28244
- }
28245
- function prepareInternal(text, font, includeSegments, options) {
28246
- const analysis = analyzeText(text, getEngineProfile(), options?.whiteSpace);
28247
- return measureAnalysis(analysis, font, includeSegments);
28248
- }
28249
- // Rich variant used by callers that need enough information to render the
28250
- // laid-out lines themselves.
28251
- function prepareWithSegments(text, font, options) {
28252
- return prepareInternal(text, font, true, options);
28253
- }
28254
-
28255
26526
  var hyphenated_cjs = {};
28256
26527
 
28257
26528
  var hyphenatedEnUs_cjs;
@@ -33556,19 +31827,19 @@
33556
31827
  var prepared = prepareWithSegments(sentence, "10px sans-serif", {
33557
31828
  whiteSpace: "pre-wrap"
33558
31829
  });
33559
- var segments = prepared.segments, kinds = prepared.kinds, breakableWidths = prepared.breakableWidths;
31830
+ var segments = prepared.segments, kinds = prepared.kinds, breakableFitAdvances = prepared.breakableFitAdvances;
33560
31831
  var words = [];
33561
31832
  var prevKind = null;
33562
31833
  var wordHasBreakable = false;
33563
31834
  for(var i = 0; i < segments.length; i++){
33564
31835
  if ((kinds[i] === "space" || kinds[i] === "preserved-space") && words.length > 0) {
33565
31836
  words[words.length - 1] += segments[i];
33566
- } else if (prevKind === "text" && kinds[i] === "text" && words.length > 0 && !isBreakable(segments[i]) && !isBreakable(segments[i - 1]) && !(breakableWidths[i] !== null && wordHasBreakable)) {
31837
+ } else if (prevKind === "text" && kinds[i] === "text" && words.length > 0 && !isBreakable(segments[i]) && !isBreakable(segments[i - 1]) && !(breakableFitAdvances[i] !== null && wordHasBreakable)) {
33567
31838
  words[words.length - 1] += segments[i];
33568
- if (breakableWidths[i] !== null) wordHasBreakable = true;
31839
+ if (breakableFitAdvances[i] !== null) wordHasBreakable = true;
33569
31840
  } else {
33570
31841
  words.push(segments[i]);
33571
- wordHasBreakable = breakableWidths[i] !== null;
31842
+ wordHasBreakable = breakableFitAdvances[i] !== null;
33572
31843
  }
33573
31844
  prevKind = kinds[i];
33574
31845
  }