import type { Universe } from '@ephox/boss'; import { Fun, Optional } from '@ephox/katamari'; import * as Parent from '../api/general/Parent'; import type { ZoneViewports } from '../api/general/ZoneViewports'; import * as Clustering from '../words/Clustering'; import { WordDecision, type WordDecisionItem } from '../words/WordDecision'; import { LanguageZones, type ZoneDetails } from './LanguageZones'; import * as Zones from './Zones'; import * as ZoneWalker from './ZoneWalker'; type Zones = Zones.Zones; const rangeOn = (universe: Universe, first: E, last: E, envLang: string, transform: (universe: Universe, item: E) => WordDecisionItem, viewport: ZoneViewports): Optional[]> => { const ancestor = universe.eq(first, last) ? Optional.some(first) : universe.property().parent(first); return ancestor.map((parent) => { const defaultLang = LanguageZones.calculate(universe, parent).getOr(envLang); return ZoneWalker.walk(universe, first, last, defaultLang, transform, viewport); }); }; const fromBoundedWith = (universe: Universe, left: E, right: E, envLang: string, transform: (universe: Universe, item: E) => WordDecisionItem, viewport: ZoneViewports): Zones => { const groups: ZoneDetails[] = Parent.subset(universe, left, right).bind((children) => { if (children.length === 0) { return Optional.none[]>(); } const first = children[0]; const last = children[children.length - 1]; return rangeOn(universe, first, last, envLang, transform, viewport); }).getOr([]); return Zones.fromWalking(universe, groups); }; const fromBounded = (universe: Universe, left: E, right: E, envLang: string, viewport: ZoneViewports): Zones => { return fromBoundedWith(universe, left, right, envLang, WordDecision.detail, viewport); }; const fromRange = (universe: Universe, start: E, finish: E, envLang: string, viewport: ZoneViewports): Zones => { const edges = Clustering.getEdges(universe, start, finish, Fun.never); const transform = transformEdges(edges.left, edges.right); return fromBoundedWith(universe, edges.left.item, edges.right.item, envLang, transform, viewport); }; const transformEdges = (leftEdge: WordDecisionItem, rightEdge: WordDecisionItem) => { return (universe: Universe, element: E): WordDecisionItem => { if (universe.eq(element, leftEdge.item)) { return leftEdge; } else if (universe.eq(element, rightEdge.item)) { return rightEdge; } else { return WordDecision.detail(universe, element); } }; }; const fromInline = (universe: Universe, element: E, envLang: string, viewport: ZoneViewports): Zones => { // Create a cluster that branches to the edge of words, and then apply the zones. We will move // past language boundaries, because we might need to be retokenizing words post a language // change const bounded = Clustering.byBoundary(universe, element); const transform = transformEdges(bounded.left, bounded.right); return bounded.isEmpty ? empty() : fromBoundedWith(universe, bounded.left.item, bounded.right.item, envLang, transform, viewport); }; const empty = (): Zones => { return { zones: [] }; }; export { fromRange, transformEdges, fromBounded, fromBoundedWith, fromInline, empty };