@20minutes/draft-convert 3.0.3 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +2 -3
  2. package/esm/blockEntities.js +62 -65
  3. package/esm/blockInlineStyles.js +69 -92
  4. package/esm/convertFromHTML.js +447 -481
  5. package/esm/convertToHTML.js +81 -102
  6. package/esm/default/defaultBlockHTML.js +33 -30
  7. package/esm/default/defaultInlineHTML.js +16 -16
  8. package/esm/encodeBlock.js +38 -42
  9. package/esm/index.js +4 -4
  10. package/esm/util/accumulateFunction.js +7 -9
  11. package/esm/util/blockTypeObjectFunction.js +7 -9
  12. package/esm/util/getBlockTags.js +20 -20
  13. package/esm/util/getElementHTML.js +25 -29
  14. package/esm/util/getElementTagLength.js +15 -17
  15. package/esm/util/getNestedBlockTags.js +21 -26
  16. package/esm/util/parseHTML.js +15 -15
  17. package/esm/util/rangeSort.js +6 -6
  18. package/esm/util/splitReactElement.js +27 -13
  19. package/esm/util/styleObjectFunction.js +6 -8
  20. package/esm/util/updateMutation.js +57 -47
  21. package/lib/blockEntities.js +74 -69
  22. package/lib/blockInlineStyles.js +81 -96
  23. package/lib/convertFromHTML.js +502 -487
  24. package/lib/convertToHTML.js +134 -106
  25. package/lib/default/defaultBlockHTML.js +47 -35
  26. package/lib/default/defaultInlineHTML.js +29 -21
  27. package/lib/encodeBlock.js +50 -46
  28. package/lib/index.js +25 -23
  29. package/lib/util/accumulateFunction.js +13 -11
  30. package/lib/util/blockTypeObjectFunction.js +13 -11
  31. package/lib/util/getBlockTags.js +35 -27
  32. package/lib/util/getElementHTML.js +40 -36
  33. package/lib/util/getElementTagLength.js +28 -22
  34. package/lib/util/getNestedBlockTags.js +35 -32
  35. package/lib/util/parseHTML.js +22 -18
  36. package/lib/util/rangeSort.js +13 -9
  37. package/lib/util/splitReactElement.js +42 -19
  38. package/lib/util/styleObjectFunction.js +12 -10
  39. package/lib/util/updateMutation.js +64 -51
  40. package/package.json +25 -28
  41. package/dist/draft-convert.js +0 -440
  42. package/dist/draft-convert.min.js +0 -1
@@ -8,530 +8,496 @@
8
8
  * This source code is licensed under the BSD-style license found in the
9
9
  * LICENSE file in the /src directory of this source tree. An additional grant
10
10
  * of patent rights can be found in the PATENTS file in the same directory.
11
- */
12
-
13
- import { List, Map, OrderedSet } from 'immutable';
14
- import { BlockMapBuilder, CharacterMetadata, ContentBlock, ContentState, Entity, SelectionState, genKey } from 'draft-js';
15
- import getSafeBodyFromHTML from './util/parseHTML';
16
- import rangeSort from './util/rangeSort';
17
- var SPACE = ' ';
18
-
11
+ */ import * as Immutable from 'immutable';
12
+ import * as DraftJS from 'draft-js';
13
+ import getSafeBodyFromHTML from './util/parseHTML.js';
14
+ import rangeSort from './util/rangeSort.js';
15
+ const { List, Map, OrderedSet } = Immutable;
16
+ const { BlockMapBuilder, CharacterMetadata, ContentBlock, ContentState, Entity, SelectionState, genKey } = DraftJS;
17
+ const SPACE = ' ';
19
18
  // Arbitrary max indent
20
- var MAX_DEPTH = 4;
21
-
19
+ const MAX_DEPTH = 4;
22
20
  // used for replacing characters in HTML
23
- var REGEX_CR = /\r/g;
24
- var REGEX_LF = /\n/g;
25
- var REGEX_NBSP = / /g;
26
- var REGEX_BLOCK_DELIMITER = /\r/g;
27
-
21
+ const REGEX_CR = /\r/g;
22
+ const REGEX_LF = /\n/g;
23
+ const REGEX_NBSP = / /g;
24
+ const REGEX_BLOCK_DELIMITER = /\r/g;
28
25
  // Block tag flow is different because LIs do not have
29
26
  // a deterministic style ;_;
