Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

764 строки
24 KiB

  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @format
  8. *
  9. * @emails oncall+draft_js
  10. */
  11. 'use strict';
  12. var _knownListItemDepthCl;
  13. function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
  14. function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
  15. var CharacterMetadata = require("./CharacterMetadata");
  16. var ContentBlock = require("./ContentBlock");
  17. var ContentBlockNode = require("./ContentBlockNode");
  18. var DefaultDraftBlockRenderMap = require("./DefaultDraftBlockRenderMap");
  19. var DraftEntity = require("./DraftEntity");
  20. var URI = require("fbjs/lib/URI");
  21. var cx = require("fbjs/lib/cx");
  22. var generateRandomKey = require("./generateRandomKey");
  23. var getSafeBodyFromHTML = require("./getSafeBodyFromHTML");
  24. var gkx = require("./gkx");
  25. var _require = require("immutable"),
  26. List = _require.List,
  27. Map = _require.Map,
  28. OrderedSet = _require.OrderedSet;
  29. var isHTMLAnchorElement = require("./isHTMLAnchorElement");
  30. var isHTMLBRElement = require("./isHTMLBRElement");
  31. var isHTMLElement = require("./isHTMLElement");
  32. var isHTMLImageElement = require("./isHTMLImageElement");
  33. var experimentalTreeDataSupport = gkx('draft_tree_data_support');
  34. var NBSP = '&nbsp;';
  35. var SPACE = ' '; // used for replacing characters in HTML
  36. var REGEX_CR = new RegExp('\r', 'g');
  37. var REGEX_LF = new RegExp('\n', 'g');
  38. var REGEX_LEADING_LF = new RegExp('^\n', 'g');
  39. var REGEX_NBSP = new RegExp(NBSP, 'g');
  40. var REGEX_CARRIAGE = new RegExp('&#13;?', 'g');
  41. var REGEX_ZWS = new RegExp('&#8203;?', 'g'); // https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight
  42. var boldValues = ['bold', 'bolder', '500', '600', '700', '800', '900'];
  43. var notBoldValues = ['light', 'lighter', 'normal', '100', '200', '300', '400'];
  44. var anchorAttr = ['className', 'href', 'rel', 'target', 'title'];
  45. var imgAttr = ['alt', 'className', 'height', 'src', 'width'];
  46. var knownListItemDepthClasses = (_knownListItemDepthCl = {}, _defineProperty(_knownListItemDepthCl, cx('public/DraftStyleDefault/depth0'), 0), _defineProperty(_knownListItemDepthCl, cx('public/DraftStyleDefault/depth1'), 1), _defineProperty(_knownListItemDepthCl, cx('public/DraftStyleDefault/depth2'), 2), _defineProperty(_knownListItemDepthCl, cx('public/DraftStyleDefault/depth3'), 3), _defineProperty(_knownListItemDepthCl, cx('public/DraftStyleDefault/depth4'), 4), _knownListItemDepthCl);
  47. var HTMLTagToRawInlineStyleMap = Map({
  48. b: 'BOLD',
  49. code: 'CODE',
  50. del: 'STRIKETHROUGH',
  51. em: 'ITALIC',
  52. i: 'ITALIC',
  53. s: 'STRIKETHROUGH',
  54. strike: 'STRIKETHROUGH',
  55. strong: 'BOLD',
  56. u: 'UNDERLINE',
  57. mark: 'HIGHLIGHT'
  58. });
  59. /**
  60. * Build a mapping from HTML tags to draftjs block types
  61. * out of a BlockRenderMap.
  62. *
  63. * The BlockTypeMap for the default BlockRenderMap looks like this:
  64. * Map({
  65. * h1: 'header-one',
  66. * h2: 'header-two',
  67. * h3: 'header-three',
  68. * h4: 'header-four',
  69. * h5: 'header-five',
  70. * h6: 'header-six',
  71. * blockquote: 'blockquote',
  72. * figure: 'atomic',
  73. * pre: ['code-block'],
  74. * div: 'unstyled',
  75. * p: 'unstyled',
  76. * li: ['ordered-list-item', 'unordered-list-item'],
  77. * })
  78. */
  79. var buildBlockTypeMap = function buildBlockTypeMap(blockRenderMap) {
  80. var blockTypeMap = {};
  81. blockRenderMap.mapKeys(function (blockType, desc) {
  82. var elements = [desc.element];
  83. if (desc.aliasedElements !== undefined) {
  84. elements.push.apply(elements, desc.aliasedElements);
  85. }
  86. elements.forEach(function (element) {
  87. if (blockTypeMap[element] === undefined) {
  88. blockTypeMap[element] = blockType;
  89. } else if (typeof blockTypeMap[element] === 'string') {
  90. blockTypeMap[element] = [blockTypeMap[element], blockType];
  91. } else {
  92. blockTypeMap[element].push(blockType);
  93. }
  94. });
  95. });
  96. return Map(blockTypeMap);
  97. };
  98. /**
  99. * If we're pasting from one DraftEditor to another we can check to see if
  100. * existing list item depth classes are being used and preserve this style
  101. */
  102. var getListItemDepth = function getListItemDepth(node) {
  103. var depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  104. Object.keys(knownListItemDepthClasses).some(function (depthClass) {
  105. if (node.classList.contains(depthClass)) {
  106. depth = knownListItemDepthClasses[depthClass];
  107. }
  108. });
  109. return depth;
  110. };
  111. /**
  112. * Return true if the provided HTML Element can be used to build a
  113. * Draftjs-compatible link.
  114. */
  115. var isValidAnchor = function isValidAnchor(node) {
  116. if (!isHTMLAnchorElement(node)) {
  117. return false;
  118. }
  119. var anchorNode = node;
  120. return !!(anchorNode.href && (anchorNode.protocol === 'http:' || anchorNode.protocol === 'https:' || anchorNode.protocol === 'mailto:'));
  121. };
  122. /**
  123. * Return true if the provided HTML Element can be used to build a
  124. * Draftjs-compatible image.
  125. */
  126. var isValidImage = function isValidImage(node) {
  127. if (!isHTMLImageElement(node)) {
  128. return false;
  129. }
  130. var imageNode = node;
  131. return !!(imageNode.attributes.getNamedItem('src') && imageNode.attributes.getNamedItem('src').value);
  132. };
  133. /**
  134. * Try to guess the inline style of an HTML element based on its css
  135. * styles (font-weight, font-style and text-decoration).
  136. */
  137. var styleFromNodeAttributes = function styleFromNodeAttributes(node, style) {
  138. if (!isHTMLElement(node)) {
  139. return style;
  140. }
  141. var htmlElement = node;
  142. var fontWeight = htmlElement.style.fontWeight;
  143. var fontStyle = htmlElement.style.fontStyle;
  144. var textDecoration = htmlElement.style.textDecoration;
  145. return style.withMutations(function (style) {
  146. if (boldValues.indexOf(fontWeight) >= 0) {
  147. style.add('BOLD');
  148. } else if (notBoldValues.indexOf(fontWeight) >= 0) {
  149. style.remove('BOLD');
  150. }
  151. if (fontStyle === 'italic') {
  152. style.add('ITALIC');
  153. } else if (fontStyle === 'normal') {
  154. style.remove('ITALIC');
  155. }
  156. if (textDecoration === 'underline') {
  157. style.add('UNDERLINE');
  158. }
  159. if (textDecoration === 'line-through') {
  160. style.add('STRIKETHROUGH');
  161. }
  162. if (textDecoration === 'none') {
  163. style.remove('UNDERLINE');
  164. style.remove('STRIKETHROUGH');
  165. }
  166. });
  167. };
  168. /**
  169. * Determine if a nodeName is a list type, 'ul' or 'ol'
  170. */
  171. var isListNode = function isListNode(nodeName) {
  172. return nodeName === 'ul' || nodeName === 'ol';
  173. };
  174. /**
  175. * ContentBlockConfig is a mutable data structure that holds all
  176. * the information required to build a ContentBlock and an array of
  177. * all the child nodes (childConfigs).
  178. * It is being used a temporary data structure by the
  179. * ContentBlocksBuilder class.
  180. */
  181. /**
  182. * ContentBlocksBuilder builds a list of ContentBlocks and an Entity Map
  183. * out of one (or several) HTMLElement(s).
  184. *
  185. * The algorithm has two passes: first it builds a tree of ContentBlockConfigs
  186. * by walking through the HTML nodes and their children, then it walks the
  187. * ContentBlockConfigs tree to compute parents/siblings and create
  188. * the actual ContentBlocks.
  189. *
  190. * Typical usage is:
  191. * new ContentBlocksBuilder()
  192. * .addDOMNode(someHTMLNode)
  193. * .addDOMNode(someOtherHTMLNode)
  194. * .getContentBlocks();
  195. *
  196. */
  197. var ContentBlocksBuilder =
  198. /*#__PURE__*/
  199. function () {
  200. // Most of the method in the class depend on the state of the content builder
  201. // (i.e. currentBlockType, currentDepth, currentEntity etc.). Though it may
  202. // be confusing at first, it made the code simpler than the alternative which
  203. // is to pass those values around in every call.
  204. // The following attributes are used to accumulate text and styles
  205. // as we are walking the HTML node tree.
  206. // Describes the future ContentState as a tree of content blocks
  207. // The content blocks generated from the blockConfigs
  208. // Entity map use to store links and images found in the HTML nodes
  209. // Map HTML tags to draftjs block types and disambiguation function
  210. function ContentBlocksBuilder(blockTypeMap, disambiguate) {
  211. _defineProperty(this, "characterList", List());
  212. _defineProperty(this, "currentBlockType", 'unstyled');
  213. _defineProperty(this, "currentDepth", 0);
  214. _defineProperty(this, "currentEntity", null);
  215. _defineProperty(this, "currentText", '');
  216. _defineProperty(this, "wrapper", null);
  217. _defineProperty(this, "blockConfigs", []);
  218. _defineProperty(this, "contentBlocks", []);
  219. _defineProperty(this, "entityMap", DraftEntity);
  220. _defineProperty(this, "blockTypeMap", void 0);
  221. _defineProperty(this, "disambiguate", void 0);
  222. this.clear();
  223. this.blockTypeMap = blockTypeMap;
  224. this.disambiguate = disambiguate;
  225. }
  226. /**
  227. * Clear the internal state of the ContentBlocksBuilder
  228. */
  229. var _proto = ContentBlocksBuilder.prototype;
  230. _proto.clear = function clear() {
  231. this.characterList = List();
  232. this.blockConfigs = [];
  233. this.currentBlockType = 'unstyled';
  234. this.currentDepth = 0;
  235. this.currentEntity = null;
  236. this.currentText = '';
  237. this.entityMap = DraftEntity;
  238. this.wrapper = null;
  239. this.contentBlocks = [];
  240. }
  241. /**
  242. * Add an HTMLElement to the ContentBlocksBuilder
  243. */
  244. ;
  245. _proto.addDOMNode = function addDOMNode(node) {
  246. var _this$blockConfigs;
  247. this.contentBlocks = [];
  248. this.currentDepth = 0; // Converts the HTML node to block config
  249. (_this$blockConfigs = this.blockConfigs).push.apply(_this$blockConfigs, this._toBlockConfigs([node], OrderedSet())); // There might be some left over text in the builder's
  250. // internal state, if so make a ContentBlock out of it.
  251. this._trimCurrentText();
  252. if (this.currentText !== '') {
  253. this.blockConfigs.push(this._makeBlockConfig());
  254. } // for chaining
  255. return this;
  256. }
  257. /**
  258. * Return the ContentBlocks and the EntityMap that corresponds
  259. * to the previously added HTML nodes.
  260. */
  261. ;
  262. _proto.getContentBlocks = function getContentBlocks() {
  263. if (this.contentBlocks.length === 0) {
  264. if (experimentalTreeDataSupport) {
  265. this._toContentBlocks(this.blockConfigs);
  266. } else {
  267. this._toFlatContentBlocks(this.blockConfigs);
  268. }
  269. }
  270. return {
  271. contentBlocks: this.contentBlocks,
  272. entityMap: this.entityMap
  273. };
  274. } // ***********************************WARNING******************************
  275. // The methods below this line are private - don't call them directly.
  276. /**
  277. * Generate a new ContentBlockConfig out of the current internal state
  278. * of the builder, then clears the internal state.
  279. */
  280. ;
  281. _proto._makeBlockConfig = function _makeBlockConfig() {
  282. var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  283. var key = config.key || generateRandomKey();
  284. var block = _objectSpread({
  285. key: key,
  286. type: this.currentBlockType,
  287. text: this.currentText,
  288. characterList: this.characterList,
  289. depth: this.currentDepth,
  290. parent: null,
  291. children: List(),
  292. prevSibling: null,
  293. nextSibling: null,
  294. childConfigs: []
  295. }, config);
  296. this.characterList = List();
  297. this.currentBlockType = 'unstyled';
  298. this.currentText = '';
  299. return block;
  300. }
  301. /**
  302. * Converts an array of HTML elements to a multi-root tree of content
  303. * block configs. Some text content may be left in the builders internal
  304. * state to enable chaining sucessive calls.
  305. */
  306. ;
  307. _proto._toBlockConfigs = function _toBlockConfigs(nodes, style) {
  308. var blockConfigs = [];
  309. for (var i = 0; i < nodes.length; i++) {
  310. var node = nodes[i];
  311. var nodeName = node.nodeName.toLowerCase();
  312. if (nodeName === 'body' || isListNode(nodeName)) {
  313. // body, ol and ul are 'block' type nodes so create a block config
  314. // with the text accumulated so far (if any)
  315. this._trimCurrentText();
  316. if (this.currentText !== '') {
  317. blockConfigs.push(this._makeBlockConfig());
  318. } // body, ol and ul nodes are ignored, but their children are inlined in
  319. // the parent block config.
  320. var wasCurrentDepth = this.currentDepth;
  321. var wasWrapper = this.wrapper;
  322. if (isListNode(nodeName)) {
  323. this.wrapper = nodeName;
  324. if (isListNode(wasWrapper)) {
  325. this.currentDepth++;
  326. }
  327. }
  328. blockConfigs.push.apply(blockConfigs, this._toBlockConfigs(Array.from(node.childNodes), style));
  329. this.currentDepth = wasCurrentDepth;
  330. this.wrapper = wasWrapper;
  331. continue;
  332. }
  333. var blockType = this.blockTypeMap.get(nodeName);
  334. if (blockType !== undefined) {
  335. // 'block' type node means we need to create a block config
  336. // with the text accumulated so far (if any)
  337. this._trimCurrentText();
  338. if (this.currentText !== '') {
  339. blockConfigs.push(this._makeBlockConfig());
  340. }
  341. var _wasCurrentDepth = this.currentDepth;
  342. var _wasWrapper = this.wrapper;
  343. this.wrapper = nodeName === 'pre' ? 'pre' : this.wrapper;
  344. if (typeof blockType !== 'string') {
  345. blockType = this.disambiguate(nodeName, this.wrapper) || blockType[0] || 'unstyled';
  346. }
  347. if (!experimentalTreeDataSupport && isHTMLElement(node) && (blockType === 'unordered-list-item' || blockType === 'ordered-list-item')) {
  348. var htmlElement = node;
  349. this.currentDepth = getListItemDepth(htmlElement, this.currentDepth);
  350. }
  351. var key = generateRandomKey();
  352. var childConfigs = this._toBlockConfigs(Array.from(node.childNodes), style);
  353. this._trimCurrentText();
  354. blockConfigs.push(this._makeBlockConfig({
  355. key: key,
  356. childConfigs: childConfigs,
  357. type: blockType
  358. }));
  359. this.currentDepth = _wasCurrentDepth;
  360. this.wrapper = _wasWrapper;
  361. continue;
  362. }
  363. if (nodeName === '#text') {
  364. this._addTextNode(node, style);
  365. continue;
  366. }
  367. if (nodeName === 'br') {
  368. this._addBreakNode(node, style);
  369. continue;
  370. }
  371. if (isValidImage(node)) {
  372. this._addImgNode(node, style);
  373. continue;
  374. }
  375. if (isValidAnchor(node)) {
  376. this._addAnchorNode(node, blockConfigs, style);
  377. continue;
  378. }
  379. var newStyle = style;
  380. if (HTMLTagToRawInlineStyleMap.has(nodeName)) {
  381. newStyle = newStyle.add(HTMLTagToRawInlineStyleMap.get(nodeName));
  382. }
  383. newStyle = styleFromNodeAttributes(node, newStyle);
  384. blockConfigs.push.apply(blockConfigs, this._toBlockConfigs(Array.from(node.childNodes), newStyle));
  385. }
  386. return blockConfigs;
  387. }
  388. /**
  389. * Append a string of text to the internal buffer.
  390. */
  391. ;
  392. _proto._appendText = function _appendText(text, style) {
  393. var _this$characterList;
  394. this.currentText += text;
  395. var characterMetadata = CharacterMetadata.create({
  396. style: style,
  397. entity: this.currentEntity
  398. });
  399. this.characterList = (_this$characterList = this.characterList).push.apply(_this$characterList, Array(text.length).fill(characterMetadata));
  400. }
  401. /**
  402. * Trim the text in the internal buffer.
  403. */
  404. ;
  405. _proto._trimCurrentText = function _trimCurrentText() {
  406. var l = this.currentText.length;
  407. var begin = l - this.currentText.trimLeft().length;
  408. var end = this.currentText.trimRight().length; // We should not trim whitespaces for which an entity is defined.
  409. var entity = this.characterList.findEntry(function (characterMetadata) {
  410. return characterMetadata.getEntity() !== null;
  411. });
  412. begin = entity !== undefined ? Math.min(begin, entity[0]) : begin;
  413. entity = this.characterList.reverse().findEntry(function (characterMetadata) {
  414. return characterMetadata.getEntity() !== null;
  415. });
  416. end = entity !== undefined ? Math.max(end, l - entity[0]) : end;
  417. if (begin > end) {
  418. this.currentText = '';
  419. this.characterList = List();
  420. } else {
  421. this.currentText = this.currentText.slice(begin, end);
  422. this.characterList = this.characterList.slice(begin, end);
  423. }
  424. }
  425. /**
  426. * Add the content of an HTML text node to the internal state
  427. */
  428. ;
  429. _proto._addTextNode = function _addTextNode(node, style) {
  430. var text = node.textContent;
  431. var trimmedText = text.trim(); // If we are not in a pre block and the trimmed content is empty,
  432. // normalize to a single space.
  433. if (trimmedText === '' && this.wrapper !== 'pre') {
  434. text = ' ';
  435. }
  436. if (this.wrapper !== 'pre') {
  437. // Trim leading line feed, which is invisible in HTML
  438. text = text.replace(REGEX_LEADING_LF, ''); // Can't use empty string because MSWord
  439. text = text.replace(REGEX_LF, SPACE);
  440. }
  441. this._appendText(text, style);
  442. };
  443. _proto._addBreakNode = function _addBreakNode(node, style) {
  444. if (!isHTMLBRElement(node)) {
  445. return;
  446. }
  447. this._appendText('\n', style);
  448. }
  449. /**
  450. * Add the content of an HTML img node to the internal state
  451. */
  452. ;
  453. _proto._addImgNode = function _addImgNode(node, style) {
  454. if (!isHTMLImageElement(node)) {
  455. return;
  456. }
  457. var image = node;
  458. var entityConfig = {};
  459. imgAttr.forEach(function (attr) {
  460. var imageAttribute = image.getAttribute(attr);
  461. if (imageAttribute) {
  462. entityConfig[attr] = imageAttribute;
  463. }
  464. }); // TODO: T15530363 update this when we remove DraftEntity entirely
  465. this.currentEntity = this.entityMap.__create('IMAGE', 'IMMUTABLE', entityConfig); // The child text node cannot just have a space or return as content (since
  466. // we strip those out), unless the image is for presentation only.
  467. // See https://github.com/facebook/draft-js/issues/231 for some context.
  468. if (gkx('draftjs_fix_paste_for_img')) {
  469. if (image.getAttribute('role') !== 'presentation') {
  470. this._appendText("\uD83D\uDCF7", style);
  471. }
  472. } else {
  473. this._appendText("\uD83D\uDCF7", style);
  474. }
  475. this.currentEntity = null;
  476. }
  477. /**
  478. * Add the content of an HTML 'a' node to the internal state. Child nodes
  479. * (if any) are converted to Block Configs and appended to the provided
  480. * blockConfig array.
  481. */
  482. ;
  483. _proto._addAnchorNode = function _addAnchorNode(node, blockConfigs, style) {
  484. // The check has already been made by isValidAnchor but
  485. // we have to do it again to keep flow happy.
  486. if (!isHTMLAnchorElement(node)) {
  487. return;
  488. }
  489. var anchor = node;
  490. var entityConfig = {};
  491. anchorAttr.forEach(function (attr) {
  492. var anchorAttribute = anchor.getAttribute(attr);
  493. if (anchorAttribute) {
  494. entityConfig[attr] = anchorAttribute;
  495. }
  496. });
  497. entityConfig.url = new URI(anchor.href).toString(); // TODO: T15530363 update this when we remove DraftEntity completely
  498. this.currentEntity = this.entityMap.__create('LINK', 'MUTABLE', entityConfig || {});
  499. blockConfigs.push.apply(blockConfigs, this._toBlockConfigs(Array.from(node.childNodes), style));
  500. this.currentEntity = null;
  501. }
  502. /**
  503. * Walk the BlockConfig tree, compute parent/children/siblings,
  504. * and generate the corresponding ContentBlockNode
  505. */
  506. ;
  507. _proto._toContentBlocks = function _toContentBlocks(blockConfigs) {
  508. var parent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
  509. var l = blockConfigs.length - 1;
  510. for (var i = 0; i <= l; i++) {
  511. var config = blockConfigs[i];
  512. config.parent = parent;
  513. config.prevSibling = i > 0 ? blockConfigs[i - 1].key : null;
  514. config.nextSibling = i < l ? blockConfigs[i + 1].key : null;
  515. config.children = List(config.childConfigs.map(function (child) {
  516. return child.key;
  517. }));
  518. this.contentBlocks.push(new ContentBlockNode(_objectSpread({}, config)));
  519. this._toContentBlocks(config.childConfigs, config.key);
  520. }
  521. }
  522. /**
  523. * Remove 'useless' container nodes from the block config hierarchy, by
  524. * replacing them with their children.
  525. */
  526. ;
  527. _proto._hoistContainersInBlockConfigs = function _hoistContainersInBlockConfigs(blockConfigs) {
  528. var _this = this;
  529. var hoisted = List(blockConfigs).flatMap(function (blockConfig) {
  530. // Don't mess with useful blocks
  531. if (blockConfig.type !== 'unstyled' || blockConfig.text !== '') {
  532. return [blockConfig];
  533. }
  534. return _this._hoistContainersInBlockConfigs(blockConfig.childConfigs);
  535. });
  536. return hoisted;
  537. } // ***********************************************************************
  538. // The two methods below are used for backward compatibility when
  539. // experimentalTreeDataSupport is disabled.
  540. /**
  541. * Same as _toContentBlocks but replaces nested blocks by their
  542. * text content.
  543. */
  544. ;
  545. _proto._toFlatContentBlocks = function _toFlatContentBlocks(blockConfigs) {
  546. var _this2 = this;
  547. var cleanConfigs = this._hoistContainersInBlockConfigs(blockConfigs);
  548. cleanConfigs.forEach(function (config) {
  549. var _this2$_extractTextFr = _this2._extractTextFromBlockConfigs(config.childConfigs),
  550. text = _this2$_extractTextFr.text,
  551. characterList = _this2$_extractTextFr.characterList;
  552. _this2.contentBlocks.push(new ContentBlock(_objectSpread({}, config, {
  553. text: config.text + text,
  554. characterList: config.characterList.concat(characterList)
  555. })));
  556. });
  557. }
  558. /**
  559. * Extract the text and the associated inline styles form an
  560. * array of content block configs.
  561. */
  562. ;
  563. _proto._extractTextFromBlockConfigs = function _extractTextFromBlockConfigs(blockConfigs) {
  564. var l = blockConfigs.length - 1;
  565. var text = '';
  566. var characterList = List();
  567. for (var i = 0; i <= l; i++) {
  568. var config = blockConfigs[i];
  569. text += config.text;
  570. characterList = characterList.concat(config.characterList);
  571. if (text !== '' && config.type !== 'unstyled') {
  572. text += '\n';
  573. characterList = characterList.push(characterList.last());
  574. }
  575. var children = this._extractTextFromBlockConfigs(config.childConfigs);
  576. text += children.text;
  577. characterList = characterList.concat(children.characterList);
  578. }
  579. return {
  580. text: text,
  581. characterList: characterList
  582. };
  583. };
  584. return ContentBlocksBuilder;
  585. }();
  586. /**
  587. * Converts an HTML string to an array of ContentBlocks and an EntityMap
  588. * suitable to initialize the internal state of a Draftjs component.
  589. */
  590. var convertFromHTMLToContentBlocks = function convertFromHTMLToContentBlocks(html) {
  591. var DOMBuilder = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : getSafeBodyFromHTML;
  592. var blockRenderMap = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : DefaultDraftBlockRenderMap;
  593. // Be ABSOLUTELY SURE that the dom builder you pass here won't execute
  594. // arbitrary code in whatever environment you're running this in. For an
  595. // example of how we try to do this in-browser, see getSafeBodyFromHTML.
  596. // Remove funky characters from the HTML string
  597. html = html.trim().replace(REGEX_CR, '').replace(REGEX_NBSP, SPACE).replace(REGEX_CARRIAGE, '').replace(REGEX_ZWS, ''); // Build a DOM tree out of the HTML string
  598. var safeBody = DOMBuilder(html);
  599. if (!safeBody) {
  600. return null;
  601. } // Build a BlockTypeMap out of the BlockRenderMap
  602. var blockTypeMap = buildBlockTypeMap(blockRenderMap); // Select the proper block type for the cases where the blockRenderMap
  603. // uses multiple block types for the same html tag.
  604. var disambiguate = function disambiguate(tag, wrapper) {
  605. if (tag === 'li') {
  606. return wrapper === 'ol' ? 'ordered-list-item' : 'unordered-list-item';
  607. }
  608. return null;
  609. };
  610. return new ContentBlocksBuilder(blockTypeMap, disambiguate).addDOMNode(safeBody).getContentBlocks();
  611. };
  612. module.exports = convertFromHTMLToContentBlocks;