/** * CaretFinder.js * * Released under LGPL License. * Copyright (c) 1999-2017 Ephox Corp. All rights reserved * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing */ import { Fun, Option } from '@ephox/katamari'; import * as CaretCandidate from './CaretCandidate'; import CaretPosition from './CaretPosition'; import * as CaretUtils from './CaretUtils'; import { CaretWalker } from './CaretWalker'; import NodeType from '../dom/NodeType'; const walkToPositionIn = (forward: boolean, root: Node, start: Node) => { const position = forward ? CaretPosition.before(start) : CaretPosition.after(start); return fromPosition(forward, root, position); }; const afterElement = (node): CaretPosition => { return NodeType.isBr(node) ? CaretPosition.before(node) : CaretPosition.after(node); }; const isBeforeOrStart = (position: CaretPosition): boolean => { if (CaretPosition.isTextPosition(position)) { return position.offset() === 0; } else { return CaretCandidate.isCaretCandidate(position.getNode()); } }; const isAfterOrEnd = (position: CaretPosition): boolean => { if (CaretPosition.isTextPosition(position)) { const container = position.container() as Text; return position.offset() === container.data.length; } else { return CaretCandidate.isCaretCandidate(position.getNode(true)); } }; const isBeforeAfterSameElement = (from: CaretPosition, to: CaretPosition): boolean => { return !CaretPosition.isTextPosition(from) && !CaretPosition.isTextPosition(to) && from.getNode() === to.getNode(true); }; const isAtBr = (position: CaretPosition): boolean => { return !CaretPosition.isTextPosition(position) && NodeType.isBr(position.getNode()); }; const shouldSkipPosition = (forward: boolean, from: CaretPosition, to: CaretPosition) => { if (forward) { return !isBeforeAfterSameElement(from, to) && !isAtBr(from) && isAfterOrEnd(from) && isBeforeOrStart(to); } else { return !isBeforeAfterSameElement(to, from) && isBeforeOrStart(from) && isAfterOrEnd(to); } }; // Finds:
a|b
->a|b
const fromPosition = function (forward: boolean, root: Node, pos: CaretPosition) { const walker = CaretWalker(root); return Option.from(forward ? walker.next(pos) : walker.prev(pos)); }; // Finds:a|b
->ab|
const navigate = (forward: boolean, root: Element, from: CaretPosition) => { return fromPosition(forward, root, from).bind(function (to) { if (CaretUtils.isInSameBlock(from, to, root) && shouldSkipPosition(forward, from, to)) { return fromPosition(forward, root, to); } else { return Option.some(to); } }); }; const positionIn = (forward: boolean, element: Element): Option