import { Arr, Type } from '@ephox/katamari'; import { ContentEditable, SugarElement, SugarNode } from '@ephox/sugar'; import * as Data from './Data'; import * as Html from './Html'; const isMatch = (n: SugarElement): n is SugarElement => { const value = SugarNode.value(n); return SugarNode.isText(n) && Type.isString(value) && Data.regExp.test(value); }; const isContentEditableFalse = (node: SugarElement) => SugarNode.isHTMLElement(node) && ContentEditable.getRaw(node) === 'false'; const isChildEditable = (node: SugarElement, currentState: boolean) => { if (SugarNode.isHTMLElement(node)) { const value = ContentEditable.getRaw(node); if (value === 'true') { return true; } else if (value === 'false') { return false; } } return currentState; }; // inlined sugars PredicateFilter.descendants for file size but also make it only act on editable nodes it changes the current editable state when it traveses down const filterEditableDescendants = (scope: SugarElement, predicate: (x: SugarElement) => x is SugarElement, editable: boolean): SugarElement[] => { let result: SugarElement[] = []; const dom = scope.dom; const children = Arr.map(dom.childNodes, SugarElement.fromDom); Arr.each(children, (x) => { if (editable && !isContentEditableFalse(x) && predicate(x)) { result = result.concat([ x ]); } result = result.concat(filterEditableDescendants(x, predicate, isChildEditable(x, editable))); }); return result; }; const findParentElm = (elm: Node, rootElm: Element): Element | undefined => { while (elm.parentNode) { if (elm.parentNode === rootElm) { return rootElm; } elm = elm.parentNode; } return undefined; }; const replaceWithSpans = (text: string): string => text.replace(Data.regExpGlobal, Html.wrapCharWithSpan); export { isMatch, filterEditableDescendants, findParentElm, replaceWithSpans };