/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @format * @flow * @emails oncall+draft_js * * This file is a fork of ContentBlock adding support for nesting references by * providing links to children, parent, prevSibling, and nextSibling. * * This is unstable and not part of the public API and should not be used by * production systems. This file may be update/removed without notice. */ 'use strict'; import type { BlockNode, BlockNodeConfig, BlockNodeKey } from "./BlockNode"; import type { DraftBlockType } from "./DraftBlockType"; import type { DraftInlineStyle } from "./DraftInlineStyle"; const CharacterMetadata = require("./CharacterMetadata"); const findRangesImmutable = require("./findRangesImmutable"); const Immutable = require("immutable"); const { List, Map, OrderedSet, Record, Repeat } = Immutable; type ContentBlockNodeConfig = BlockNodeConfig & { children?: List, parent?: ?BlockNodeKey, prevSibling?: ?BlockNodeKey, nextSibling?: ?BlockNodeKey, ... }; const EMPTY_SET = OrderedSet(); const defaultRecord: ContentBlockNodeConfig = { parent: null, characterList: List(), data: Map(), depth: 0, key: '', text: '', type: 'unstyled', children: List(), prevSibling: null, nextSibling: null }; const haveEqualStyle = (charA: CharacterMetadata, charB: CharacterMetadata): boolean => charA.getStyle() === charB.getStyle(); const haveEqualEntity = (charA: CharacterMetadata, charB: CharacterMetadata): boolean => charA.getEntity() === charB.getEntity(); const decorateCharacterList = (config: ContentBlockNodeConfig): ContentBlockNodeConfig => { if (!config) { return config; } const { characterList, text } = config; if (text && !characterList) { config.characterList = List(Repeat(CharacterMetadata.EMPTY, text.length)); } return config; }; class ContentBlockNode extends (Record(defaultRecord): any) implements BlockNode { constructor(props: ContentBlockNodeConfig = defaultRecord) { /* eslint-disable-next-line constructor-super */ super(decorateCharacterList(props)); } getKey(): BlockNodeKey { return this.get('key'); } getType(): DraftBlockType { return this.get('type'); } getText(): string { return this.get('text'); } getCharacterList(): List { return this.get('characterList'); } getLength(): number { return this.getText().length; } getDepth(): number { return this.get('depth'); } getData(): Map { return this.get('data'); } getInlineStyleAt(offset: number): DraftInlineStyle { const character = this.getCharacterList().get(offset); return character ? character.getStyle() : EMPTY_SET; } getEntityAt(offset: number): ?string { const character = this.getCharacterList().get(offset); return character ? character.getEntity() : null; } getChildKeys(): List { return this.get('children'); } getParentKey(): ?BlockNodeKey { return this.get('parent'); } getPrevSiblingKey(): ?BlockNodeKey { return this.get('prevSibling'); } getNextSiblingKey(): ?BlockNodeKey { return this.get('nextSibling'); } findStyleRanges(filterFn: (value: CharacterMetadata) => boolean, callback: (start: number, end: number) => void): void { findRangesImmutable(this.getCharacterList(), haveEqualStyle, filterFn, callback); } findEntityRanges(filterFn: (value: CharacterMetadata) => boolean, callback: (start: number, end: number) => void): void { findRangesImmutable(this.getCharacterList(), haveEqualEntity, filterFn, callback); } } module.exports = ContentBlockNode;