/** * Copyright (c) 2013-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule ContentBlockNode * @format * @flow * * 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 Immutable = require('immutable'); const findRangesImmutable = require('./findRangesImmutable'); 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) implements BlockNode { constructor(props: ContentBlockNodeConfig = defaultRecord) { 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 { var character = this.getCharacterList().get(offset); return character ? character.getStyle() : EMPTY_SET; } getEntityAt(offset: number): ?string { var 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;