30
- var blockTags = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'blockquote', 'pre'];
31
- var inlineTags = {
32
- b: 'BOLD',
33
- code: 'CODE',
34
- del: 'STRIKETHROUGH',
35
- em: 'ITALIC',
36
- i: 'ITALIC',
37
- s: 'STRIKETHROUGH',
38
- strike: 'STRIKETHROUGH',
39
- strong: 'BOLD',
40
- u: 'UNDERLINE'
27
+ const blockTags = [
28
+ 'p',
29
+ 'h1',
30
+ 'h2',
31
+ 'h3',
32
+ 'h4',
33
+ 'h5',
34
+ 'h6',
35
+ 'li',
36
+ 'blockquote',
37
+ 'pre'
38
+ ];
39
+ const inlineTags = {
40
+ b: 'BOLD',
41
+ code: 'CODE',
42
+ del: 'STRIKETHROUGH',
43
+ em: 'ITALIC',
44
+ i: 'ITALIC',
45
+ s: 'STRIKETHROUGH',
46
+ strike: 'STRIKETHROUGH',
47
+ strong: 'BOLD',
48
+ u: 'UNDERLINE'
41
49
  };
42
- var handleMiddleware = function handleMiddleware(maybeMiddleware, base) {
43
- if (maybeMiddleware && maybeMiddleware.__isMiddleware === true) {
44
- return maybeMiddleware(base);
45
- }
46
- return maybeMiddleware;
47
- };
48
- var defaultHTMLToBlock = function defaultHTMLToBlock(nodeName, node, lastList) {
49
- return undefined;
50
- };
51
- var defaultHTMLToStyle = function defaultHTMLToStyle(nodeName, node, currentStyle) {
52
- return currentStyle;
53
- };
54
- var defaultHTMLToEntity = function defaultHTMLToEntity(nodeName, node) {
55
- return undefined;
56
- };
57
- var defaultTextToEntity = function defaultTextToEntity(text) {
58
- return [];
59
- };
60
- var nullthrows = function nullthrows(x) {
61
- if (x != null) {
62
- return x;
63
- }
64
- throw new Error('Got unexpected null or undefined');
50
+ const handleMiddleware = (maybeMiddleware, base)=>{
51
+ if (maybeMiddleware && maybeMiddleware.__isMiddleware === true) {
52
+ return maybeMiddleware(base);
53
+ }
54
+ return maybeMiddleware;
65
55
  };
66
- var sanitizeDraftText = function sanitizeDraftText(input) {
67
- return input.replace(REGEX_BLOCK_DELIMITER, '');
56
+ const defaultHTMLToBlock = (nodeName, node, lastList)=>undefined;
57
+ const defaultHTMLToStyle = (nodeName, node, currentStyle)=>currentStyle;
58
+ const defaultHTMLToEntity = (nodeName, node)=>undefined;
59
+ const defaultTextToEntity = (text)=>[];
60
+ const nullthrows = (x)=>{
61
+ if (x != null) {
62
+ return x;
63
+ }
64
+ throw new Error('Got unexpected null or undefined');
68
65
  };
66
+ const sanitizeDraftText = (input)=>input.replace(REGEX_BLOCK_DELIMITER, '');
69
67
  function getEmptyChunk() {
70
- return {
71
- text: '',
72
- inlines: [],
73
- entities: [],
74
- blocks: []
75
- };
68
+ return {
69
+ text: '',
70
+ inlines: [],
71
+ entities: [],
72
+ blocks: []
73
+ };
76
74
  }
77
75
  function getWhitespaceChunk(inEntity) {
78
- var entities = new Array(1);
79
- if (inEntity) {
80
- entities[0] = inEntity;
81
- }
82
- return {
83
- text: SPACE,
84
- inlines: [OrderedSet()],
85
- entities: entities,
86
- blocks: []
87
- };
76
+ const entities = new Array(1);
77
+ if (inEntity) {
78
+ entities[0] = inEntity;
79
+ }
80
+ return {
81
+ text: SPACE,
82
+ inlines: [
83
+ OrderedSet()
84
+ ],
85
+ entities,
86
+ blocks: []
87
+ };
88
88
  }
89
- function getSoftNewlineChunk(block, depth) {
90
- var flat = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
91
- var data = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : Map();
92
- if (flat === true) {
89
+ function getSoftNewlineChunk(block, depth, flat = false, data = Map()) {
90
+ if (flat === true) {
91
+ return {
92
+ text: '\r',
93
+ inlines: [
94
+ OrderedSet()
95
+ ],
96
+ entities: new Array(1),
97
+ blocks: [
98
+ {
99
+ type: block,
100
+ data,
101
+ depth: Math.max(0, Math.min(MAX_DEPTH, depth))
102
+ }
103
+ ],
104
+ isNewline: true
105
+ };
106
+ }
93
107
  return {
94
- text: '\r',
95
- inlines: [OrderedSet()],
96
- entities: new Array(1),
97
- blocks: [{
98
- type: block,
99
- data: data,
100
- depth: Math.max(0, Math.min(MAX_DEPTH, depth))
101
- }],
102
- isNewline: true
108
+ text: '\n',
109
+ inlines: [
110
+ OrderedSet()
111
+ ],
112
+ entities: new Array(1),
113
+ blocks: []
103
114
  };
104
- }
105
- return {
106
- text: '\n',
107
- inlines: [OrderedSet()],
108
- entities: new Array(1),
109
- blocks: []
110
- };
111
115
  }
112
- function getBlockDividerChunk(block, depth) {
113
- var data = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : Map();
114
- return {
115
- text: '\r',
116
- inlines: [OrderedSet()],
117
- entities: new Array(1),
118
- blocks: [{
119
- type: block,
120
- data: data,
121
- depth: Math.max(0, Math.min(MAX_DEPTH, depth))
122
- }]
123
- };
116
+ function getBlockDividerChunk(block, depth, data = Map()) {
117
+ return {
118
+ text: '\r',
119
+ inlines: [
120
+ OrderedSet()
121
+ ],
122
+ entities: new Array(1),
123
+ blocks: [
124
+ {
125
+ type: block,
126
+ data,
127
+ depth: Math.max(0, Math.min(MAX_DEPTH, depth))
128
+ }
129
+ ]
130
+ };
124
131
  }
125
132
  function getBlockTypeForTag(tag, lastList) {
126
- switch (tag) {
127
- case 'h1':
128
- return 'header-one';
129
- case 'h2':
130
- return 'header-two';
131
- case 'h3':
132
- return 'header-three';
133
- case 'h4':
134
- return 'header-four';
135
- case 'h5':
136
- return 'header-five';
137
- case 'h6':
138
- return 'header-six';
139
- case 'li':
140
- if (lastList === 'ol') {
141
- return 'ordered-list-item';
142
- }
143
- return 'unordered-list-item';
144
- case 'blockquote':
145
- return 'blockquote';
146
- case 'pre':
147
- return 'code-block';
148
- case 'div':
149
- case 'p':
150
- return 'unstyled';
151
- default:
152
- return null;
153
- }
133
+ switch(tag){
134
+ case 'h1':
135
+ return 'header-one';
136
+ case 'h2':
137
+ return 'header-two';
138
+ case 'h3':
139
+ return 'header-three';
140
+ case 'h4':
141
+ return 'header-four';
142
+ case 'h5':
143
+ return 'header-five';
144
+ case 'h6':
145
+ return 'header-six';
146
+ case 'li':
147
+ if (lastList === 'ol') {
148
+ return 'ordered-list-item';
149
+ }
150
+ return 'unordered-list-item';
151
+ case 'blockquote':
152
+ return 'blockquote';
153
+ case 'pre':
154
+ return 'code-block';
155
+ case 'div':
156
+ case 'p':
157
+ return 'unstyled';
158
+ default:
159
+ return null;
160
+ }
154
161
  }
155
162
  function baseCheckBlockType(nodeName, node, lastList) {
156
- return getBlockTypeForTag(nodeName, lastList);
163
+ return getBlockTypeForTag(nodeName, lastList);
157
164
  }
158
165
  function processInlineTag(tag, node, currentStyle) {
159
- var styleToCheck = inlineTags[tag];
160
- if (styleToCheck) {
161
- currentStyle = currentStyle.add(styleToCheck).toOrderedSet();
162
- } else if (node instanceof HTMLElement) {
163
- var htmlElement = node;
164
- currentStyle = currentStyle.withMutations(function (style) {
165
- if (htmlElement.style.fontWeight === 'bold') {
166
- style.add('BOLD');
167
- }
168
- if (htmlElement.style.fontStyle === 'italic') {
169
- style.add('ITALIC');
170
- }
171
- if (htmlElement.style.textDecoration === 'underline') {
172
- style.add('UNDERLINE');
173
- }
174
- if (htmlElement.style.textDecoration === 'line-through') {
175
- style.add('STRIKETHROUGH');
176
- }
177
- }).toOrderedSet();
178
- }
179
- return currentStyle;
166
+ const styleToCheck = inlineTags[tag];
167
+ if (styleToCheck) {
168
+ currentStyle = currentStyle.add(styleToCheck).toOrderedSet();
169
+ } else if (node instanceof HTMLElement) {
170
+ const htmlElement = node;
171
+ currentStyle = currentStyle.withMutations((style)=>{
172
+ if (htmlElement.style.fontWeight === 'bold') {
173
+ style.add('BOLD');
174
+ }
175
+ if (htmlElement.style.fontStyle === 'italic') {
176
+ style.add('ITALIC');
177
+ }
178
+ if (htmlElement.style.textDecoration === 'underline') {
179
+ style.add('UNDERLINE');
180
+ }
181
+ if (htmlElement.style.textDecoration === 'line-through') {
182
+ style.add('STRIKETHROUGH');
183
+ }
184
+ }).toOrderedSet();
185
+ }
186
+ return currentStyle;
180
187
  }
181
- function baseProcessInlineTag(tag, node) {
182
- var inlineStyles = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : OrderedSet();
183
- return processInlineTag(tag, node, inlineStyles);
188
+ function baseProcessInlineTag(tag, node, inlineStyles = OrderedSet()) {
189
+ return processInlineTag(tag, node, inlineStyles);
184
190
  }
185
- function joinChunks(A, B) {
186
- var flat = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
187
- // Sometimes two blocks will touch in the DOM and we need to strip the
188
- // extra delimiter to preserve niceness.
189
- var firstInB = B.text.slice(0, 1);
190
- var lastInA = A.text.slice(-1);
191
- var adjacentDividers = lastInA === '\r' && firstInB === '\r';
192
- var isJoiningBlocks = A.text !== '\r' && B.text !== '\r'; // when joining two full blocks like this we want to pop one divider
193
- var addingNewlineToEmptyBlock = A.text === '\r' && !A.isNewline && B.isNewline; // when joining a newline to an empty block we want to remove the newline
194
-
195
- if (adjacentDividers && (isJoiningBlocks || addingNewlineToEmptyBlock)) {
196
- A.text = A.text.slice(0, -1);
197
- A.inlines.pop();
198
- A.entities.pop();
199
- A.blocks.pop();
200
- }
201
-
202
- // Kill whitespace after blocks if flat mode is on
203
- if (A.text.slice(-1) === '\r' && flat === true) {
204
- if (B.text === SPACE || B.text === '\n') {
205
- return A;
191
+ function joinChunks(A, B, flat = false) {
192
+ // Sometimes two blocks will touch in the DOM and we need to strip the
193
+ // extra delimiter to preserve niceness.
194
+ const firstInB = B.text.slice(0, 1);
195
+ const lastInA = A.text.slice(-1);
196
+ const adjacentDividers = lastInA === '\r' && firstInB === '\r';
197
+ const isJoiningBlocks = A.text !== '\r' && B.text !== '\r' // when joining two full blocks like this we want to pop one divider
198
+ ;
199
+ const addingNewlineToEmptyBlock = A.text === '\r' && !A.isNewline && B.isNewline // when joining a newline to an empty block we want to remove the newline
200
+ ;
201
+ if (adjacentDividers && (isJoiningBlocks || addingNewlineToEmptyBlock)) {
202
+ A.text = A.text.slice(0, -1);
203
+ A.inlines.pop();
204
+ A.entities.pop();
205
+ A.blocks.pop();
206
206
  }
207
- if (firstInB === SPACE || firstInB === '\n') {
208
- B.text = B.text.slice(1);
209
- B.inlines.shift();
210
- B.entities.shift();
207
+ // Kill whitespace after blocks if flat mode is on
208
+ if (A.text.slice(-1) === '\r' && flat === true) {
209
+ if (B.text === SPACE || B.text === '\n') {
210
+ return A;
211
+ }
212
+ if (firstInB === SPACE || firstInB === '\n') {
213
+ B.text = B.text.slice(1);
214
+ B.inlines.shift();
215
+ B.entities.shift();
216
+ }
211
217
  }
212
- }
213
- var isNewline = A.text.length === 0 && B.isNewline;
214
- return {
215
- text: A.text + B.text,
216
- inlines: A.inlines.concat(B.inlines),
217
- entities: A.entities.concat(B.entities),
218
- blocks: A.blocks.concat(B.blocks),
219
- isNewline: isNewline
220
- };
218
+ const isNewline = A.text.length === 0 && B.isNewline;
219
+ return {
220
+ text: A.text + B.text,
221
+ inlines: A.inlines.concat(B.inlines),
222
+ entities: A.entities.concat(B.entities),
223
+ blocks: A.blocks.concat(B.blocks),
224
+ isNewline
225
+ };
221
226
  }
222
-
223
227
  /*
224
228
  * Check to see if we have anything like <p> <blockquote> <h1>... to create
225
229
  * block tags from. If we do, we can use those and ignore <div> tags. If we
226
230
  * don't, we can treat <div> tags as meaningful (unstyled) blocks.
227
- */
228
- function containsSemanticBlockMarkup(html) {
229
- return blockTags.some(function (tag) {
230
- return html.indexOf("<".concat(tag)) !== -1;
231
- });
231
+ */ function containsSemanticBlockMarkup(html) {
232
+ return blockTags.some((tag)=>html.indexOf(`<${tag}`) !== -1);
232
233
  }
233
234
  function genFragment(node, inlineStyle, lastList, inBlock, fragmentBlockTags, depth, processCustomInlineStyles, checkEntityNode, checkEntityText, checkBlockType, createEntity, getEntity, mergeEntityData, replaceEntityData, options, inEntity) {
234
- var nodeName = node.nodeName.toLowerCase();
235
- var newBlock = false;
236
- var nextBlockType = 'unstyled';
237
-
238
- // Base Case
239
- if (nodeName === '#text') {
240
- var text = node.textContent;
241
- if (text.trim() === '' && inBlock === null) {
242
- return getEmptyChunk();
235
+ let nodeName = node.nodeName.toLowerCase();
236
+ let newBlock = false;
237
+ let nextBlockType = 'unstyled';
238
+ // Base Case
239
+ if (nodeName === '#text') {
240
+ let text = node.textContent;
241
+ if (text.trim() === '' && inBlock === null) {
242
+ return getEmptyChunk();
243
+ }
244
+ if (text.trim() === '' && inBlock !== 'code-block') {
245
+ return getWhitespaceChunk(inEntity);
246
+ }
247
+ if (inBlock !== 'code-block') {
248
+ // Can't use empty string because MSWord
249
+ text = text.replace(REGEX_LF, SPACE);
250
+ }
251
+ const entities = Array(text.length).fill(inEntity);
252
+ let offsetChange = 0;
253
+ const textEntities = checkEntityText(text, createEntity, getEntity, mergeEntityData, replaceEntityData).sort(rangeSort);
254
+ textEntities.forEach(({ entity, offset, length, result })=>{
255
+ const adjustedOffset = offset + offsetChange;
256
+ if (result === null || result === undefined) {
257
+ result = text.substr(adjustedOffset, length);
258
+ }
259
+ const textArray = text.split('');
260
+ textArray.splice.bind(textArray, adjustedOffset, length).apply(textArray, result.split(''));
261
+ text = textArray.join('');
262
+ entities.splice.bind(entities, adjustedOffset, length).apply(entities, Array(result.length).fill(entity));
263
+ offsetChange += result.length - length;
264
+ });
265
+ return {
266
+ text,
267
+ inlines: Array(text.length).fill(inlineStyle),
268
+ entities,
269
+ blocks: []
270
+ };
243
271
  }
244
- if (text.trim() === '' && inBlock !== 'code-block') {
245
- return getWhitespaceChunk(inEntity);
272
+ // BR tags
273
+ if (nodeName === 'br') {
274
+ const blockType = inBlock;
275
+ if (blockType === null) {
276
+ // BR tag is at top level, treat it as an unstyled block
277
+ return getSoftNewlineChunk('unstyled', depth, true);
278
+ }
279
+ return getSoftNewlineChunk(blockType || 'unstyled', depth, options.flat);
246
280
  }
247
- if (inBlock !== 'code-block') {
248
- // Can't use empty string because MSWord
249
- text = text.replace(REGEX_LF, SPACE);
281
+ let chunk = getEmptyChunk();
282
+ let newChunk = null;
283
+ // Inline tags
284
+ inlineStyle = processInlineTag(nodeName, node, inlineStyle);
285
+ inlineStyle = processCustomInlineStyles(nodeName, node, inlineStyle);
286
+ // Handle lists
287
+ if (nodeName === 'ul' || nodeName === 'ol') {
288
+ if (lastList) {
289
+ depth += 1;
290
+ }
291
+ lastList = nodeName;
292
+ inBlock = null;
250
293
  }
251
- var entities = Array(text.length).fill(inEntity);
252
- var offsetChange = 0;
253
- var textEntities = checkEntityText(text, createEntity, getEntity, mergeEntityData, replaceEntityData).sort(rangeSort);
254
- textEntities.forEach(function (_ref) {
255
- var entity = _ref.entity,
256
- offset = _ref.offset,
257
- length = _ref.length,
258
- result = _ref.result;
259
- var adjustedOffset = offset + offsetChange;
260
- if (result === null || result === undefined) {
261
- result = text.substr(adjustedOffset, length);
262
- }
263
- var textArray = text.split('');
264
- textArray.splice.bind(textArray, adjustedOffset, length).apply(textArray, result.split(''));
265
- text = textArray.join('');
266
- entities.splice.bind(entities, adjustedOffset, length).apply(entities, Array(result.length).fill(entity));
267
- offsetChange += result.length - length;
268
- });
269
- return {
270
- text: text,
271
- inlines: Array(text.length).fill(inlineStyle),
272
- entities: entities,
273
- blocks: []
274
- };
275
- }
276
-
277
- // BR tags
278
- if (nodeName === 'br') {
279
- var _blockType = inBlock;
280
- if (_blockType === null) {
281
- // BR tag is at top level, treat it as an unstyled block
282
- return getSoftNewlineChunk('unstyled', depth, true);
294
+ // Block Tags
295
+ let blockInfo = checkBlockType(nodeName, node, lastList, inBlock);
296
+ let blockType;
297
+ let blockDataMap;
298
+ if (blockInfo === false) {
299
+ return getEmptyChunk();
300
+ }
301
+ blockInfo = blockInfo || {};
302
+ if (typeof blockInfo === 'string') {
303
+ blockType = blockInfo;
304
+ blockDataMap = Map();
305
+ } else {
306
+ blockType = typeof blockInfo === 'string' ? blockInfo : blockInfo.type;
307
+ blockDataMap = blockInfo.data ? Map(blockInfo.data) : Map();
308
+ }
309
+ if (!inBlock && (fragmentBlockTags.indexOf(nodeName) !== -1 || blockType)) {
310
+ chunk = getBlockDividerChunk(blockType || getBlockTypeForTag(nodeName, lastList), depth, blockDataMap);
311
+ inBlock = blockType || getBlockTypeForTag(nodeName, lastList);
312
+ newBlock = true;
313
+ } else if (lastList && (inBlock === 'ordered-list-item' || inBlock === 'unordered-list-item') && nodeName === 'li') {
314
+ const listItemBlockType = getBlockTypeForTag(nodeName, lastList);
315
+ chunk = getBlockDividerChunk(listItemBlockType, depth);
316
+ inBlock = listItemBlockType;
317
+ newBlock = true;
318
+ nextBlockType = lastList === 'ul' ? 'unordered-list-item' : 'ordered-list-item';
319
+ } else if (inBlock && inBlock !== 'atomic' && blockType === 'atomic') {
320
+ inBlock = blockType;
321
+ newBlock = true;
322
+ chunk = getSoftNewlineChunk(blockType, depth, true, blockDataMap);
283
323
  }
284
- return getSoftNewlineChunk(_blockType || 'unstyled', depth, options.flat);
285
- }
286
- var chunk = getEmptyChunk();
287
- var newChunk = null;
288
-
289
- // Inline tags
290
- inlineStyle = processInlineTag(nodeName, node, inlineStyle);
291
- inlineStyle = processCustomInlineStyles(nodeName, node, inlineStyle);
292
-
293
- // Handle lists
294
- if (nodeName === 'ul' || nodeName === 'ol') {
295
- if (lastList) {
296
- depth += 1;
324
+ // Recurse through children
325
+ let child = node.firstChild;
326
+ // hack to allow conversion of atomic blocks from HTML (e.g. <figure><img
327
+ // src="..." /></figure>). since metadata must be stored on an entity text
328
+ // must exist for the entity to apply to. the way chunks are joined strips
329
+ // whitespace at the end so it cannot be a space character.
330
+ if (child == null && inEntity && (blockType === 'atomic' || inBlock === 'atomic')) {
331
+ child = document.createTextNode('a');
297
332
  }
298
- lastList = nodeName;
299
- inBlock = null;
300
- }
301
-
302
- // Block Tags
303
- var blockInfo = checkBlockType(nodeName, node, lastList, inBlock);
304
- var blockType;
305
- var blockDataMap;
306
- if (blockInfo === false) {
307
- return getEmptyChunk();
308
- }
309
- blockInfo = blockInfo || {};
310
- if (typeof blockInfo === 'string') {
311
- blockType = blockInfo;
312
- blockDataMap = Map();
313
- } else {
314
- blockType = typeof blockInfo === 'string' ? blockInfo : blockInfo.type;
315
- blockDataMap = blockInfo.data ? Map(blockInfo.data) : Map();
316
- }
317
- if (!inBlock && (fragmentBlockTags.indexOf(nodeName) !== -1 || blockType)) {
318
- chunk = getBlockDividerChunk(blockType || getBlockTypeForTag(nodeName, lastList), depth, blockDataMap);
319
- inBlock = blockType || getBlockTypeForTag(nodeName, lastList);
320
- newBlock = true;
321
- } else if (lastList && (inBlock === 'ordered-list-item' || inBlock === 'unordered-list-item') && nodeName === 'li') {
322
- var listItemBlockType = getBlockTypeForTag(nodeName, lastList);
323
- chunk = getBlockDividerChunk(listItemBlockType, depth);
324
- inBlock = listItemBlockType;
325
- newBlock = true;
326
- nextBlockType = lastList === 'ul' ? 'unordered-list-item' : 'ordered-list-item';
327
- } else if (inBlock && inBlock !== 'atomic' && blockType === 'atomic') {
328
- inBlock = blockType;
329
- newBlock = true;
330
- chunk = getSoftNewlineChunk(blockType, depth, true,
331
- // atomic blocks within non-atomic blocks must always be split out
332
- blockDataMap);
333
- }
334
-
335
- // Recurse through children
336
- var child = node.firstChild;
337
-
338
- // hack to allow conversion of atomic blocks from HTML (e.g. <figure><img
339
- // src="..." /></figure>). since metadata must be stored on an entity text
340
- // must exist for the entity to apply to. the way chunks are joined strips
341
- // whitespace at the end so it cannot be a space character.
342
-
343
- if (child == null && inEntity && (blockType === 'atomic' || inBlock === 'atomic')) {
344
- child = document.createTextNode('a');
345
- }
346
- if (child != null) {
347
- nodeName = child.nodeName.toLowerCase();
348
- }
349
- var entityId = null;
350
- while (child) {
351
- entityId = checkEntityNode(nodeName, child, createEntity, getEntity, mergeEntityData, replaceEntityData);
352
- newChunk = genFragment(child, inlineStyle, lastList, inBlock, fragmentBlockTags, depth, processCustomInlineStyles, checkEntityNode, checkEntityText, checkBlockType, createEntity, getEntity, mergeEntityData, replaceEntityData, options, entityId || inEntity);
353
- chunk = joinChunks(chunk, newChunk, options.flat);
354
- var sibling = child.nextSibling;
355
-
356
- // Put in a newline to break up blocks inside blocks
357
- if (sibling && fragmentBlockTags.indexOf(nodeName) >= 0 && inBlock) {
358
- var newBlockInfo = checkBlockType(nodeName, child, lastList, inBlock);
359
- var newBlockType = void 0;
360
- var newBlockData = void 0;
361
- if (newBlockInfo !== false) {
362
- newBlockInfo = newBlockInfo || {};
363
- if (typeof newBlockInfo === 'string') {
364
- newBlockType = newBlockInfo;
365
- newBlockData = Map();
366
- } else {
367
- newBlockType = newBlockInfo.type || getBlockTypeForTag(nodeName, lastList);
368
- newBlockData = newBlockInfo.data ? Map(newBlockInfo.data) : Map();
333
+ if (child != null) {
334
+ nodeName = child.nodeName.toLowerCase();
335
+ }
336
+ let entityId = null;
337
+ while(child){
338
+ entityId = checkEntityNode(nodeName, child, createEntity, getEntity, mergeEntityData, replaceEntityData);
339
+ newChunk = genFragment(child, inlineStyle, lastList, inBlock, fragmentBlockTags, depth, processCustomInlineStyles, checkEntityNode, checkEntityText, checkBlockType, createEntity, getEntity, mergeEntityData, replaceEntityData, options, entityId || inEntity);
340
+ chunk = joinChunks(chunk, newChunk, options.flat);
341
+ const sibling = child.nextSibling;
342
+ // Put in a newline to break up blocks inside blocks
343
+ if (sibling && fragmentBlockTags.indexOf(nodeName) >= 0 && inBlock) {
344
+ let newBlockInfo = checkBlockType(nodeName, child, lastList, inBlock);
345
+ let newBlockType;
346
+ let newBlockData;
347
+ if (newBlockInfo !== false) {
348
+ newBlockInfo = newBlockInfo || {};
349
+ if (typeof newBlockInfo === 'string') {
350
+ newBlockType = newBlockInfo;
351
+ newBlockData = Map();
352
+ } else {
353
+ newBlockType = newBlockInfo.type || getBlockTypeForTag(nodeName, lastList);
354
+ newBlockData = newBlockInfo.data ? Map(newBlockInfo.data) : Map();
355
+ }
356
+ chunk = joinChunks(chunk, getSoftNewlineChunk(newBlockType, depth, options.flat, newBlockData), options.flat);
357
+ }
358
+ }
359
+ if (sibling) {
360
+ nodeName = sibling.nodeName.toLowerCase();
369
361
  }
370
- chunk = joinChunks(chunk, getSoftNewlineChunk(newBlockType, depth, options.flat, newBlockData), options.flat);
371
- }
362
+ child = sibling;
372
363
  }
373
- if (sibling) {
374
- nodeName = sibling.nodeName.toLowerCase();
364
+ if (newBlock) {
365
+ chunk = joinChunks(chunk, getBlockDividerChunk(nextBlockType, depth, Map()), options.flat);
375
366
  }
376
- child = sibling;
377
- }
378
- if (newBlock) {
379
- chunk = joinChunks(chunk, getBlockDividerChunk(nextBlockType, depth, Map()), options.flat);
380
- }
381
- return chunk;
367
+ return chunk;
382
368
  }
383
369
  function getChunkForHTML(html, processCustomInlineStyles, checkEntityNode, checkEntityText, checkBlockType, createEntity, getEntity, mergeEntityData, replaceEntityData, options, DOMBuilder) {
384
- html = html.trim().replace(REGEX_CR, '').replace(REGEX_NBSP, SPACE);
385
- var safeBody = DOMBuilder(html);
386
- if (!safeBody) {
387
- return null;
388
- }
389
-
390
- // Sometimes we aren't dealing with content that contains nice semantic
391
- // tags. In this case, use divs to separate everything out into paragraphs
392
- // and hope for the best.
393
- var workingBlocks = containsSemanticBlockMarkup(html) ? blockTags.concat(['div']) : ['div'];
394
-
395
- // Start with -1 block depth to offset the fact that we are passing in a fake
396
- // UL block to sta rt with.
397
- var chunk = genFragment(safeBody, OrderedSet(), 'ul', null, workingBlocks, -1, processCustomInlineStyles, checkEntityNode, checkEntityText, checkBlockType, createEntity, getEntity, mergeEntityData, replaceEntityData, options);
398
-
399
- // join with previous block to prevent weirdness on paste
400
- if (chunk.text.indexOf('\r') === 0) {
401
- chunk = {
402
- text: chunk.text.slice(1),
403
- inlines: chunk.inlines.slice(1),
404
- entities: chunk.entities.slice(1),
405
- blocks: chunk.blocks
406
- };
407
- }
408
-
409
- // Kill block delimiter at the end
410
- if (chunk.text.slice(-1) === '\r') {
411
- chunk.text = chunk.text.slice(0, -1);
412
- chunk.inlines = chunk.inlines.slice(0, -1);
413
- chunk.entities = chunk.entities.slice(0, -1);
414
- chunk.blocks.pop();
415
- }
416
-
417
- // If we saw no block tags, put an unstyled one in
418
- if (chunk.blocks.length === 0) {
419
- chunk.blocks.push({
420
- type: 'unstyled',
421
- data: Map(),
422
- depth: 0
423
- });
424
- }
425
-
426
- // Sometimes we start with text that isn't in a block, which is then
427
- // followed by blocks. Need to fix up the blocks to add in
428
- // an unstyled block for this content
429
- if (chunk.text.split('\r').length === chunk.blocks.length + 1) {
430
- chunk.blocks.unshift({
431
- type: 'unstyled',
432
- data: Map(),
433
- depth: 0
434
- });
435
- }
436
- return chunk;
370
+ html = html.trim().replace(REGEX_CR, '').replace(REGEX_NBSP, SPACE);
371
+ const safeBody = DOMBuilder(html);
372
+ if (!safeBody) {
373
+ return null;
374
+ }
375
+ // Sometimes we aren't dealing with content that contains nice semantic
376
+ // tags. In this case, use divs to separate everything out into paragraphs
377
+ // and hope for the best.
378
+ const workingBlocks = containsSemanticBlockMarkup(html) ? blockTags.concat([
379
+ 'div'
380
+ ]) : [
381
+ 'div'
382
+ ];
383
+ // Start with -1 block depth to offset the fact that we are passing in a fake
384
+ // UL block to sta rt with.
385
+ let chunk = genFragment(safeBody, OrderedSet(), 'ul', null, workingBlocks, -1, processCustomInlineStyles, checkEntityNode, checkEntityText, checkBlockType, createEntity, getEntity, mergeEntityData, replaceEntityData, options);
386
+ // join with previous block to prevent weirdness on paste
387
+ if (chunk.text.indexOf('\r') === 0) {
388
+ chunk = {
389
+ text: chunk.text.slice(1),
390
+ inlines: chunk.inlines.slice(1),
391
+ entities: chunk.entities.slice(1),
392
+ blocks: chunk.blocks
393
+ };
394
+ }
395
+ // Kill block delimiter at the end
396
+ if (chunk.text.slice(-1) === '\r') {
397
+ chunk.text = chunk.text.slice(0, -1);
398
+ chunk.inlines = chunk.inlines.slice(0, -1);
399
+ chunk.entities = chunk.entities.slice(0, -1);
400
+ chunk.blocks.pop();
401
+ }
402
+ // If we saw no block tags, put an unstyled one in
403
+ if (chunk.blocks.length === 0) {
404
+ chunk.blocks.push({
405
+ type: 'unstyled',
406
+ data: Map(),
407
+ depth: 0
408
+ });
409
+ }
410
+ // Sometimes we start with text that isn't in a block, which is then
411
+ // followed by blocks. Need to fix up the blocks to add in
412
+ // an unstyled block for this content
413
+ if (chunk.text.split('\r').length === chunk.blocks.length + 1) {
414
+ chunk.blocks.unshift({
415
+ type: 'unstyled',
416
+ data: Map(),
417
+ depth: 0
418
+ });
419
+ }
420
+ return chunk;
437
421
  }
438
422
  function convertFromHTMLtoContentBlocks(html, processCustomInlineStyles, checkEntityNode, checkEntityText, checkBlockType, createEntity, getEntity, mergeEntityData, replaceEntityData, options, DOMBuilder, generateKey) {
439
- // Be ABSOLUTELY SURE that the dom builder you pass hare won't execute
440
- // arbitrary code in whatever environment you're running this in. For an
441
- // example of how we try to do this in-browser, see getSafeBodyFromHTML.
442
-
443
- var chunk = getChunkForHTML(html, processCustomInlineStyles, checkEntityNode, checkEntityText, checkBlockType, createEntity, getEntity, mergeEntityData, replaceEntityData, options, DOMBuilder, generateKey);
444
- if (chunk == null) {
445
- return [];
446
- }
447
- var start = 0;
448
- return chunk.text.split('\r').map(function (textBlock, blockIndex) {
449
- // Make absolutely certain that our text is acceptable.
450
- textBlock = sanitizeDraftText(textBlock);
451
- var end = start + textBlock.length;
452
- var inlines = nullthrows(chunk).inlines.slice(start, end);
453
- var entities = nullthrows(chunk).entities.slice(start, end);
454
- var characterList = List(inlines.map(function (style, entityIndex) {
455
- var data = {
456
- style: style,
457
- entity: null
458
- };
459
- if (entities[entityIndex]) {
460
- data.entity = entities[entityIndex];
461
- }
462
- return CharacterMetadata.create(data);
463
- }));
464
- start = end + 1;
465
- return new ContentBlock({
466
- key: generateKey(),
467
- type: nullthrows(chunk).blocks[blockIndex].type,
468
- data: nullthrows(chunk).blocks[blockIndex].data,
469
- depth: nullthrows(chunk).blocks[blockIndex].depth,
470
- text: textBlock,
471
- characterList: characterList
423
+ // Be ABSOLUTELY SURE that the dom builder you pass hare won't execute
424
+ // arbitrary code in whatever environment you're running this in. For an
425
+ // example of how we try to do this in-browser, see getSafeBodyFromHTML.
426
+ const chunk = getChunkForHTML(html, processCustomInlineStyles, checkEntityNode, checkEntityText, checkBlockType, createEntity, getEntity, mergeEntityData, replaceEntityData, options, DOMBuilder, generateKey);
427
+ if (chunk == null) {
428
+ return [];
429
+ }
430
+ let start = 0;
431
+ return chunk.text.split('\r').map((textBlock, blockIndex)=>{
432
+ // Make absolutely certain that our text is acceptable.
433
+ textBlock = sanitizeDraftText(textBlock);
434
+ const end = start + textBlock.length;
435
+ const inlines = nullthrows(chunk).inlines.slice(start, end);
436
+ const entities = nullthrows(chunk).entities.slice(start, end);
437
+ const characterList = List(inlines.map((style, entityIndex)=>{
438
+ const data = {
439
+ style,
440
+ entity: null
441
+ };
442
+ if (entities[entityIndex]) {
443
+ data.entity = entities[entityIndex];
444
+ }
445
+ return CharacterMetadata.create(data);
446
+ }));
447
+ start = end + 1;
448
+ return new ContentBlock({
449
+ key: generateKey(),
450
+ type: nullthrows(chunk).blocks[blockIndex].type,
451
+ data: nullthrows(chunk).blocks[blockIndex].data,
452
+ depth: nullthrows(chunk).blocks[blockIndex].depth,
453
+ text: textBlock,
454
+ characterList
455
+ });
472
456
  });
473
- });
474
457
  }
475
- var convertFromHTML = function convertFromHTML(_ref2) {
476
- var _ref2$htmlToStyle = _ref2.htmlToStyle,
477
- htmlToStyle = _ref2$htmlToStyle === void 0 ? defaultHTMLToStyle : _ref2$htmlToStyle,
478
- _ref2$htmlToEntity = _ref2.htmlToEntity,
479
- htmlToEntity = _ref2$htmlToEntity === void 0 ? defaultHTMLToEntity : _ref2$htmlToEntity,
480
- _ref2$textToEntity = _ref2.textToEntity,
481
- textToEntity = _ref2$textToEntity === void 0 ? defaultTextToEntity : _ref2$textToEntity,
482
- _ref2$htmlToBlock = _ref2.htmlToBlock,
483
- htmlToBlock = _ref2$htmlToBlock === void 0 ? defaultHTMLToBlock : _ref2$htmlToBlock;
484
- return function (html) {
485
- var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
486
- flat: false
487
- };
488
- var DOMBuilder = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : getSafeBodyFromHTML;
489
- var generateKey = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : genKey;
490
- var contentState = ContentState.createFromText('');
491
- var createEntityWithContentState = function createEntityWithContentState() {
492
- if (contentState.createEntity) {
493
- var _contentState;
494
- contentState = (_contentState = contentState).createEntity.apply(_contentState, arguments);
495
- return contentState.getLastCreatedEntityKey();
496
- }
497
- return Entity.create.apply(Entity, arguments);
458
+ const convertFromHTML = ({ htmlToStyle = defaultHTMLToStyle, htmlToEntity = defaultHTMLToEntity, textToEntity = defaultTextToEntity, htmlToBlock = defaultHTMLToBlock })=>(html, options = {
459
+ flat: false
460
+ }, DOMBuilder = getSafeBodyFromHTML, generateKey = genKey)=>{
461
+ let contentState = ContentState.createFromText('');
462
+ const createEntityWithContentState = (...args)=>{
463
+ if (contentState.createEntity) {
464
+ contentState = contentState.createEntity(...args);
465
+ return contentState.getLastCreatedEntityKey();
466
+ }
467
+ return Entity.create(...args);
468
+ };
469
+ const getEntityWithContentState = (...args)=>{
470
+ if (contentState.getEntity) {
471
+ return contentState.getEntity(...args);
472
+ }
473
+ return Entity.get(...args);
474
+ };
475
+ const mergeEntityDataWithContentState = (...args)=>{
476
+ if (contentState.mergeEntityData) {
477
+ contentState = contentState.mergeEntityData(...args);
478
+ return;
479
+ }
480
+ Entity.mergeData(...args);
481
+ };
482
+ const replaceEntityDataWithContentState = (...args)=>{
483
+ if (contentState.replaceEntityData) {
484
+ contentState = contentState.replaceEntityData(...args);
485
+ return;
486
+ }
487
+ Entity.replaceData(...args);
488
+ };
489
+ const contentBlocks = convertFromHTMLtoContentBlocks(html, handleMiddleware(htmlToStyle, baseProcessInlineTag), handleMiddleware(htmlToEntity, defaultHTMLToEntity), handleMiddleware(textToEntity, defaultTextToEntity), handleMiddleware(htmlToBlock, baseCheckBlockType), createEntityWithContentState, getEntityWithContentState, mergeEntityDataWithContentState, replaceEntityDataWithContentState, options, DOMBuilder, generateKey);
490
+ const blockMap = BlockMapBuilder.createFromArray(contentBlocks);
491
+ const firstBlockKey = contentBlocks[0].getKey();
492
+ return contentState.merge({
493
+ blockMap,
494
+ selectionBefore: SelectionState.createEmpty(firstBlockKey),
495
+ selectionAfter: SelectionState.createEmpty(firstBlockKey)
496
+ });
498
497
  };
499
- var getEntityWithContentState = function getEntityWithContentState() {
500
- if (contentState.getEntity) {
501
- var _contentState2;
502
- return (_contentState2 = contentState).getEntity.apply(_contentState2, arguments);
503
- }
504
- return Entity.get.apply(Entity, arguments);
505
- };
506
- var mergeEntityDataWithContentState = function mergeEntityDataWithContentState() {
507
- if (contentState.mergeEntityData) {
508
- var _contentState3;
509
- contentState = (_contentState3 = contentState).mergeEntityData.apply(_contentState3, arguments);
510
- return;
511
- }
512
- Entity.mergeData.apply(Entity, arguments);
513
- };
514
- var replaceEntityDataWithContentState = function replaceEntityDataWithContentState() {
515
- if (contentState.replaceEntityData) {
516
- var _contentState4;
517
- contentState = (_contentState4 = contentState).replaceEntityData.apply(_contentState4, arguments);
518
- return;
519
- }
520
- Entity.replaceData.apply(Entity, arguments);
521
- };
522
- var contentBlocks = convertFromHTMLtoContentBlocks(html, handleMiddleware(htmlToStyle, baseProcessInlineTag), handleMiddleware(htmlToEntity, defaultHTMLToEntity), handleMiddleware(textToEntity, defaultTextToEntity), handleMiddleware(htmlToBlock, baseCheckBlockType), createEntityWithContentState, getEntityWithContentState, mergeEntityDataWithContentState, replaceEntityDataWithContentState, options, DOMBuilder, generateKey);
523
- var blockMap = BlockMapBuilder.createFromArray(contentBlocks);
524
- var firstBlockKey = contentBlocks[0].getKey();
525
- return contentState.merge({
526
- blockMap: blockMap,
527
- selectionBefore: SelectionState.createEmpty(firstBlockKey),
528
- selectionAfter: SelectionState.createEmpty(firstBlockKey)
529
- });
530
- };
531
- };
532
- export default (function () {
533
- if (arguments.length >= 1 && typeof (arguments.length <= 0 ? undefined : arguments[0]) === 'string') {
534
- return convertFromHTML({}).apply(void 0, arguments);
535
- }
536
- return convertFromHTML.apply(void 0, arguments);
537
- });
498
+ export default ((...args)=>{
499
+ if (args.length >= 1 && typeof args[0] === 'string') {
500
+ return convertFromHTML({})(...args);
501
+ }
502
+ return convertFromHTML(...args);
503
+ });