1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | 26x 26x 26x 26x 26x 26x 26x 19x 19x 26x 21x 21x 1x 20x 25x 25x 21x 21x 1x 20x 24x 2x 22x 26x 26x 26x | /** * @copyright 2016, Miles Johnson * @license https://opensource.org/licenses/MIT * @flow */ /* eslint-disable react/no-unused-prop-types */ import React, { PropTypes } from 'react'; import Filter from './Filter'; import Matcher from './Matcher'; import Parser from './Parser'; import Element from './components/Element'; import type { ParsedNodes, InterweaveProps, AfterParseCallback, BeforeParseCallback, } from './types'; export default class Interweave extends React.Component { // eslint-disable-next-line react/sort-comp props: InterweaveProps; static propTypes = { content: PropTypes.string, disableFilters: PropTypes.bool, disableMatchers: PropTypes.bool, disableLineBreaks: PropTypes.bool, disableWhitelist: PropTypes.bool, emptyContent: PropTypes.node, filters: PropTypes.arrayOf(PropTypes.instanceOf(Filter)), matchers: PropTypes.arrayOf(PropTypes.instanceOf(Matcher)), noHtml: PropTypes.bool, onBeforeParse: PropTypes.func, onAfterParse: PropTypes.func, tagName: PropTypes.oneOf(['span', 'div', 'p']), }; static defaultProps = { content: '', emptyContent: null, filters: [], matchers: [], tagName: 'span', }; /** * Parse the markup and apply hooks. */ parseMarkup(): ParsedNodes | ?React.Element<*> { const { tagName, // eslint-disable-line content, emptyContent, onBeforeParse, onAfterParse, matchers, disableMatchers, filters, disableFilters, ...props } = this.props; let markup = content; const allMatchers = disableMatchers ? [] : matchers; const allFilters = disableFilters ? [] : filters; const beforeCallbacks = onBeforeParse ? [onBeforeParse] : []; const afterCallbacks = onAfterParse ? [onAfterParse] : []; // Inherit callbacks from matchers allMatchers.forEach((matcher: Matcher<*>) => { beforeCallbacks.push(matcher.onBeforeParse.bind(matcher)); afterCallbacks.push(matcher.onAfterParse.bind(matcher)); }); // Trigger before callbacks markup = beforeCallbacks.reduce((string: string, callback: BeforeParseCallback) => { string = callback(string); if (typeof string !== 'string') { throw new TypeError('Interweave `onBeforeParse` must return a valid HTML string.'); } return string; }, markup); // Parse the markup markup = new Parser(markup, props, allMatchers, allFilters).parse(); // Trigger after callbacks markup = afterCallbacks.reduce((nodes: ParsedNodes, callback: AfterParseCallback) => { nodes = callback(nodes); if (!Array.isArray(nodes)) { throw new TypeError('Interweave `onAfterParse` must return an array of strings and React elements.'); } return nodes; }, markup); if (!markup.length) { return emptyContent; } return markup; } /** * Render the component by parsing the markup. */ render() { const { tagName, noHtml } = this.props; const className = noHtml ? 'interweave--no-html' : ''; return ( <Element tagName={tagName} className={className}> {this.parseMarkup()} </Element> ); } } |