Comparing version 0.10.0 to 0.10.1
@@ -7,2 +7,26 @@ # Changelog | ||
## 0.10.1 | ||
### Added | ||
* Support UMD in dist output format (#1090) | ||
* Expose textDirectionality prop | ||
* Expose props disabling auto-correct, auto-complete, auto-capitalize | ||
* Add `editorKey` prop for SSR | ||
* Pass `block` to `customStyleFn` callback | ||
* Added `moveAtomicBlock` to `AtomicBlockUtils` | ||
### Fixed | ||
* Fix some cases of "Failed to execute 'setStart' on 'Range" bug (#1162) | ||
* Fix Chrome text deletion bug (#1155) | ||
* Pass fresh editorState to edit handlers (#1112 and #1113) | ||
* Fix for text insertion bugs in Android 5.1 | ||
* Correctly delete immutable and segmented entity content when at the edge of a | ||
selection | ||
* Fix bug where all text except first letter was dropped in IE11 | ||
* Fix bug where starting new line incorrectly toggled inline style | ||
* Fix 'getRangeClientRects' to work around [webkit selection bounding rect | ||
bug](https://www.youtube.com/watch?v=TpNzVH5jlcU) | ||
## 0.10.0 (Dec. 16, 2016) | ||
@@ -21,3 +45,3 @@ | ||
are deprecating the Entity module in favor of | ||
using contentState. | ||
using contentState. See [the migration guide.](https://draftjs.org/docs/v0-10-api-migration.html#content) | ||
@@ -24,0 +48,0 @@ ### Fixed |
@@ -21,5 +21,7 @@ /** | ||
var EditorState = require('./EditorState'); | ||
var SelectionState = require('./SelectionState'); | ||
var Immutable = require('immutable'); | ||
var generateRandomKey = require('./generateRandomKey'); | ||
var moveBlockInContentState = require('./moveBlockInContentState'); | ||
@@ -67,2 +69,40 @@ var List = Immutable.List; | ||
return EditorState.push(editorState, newContent, 'insert-fragment'); | ||
}, | ||
moveAtomicBlock: function moveAtomicBlock(editorState, atomicBlock, targetRange, insertionMode) { | ||
var contentState = editorState.getCurrentContent(); | ||
var selectionState = editorState.getSelection(); | ||
var withMovedAtomicBlock = void 0; | ||
if (insertionMode === 'before' || insertionMode === 'after') { | ||
var targetBlock = contentState.getBlockForKey(insertionMode === 'before' ? targetRange.getStartKey() : targetRange.getEndKey()); | ||
withMovedAtomicBlock = moveBlockInContentState(contentState, atomicBlock, targetBlock, insertionMode); | ||
} else { | ||
var afterRemoval = DraftModifier.removeRange(contentState, targetRange, 'backward'); | ||
var selectionAfterRemoval = afterRemoval.getSelectionAfter(); | ||
var _targetBlock = afterRemoval.getBlockForKey(selectionAfterRemoval.getFocusKey()); | ||
if (selectionAfterRemoval.getStartOffset() === 0) { | ||
withMovedAtomicBlock = moveBlockInContentState(afterRemoval, atomicBlock, _targetBlock, 'before'); | ||
} else if (selectionAfterRemoval.getEndOffset() === _targetBlock.getLength()) { | ||
withMovedAtomicBlock = moveBlockInContentState(afterRemoval, atomicBlock, _targetBlock, 'after'); | ||
} else { | ||
var afterSplit = DraftModifier.splitBlock(afterRemoval, selectionAfterRemoval); | ||
var selectionAfterSplit = afterSplit.getSelectionAfter(); | ||
var _targetBlock2 = afterSplit.getBlockForKey(selectionAfterSplit.getFocusKey()); | ||
withMovedAtomicBlock = moveBlockInContentState(afterSplit, atomicBlock, _targetBlock2, 'before'); | ||
} | ||
} | ||
var newContent = withMovedAtomicBlock.merge({ | ||
selectionBefore: selectionState, | ||
selectionAfter: withMovedAtomicBlock.getSelectionAfter().set('hasFocus', true) | ||
}); | ||
return EditorState.push(editorState, newContent, 'move-block'); | ||
} | ||
@@ -69,0 +109,0 @@ }; |
@@ -58,3 +58,3 @@ /** | ||
CharacterMetadata.prototype.hasStyle = function hasStyle(style) { | ||
return this.getStyle().has(style); | ||
return this.getStyle().includes(style); | ||
}; | ||
@@ -61,0 +61,0 @@ |
@@ -60,2 +60,3 @@ /** | ||
key = key || generateRandomKey(); | ||
type = type || 'unstyled'; | ||
depth = depth || 0; | ||
@@ -62,0 +63,0 @@ inlineStyleRanges = inlineStyleRanges || []; |
@@ -34,2 +34,3 @@ /** | ||
var DraftEditorPlaceholder = require('./DraftEditorPlaceholder.react'); | ||
var EditorState = require('./EditorState'); | ||
@@ -46,4 +47,6 @@ var React = require('react'); | ||
var getDefaultKeyBinding = require('./getDefaultKeyBinding'); | ||
var getScrollPosition = require('fbjs/lib/getScrollPosition'); | ||
var invariant = require('fbjs/lib/invariant'); | ||
var nullthrows = require('fbjs/lib/nullthrows'); | ||
var getScrollPosition = require('fbjs/lib/getScrollPosition'); | ||
@@ -83,3 +86,3 @@ var isIE = UserAgent.isBrowser('IE'); | ||
_this._dragCount = 0; | ||
_this._editorKey = generateRandomKey(); | ||
_this._editorKey = props.editorKey || generateRandomKey(); | ||
_this._placeholderAccessibilityID = 'placeholder-' + _this._editorKey; | ||
@@ -125,3 +128,3 @@ _this._latestEditorState = props.editorState; | ||
// See `_restoreEditorDOM()`. | ||
_this.state = { containerKey: 0 }; | ||
_this.state = { contentsKey: 0 }; | ||
return _this; | ||
@@ -195,3 +198,2 @@ } | ||
className: cx('DraftEditor/editorContainer'), | ||
key: 'editor' + this.state.containerKey, | ||
ref: 'editorContainer' }, | ||
@@ -208,3 +210,13 @@ React.createElement( | ||
'aria-owns': readOnly ? null : this.props.ariaOwneeID, | ||
className: cx('public/DraftEditor/content'), | ||
autoCapitalize: this.props.autoCapitalize, | ||
autoComplete: this.props.autoComplete, | ||
autoCorrect: this.props.autoCorrect, | ||
className: cx({ | ||
// Chrome's built-in translation feature mutates the DOM in ways | ||
// that Draft doesn't expect (ex: adding <font> tags inside | ||
// DraftEditorLeaf spans) and causes problems. We add notranslate | ||
// here which makes its autotranslation skip over this subtree. | ||
'notranslate': !readOnly, | ||
'public/DraftEditor/content': true | ||
}), | ||
contentEditable: !readOnly, | ||
@@ -245,3 +257,5 @@ 'data-testid': this.props.webDriverTestID, | ||
editorKey: this._editorKey, | ||
editorState: this.props.editorState | ||
editorState: this.props.editorState, | ||
key: 'contents' + this.state.contentsKey, | ||
textDirectionality: this.props.textDirectionality | ||
}) | ||
@@ -313,2 +327,3 @@ ) | ||
!(editorNode instanceof HTMLElement) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'editorNode is not an HTMLElement') : invariant(false) : void 0; | ||
editorNode.focus(); | ||
@@ -331,3 +346,5 @@ if (scrollParent === window) { | ||
DraftEditor.prototype._blur = function _blur() { | ||
ReactDOM.findDOMNode(this.refs.editor).blur(); | ||
var editorNode = ReactDOM.findDOMNode(this.refs.editor); | ||
!(editorNode instanceof HTMLElement) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'editorNode is not an HTMLElement') : invariant(false) : void 0; | ||
editorNode.blur(); | ||
}; | ||
@@ -355,6 +372,7 @@ | ||
* | ||
* Force a complete re-render of the editor based on the current EditorState. | ||
* This is useful when we know we are going to lose control of the DOM | ||
* state (cut command, IME) and we want to make sure that reconciliation | ||
* occurs on a version of the DOM that is synchronized with our EditorState. | ||
* Force a complete re-render of the DraftEditorContents based on the current | ||
* EditorState. This is useful when we know we are going to lose control of | ||
* the DOM state (cut command, IME) and we want to make sure that | ||
* reconciliation occurs on a version of the DOM that is synchronized with | ||
* our EditorState. | ||
*/ | ||
@@ -366,3 +384,3 @@ | ||
this.setState({ containerKey: this.state.containerKey + 1 }, function () { | ||
this.setState({ contentsKey: this.state.contentsKey + 1 }, function () { | ||
_this3._focus(scrollPosition); | ||
@@ -369,0 +387,0 @@ }); |
@@ -26,4 +26,2 @@ /** | ||
var ContentBlock = require('./ContentBlock'); | ||
var ContentState = require('./ContentState'); | ||
var DraftEditorLeaf = require('./DraftEditorLeaf.react'); | ||
@@ -34,3 +32,3 @@ var DraftOffsetKey = require('./DraftOffsetKey'); | ||
var Scroll = require('fbjs/lib/Scroll'); | ||
var SelectionState = require('./SelectionState'); | ||
var Style = require('fbjs/lib/Style'); | ||
@@ -44,2 +42,3 @@ var UnicodeBidi = require('fbjs/lib/UnicodeBidi'); | ||
var getViewportDimensions = require('fbjs/lib/getViewportDimensions'); | ||
var invariant = require('fbjs/lib/invariant'); | ||
var nullthrows = require('fbjs/lib/nullthrows'); | ||
@@ -103,2 +102,3 @@ | ||
} else { | ||
!(blockNode instanceof HTMLElement) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'blockNode is not an HTMLElement') : invariant(false) : void 0; | ||
var blockBottom = blockNode.offsetHeight + blockNode.offsetTop; | ||
@@ -132,3 +132,3 @@ var scrollBottom = scrollParent.offsetHeight + scrollPosition.y; | ||
offsetKey: offsetKey, | ||
blockKey: blockKey, | ||
block: block, | ||
start: start, | ||
@@ -135,0 +135,0 @@ selection: hasSelection ? _this2.props.selection : undefined, |
@@ -125,3 +125,5 @@ /** | ||
var direction = directionMap.get(key); | ||
var _textDirectionality = this.props.textDirectionality; | ||
var direction = _textDirectionality ? _textDirectionality : directionMap.get(key); | ||
var offsetKey = DraftOffsetKey.encode(key, 0, 0); | ||
@@ -128,0 +130,0 @@ var componentProps = { |
@@ -83,4 +83,3 @@ /** | ||
getTextContentFromFiles(files, function (fileText) { | ||
fileText && editor.update(insertTextAtSelection(editorState, nullthrows(dropSelection), // flow wtf | ||
fileText)); | ||
fileText && editor.update(insertTextAtSelection(editorState, dropSelection, fileText)); | ||
}); | ||
@@ -87,0 +86,0 @@ return; |
@@ -24,7 +24,8 @@ /** | ||
var ContentBlock = require('./ContentBlock'); | ||
var DraftEditorTextNode = require('./DraftEditorTextNode.react'); | ||
var React = require('react'); | ||
var ReactDOM = require('react-dom'); | ||
var SelectionState = require('./SelectionState'); | ||
var invariant = require('fbjs/lib/invariant'); | ||
var setDraftEditorSelection = require('./setDraftEditorSelection'); | ||
@@ -56,3 +57,3 @@ | ||
* Note that this depends on our maintaining tight control over the | ||
* DOM structure of the TextEditor component. If leaves had multiple | ||
* DOM structure of the DraftEditor component. If leaves had multiple | ||
* text nodes, this would be harder. | ||
@@ -70,6 +71,7 @@ */ | ||
var _props = this.props; | ||
var blockKey = _props.blockKey; | ||
var block = _props.block; | ||
var start = _props.start; | ||
var text = _props.text; | ||
var blockKey = block.getKey(); | ||
var end = start + text.length; | ||
@@ -84,3 +86,5 @@ if (!selection.hasEdgeWithin(blockKey, start, end)) { | ||
var node = ReactDOM.findDOMNode(this); | ||
!node ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Missing node') : invariant(false) : void 0; | ||
var child = node.firstChild; | ||
!child ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Missing child') : invariant(false) : void 0; | ||
var targetNode = void 0; | ||
@@ -94,2 +98,3 @@ | ||
targetNode = child.firstChild; | ||
!targetNode ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Missing targetNode') : invariant(false) : void 0; | ||
} | ||
@@ -101,3 +106,5 @@ | ||
DraftEditorLeaf.prototype.shouldComponentUpdate = function shouldComponentUpdate(nextProps) { | ||
return ReactDOM.findDOMNode(this.refs.leaf).textContent !== nextProps.text || nextProps.styleSet !== this.props.styleSet || nextProps.forceSelection; | ||
var leafNode = ReactDOM.findDOMNode(this.refs.leaf); | ||
!leafNode ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Missing leafNode') : invariant(false) : void 0; | ||
return leafNode.textContent !== nextProps.text || nextProps.styleSet !== this.props.styleSet || nextProps.forceSelection; | ||
}; | ||
@@ -114,2 +121,3 @@ | ||
DraftEditorLeaf.prototype.render = function render() { | ||
var block = this.props.block; | ||
var text = this.props.text; | ||
@@ -145,3 +153,3 @@ | ||
if (customStyleFn) { | ||
var newStyles = customStyleFn(styleSet); | ||
var newStyles = customStyleFn(styleSet, block); | ||
styleObj = _assign(styleObj, newStyles); | ||
@@ -148,0 +156,0 @@ } |
@@ -26,2 +26,4 @@ /** | ||
var invariant = require('fbjs/lib/invariant'); | ||
// In IE, spans with <br> tags render as two newlines. By rendering a span | ||
@@ -83,2 +85,3 @@ // with only a newline character, we can be sure to render a single line. | ||
var shouldBeNewline = nextProps.children === ''; | ||
!(node instanceof Element) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'node is not an Element') : invariant(false) : void 0; | ||
if (shouldBeNewline) { | ||
@@ -85,0 +88,0 @@ return !isNewline(node); |
@@ -18,2 +18,3 @@ /** | ||
var ContentStateInlineStyle = require('./ContentStateInlineStyle'); | ||
var DraftFeatureFlags = require('./DraftFeatureFlags'); | ||
var Immutable = require('immutable'); | ||
@@ -79,20 +80,42 @@ | ||
removeRange: function removeRange(contentState, rangeToRemove, removalDirection) { | ||
var startKey = void 0, | ||
endKey = void 0, | ||
startBlock = void 0, | ||
endBlock = void 0; | ||
if (rangeToRemove.getIsBackward()) { | ||
rangeToRemove = rangeToRemove.merge({ | ||
anchorKey: rangeToRemove.getFocusKey(), | ||
anchorOffset: rangeToRemove.getFocusOffset(), | ||
focusKey: rangeToRemove.getAnchorKey(), | ||
focusOffset: rangeToRemove.getAnchorOffset(), | ||
isBackward: false | ||
}); | ||
} | ||
startKey = rangeToRemove.getAnchorKey(); | ||
endKey = rangeToRemove.getFocusKey(); | ||
startBlock = contentState.getBlockForKey(startKey); | ||
endBlock = contentState.getBlockForKey(endKey); | ||
var startOffset = rangeToRemove.getStartOffset(); | ||
var endOffset = rangeToRemove.getEndOffset(); | ||
var startEntityKey = startBlock.getEntityAt(startOffset); | ||
var endEntityKey = endBlock.getEntityAt(endOffset - 1); | ||
// Check whether the selection state overlaps with a single entity. | ||
// If so, try to remove the appropriate substring of the entity text. | ||
if (rangeToRemove.getAnchorKey() === rangeToRemove.getFocusKey()) { | ||
var key = rangeToRemove.getAnchorKey(); | ||
var startOffset = rangeToRemove.getStartOffset(); | ||
var endOffset = rangeToRemove.getEndOffset(); | ||
var block = contentState.getBlockForKey(key); | ||
var startEntity = block.getEntityAt(startOffset); | ||
var endEntity = block.getEntityAt(endOffset - 1); | ||
if (startEntity && startEntity === endEntity) { | ||
var adjustedRemovalRange = getCharacterRemovalRange(contentState.getEntityMap(), block, rangeToRemove, removalDirection); | ||
return removeRangeFromContentState(contentState, adjustedRemovalRange); | ||
if (startKey === endKey) { | ||
if (startEntityKey && startEntityKey === endEntityKey) { | ||
var _adjustedRemovalRange = getCharacterRemovalRange(contentState.getEntityMap(), startBlock, endBlock, rangeToRemove, removalDirection); | ||
return removeRangeFromContentState(contentState, _adjustedRemovalRange); | ||
} | ||
} | ||
var adjustedRemovalRange = rangeToRemove; | ||
if (DraftFeatureFlags.draft_segmented_entities_behavior) { | ||
// Adjust the selection to properly delete segemented and immutable | ||
// entities | ||
adjustedRemovalRange = getCharacterRemovalRange(contentState.getEntityMap(), startBlock, endBlock, rangeToRemove, removalDirection); | ||
} | ||
var withoutEntities = removeEntitiesAtEdges(contentState, rangeToRemove); | ||
return removeRangeFromContentState(withoutEntities, rangeToRemove); | ||
var withoutEntities = removeEntitiesAtEdges(contentState, adjustedRemovalRange); | ||
return removeRangeFromContentState(withoutEntities, adjustedRemovalRange); | ||
}, | ||
@@ -99,0 +122,0 @@ |
@@ -66,2 +66,4 @@ /** | ||
var editorState = editor._latestEditorState; | ||
var chars = e.data; | ||
@@ -80,3 +82,3 @@ | ||
// start of the block. | ||
if (editor.props.handleBeforeInput && isEventHandled(editor.props.handleBeforeInput(chars))) { | ||
if (editor.props.handleBeforeInput && isEventHandled(editor.props.handleBeforeInput(chars, editorState))) { | ||
e.preventDefault(); | ||
@@ -89,3 +91,2 @@ return; | ||
// is not collapsed, we will re-render. | ||
var editorState = editor._latestEditorState; | ||
var selection = editorState.getSelection(); | ||
@@ -92,0 +93,0 @@ |
@@ -15,2 +15,3 @@ /** | ||
var DraftFeatureFlags = require('./DraftFeatureFlags'); | ||
var DraftModifier = require('./DraftModifier'); | ||
@@ -51,6 +52,30 @@ var DraftOffsetKey = require('./DraftOffsetKey'); | ||
if (anchorNode.nodeType !== Node.TEXT_NODE) { | ||
return; | ||
var isNotTextNode = anchorNode.nodeType !== Node.TEXT_NODE; | ||
var isNotTextOrElementNode = anchorNode.nodeType !== Node.TEXT_NODE && anchorNode.nodeType !== Node.ELEMENT_NODE; | ||
if (DraftFeatureFlags.draft_killswitch_allow_nontextnodes) { | ||
if (isNotTextNode) { | ||
return; | ||
} | ||
} else { | ||
if (isNotTextOrElementNode) { | ||
// TODO: (t16149272) figure out context for this change | ||
return; | ||
} | ||
} | ||
if (anchorNode.nodeType === Node.TEXT_NODE && (anchorNode.previousSibling !== null || anchorNode.nextSibling !== null)) { | ||
// When typing at the beginning of a visual line, Chrome splits the text | ||
// nodes into two. Why? No one knows. This commit is suspicious: | ||
// https://chromium.googlesource.com/chromium/src/+/a3b600981286b135632371477f902214c55a1724 | ||
// To work around, we'll merge the sibling text nodes back into this one. | ||
var span = anchorNode.parentNode; | ||
anchorNode.nodeValue = span.textContent; | ||
for (var child = span.firstChild; child !== null; child = child.nextSibling) { | ||
if (child !== anchorNode) { | ||
span.removeChild(child); | ||
} | ||
} | ||
} | ||
var domText = anchorNode.textContent; | ||
@@ -57,0 +82,0 @@ var editorState = editor._latestEditorState; |
@@ -91,3 +91,3 @@ /** | ||
// no special handling is performed, fall through to command handling. | ||
if (editor.props.handleReturn && isEventHandled(editor.props.handleReturn(e))) { | ||
if (editor.props.handleReturn && isEventHandled(editor.props.handleReturn(e, editorState))) { | ||
return; | ||
@@ -139,3 +139,3 @@ } | ||
// Allow components higher up the tree to handle the command first. | ||
if (editor.props.handleKeyCommand && isEventHandled(editor.props.handleKeyCommand(command))) { | ||
if (editor.props.handleKeyCommand && isEventHandled(editor.props.handleKeyCommand(command, editorState))) { | ||
return; | ||
@@ -142,0 +142,0 @@ } |
@@ -73,4 +73,5 @@ /** | ||
var html = data.getHTML(); | ||
var editorState = editor._latestEditorState; | ||
if (editor.props.handlePastedText && isEventHandled(editor.props.handlePastedText(text, html))) { | ||
if (editor.props.handlePastedText && isEventHandled(editor.props.handlePastedText(text, html, editorState))) { | ||
return; | ||
@@ -133,3 +134,2 @@ } | ||
if (textBlocks.length) { | ||
var editorState = editor._latestEditorState; | ||
var character = CharacterMetadata.create({ | ||
@@ -136,0 +136,0 @@ style: editorState.getCurrentInlineStyle(), |
@@ -19,2 +19,3 @@ /** | ||
var getDraftEditorSelection = require('./getDraftEditorSelection'); | ||
var invariant = require('fbjs/lib/invariant'); | ||
@@ -27,3 +28,6 @@ function editOnSelect(editor) { | ||
var editorState = editor.props.editorState; | ||
var documentSelection = getDraftEditorSelection(editorState, ReactDOM.findDOMNode(editor.refs.editorContainer).firstChild); | ||
var editorNode = ReactDOM.findDOMNode(editor.refs.editorContainer); | ||
!editorNode ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Missing editorNode') : invariant(false) : void 0; | ||
!(editorNode.firstChild instanceof HTMLElement) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'editorNode.firstChild is not an HTMLElement') : invariant(false) : void 0; | ||
var documentSelection = getDraftEditorSelection(editorState, editorNode.firstChild); | ||
var updatedSelectionState = documentSelection.selectionState; | ||
@@ -30,0 +34,0 @@ |
@@ -331,4 +331,6 @@ /** | ||
// Don't discard inline style overrides on block type or depth changes. | ||
if (changeType !== 'adjust-depth' && changeType !== 'change-block-type') { | ||
// Don't discard inline style overrides for the following change types: | ||
var overrideChangeTypes = ['adjust-depth', 'change-block-type', 'split-block']; | ||
if (overrideChangeTypes.indexOf(changeType) === -1) { | ||
inlineStyleOverride = null; | ||
@@ -335,0 +337,0 @@ } |
@@ -35,6 +35,9 @@ 'use strict'; | ||
var documentBody = document.body; | ||
!documentBody ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Missing document.body') : invariant(false) : void 0; | ||
// forced layout here | ||
document.body.appendChild(div); | ||
documentBody.appendChild(div); | ||
var rect = div.getBoundingClientRect(); | ||
document.body.removeChild(div); | ||
documentBody.removeChild(div); | ||
@@ -41,0 +44,0 @@ return rect.height; |
@@ -30,5 +30,3 @@ /** | ||
haystack.reduce(function (value, nextValue, nextIndex) { | ||
/* $FlowFixMe(>=0.28.0): `value` could be undefined! */ | ||
if (!areEqualFn(value, nextValue)) { | ||
/* $FlowFixMe(>=0.28.0): `value` could be undefined! */ | ||
if (filterFn(value)) { | ||
@@ -35,0 +33,0 @@ foundFn(cursor, nextIndex); |
@@ -30,12 +30,43 @@ /** | ||
*/ | ||
function getCharacterRemovalRange(entityMap, block, selectionState, direction) { | ||
function getCharacterRemovalRange(entityMap, startBlock, endBlock, selectionState, direction) { | ||
var start = selectionState.getStartOffset(); | ||
var end = selectionState.getEndOffset(); | ||
var entityKey = block.getEntityAt(start); | ||
if (!entityKey) { | ||
var startEntityKey = startBlock.getEntityAt(start); | ||
var endEntityKey = endBlock.getEntityAt(end - 1); | ||
if (!startEntityKey && !endEntityKey) { | ||
return selectionState; | ||
} | ||
var newSelectionState = selectionState; | ||
if (startEntityKey && startEntityKey === endEntityKey) { | ||
newSelectionState = getEntityRemovalRange(entityMap, startBlock, newSelectionState, direction, startEntityKey, true, true); | ||
} else if (startEntityKey && endEntityKey) { | ||
var startSelectionState = getEntityRemovalRange(entityMap, startBlock, newSelectionState, direction, startEntityKey, false, true); | ||
var endSelectionState = getEntityRemovalRange(entityMap, endBlock, newSelectionState, direction, endEntityKey, false, false); | ||
newSelectionState = newSelectionState.merge({ | ||
anchorOffset: startSelectionState.getAnchorOffset(), | ||
focusOffset: endSelectionState.getFocusOffset(), | ||
isBackward: false | ||
}); | ||
} else if (startEntityKey) { | ||
var _startSelectionState = getEntityRemovalRange(entityMap, startBlock, newSelectionState, direction, startEntityKey, false, true); | ||
newSelectionState = newSelectionState.merge({ | ||
anchorOffset: _startSelectionState.getStartOffset(), | ||
isBackward: false | ||
}); | ||
} else if (endEntityKey) { | ||
var _endSelectionState = getEntityRemovalRange(entityMap, endBlock, newSelectionState, direction, endEntityKey, false, false); | ||
newSelectionState = newSelectionState.merge({ | ||
focusOffset: _endSelectionState.getEndOffset(), | ||
isBackward: false | ||
}); | ||
} | ||
return newSelectionState; | ||
} | ||
function getEntityRemovalRange(entityMap, block, selectionState, direction, entityKey, isEntireSelectionWithinEntity, isEntityAtStart) { | ||
var start = selectionState.getStartOffset(); | ||
var end = selectionState.getEndOffset(); | ||
var entity = entityMap.__get(entityKey); | ||
var mutability = entity.getMutability(); | ||
var sideToConsider = isEntityAtStart ? start : end; | ||
@@ -50,3 +81,3 @@ // `MUTABLE` entities can just have the specified range of text removed | ||
var entityRanges = getRangesForDraftEntity(block, entityKey).filter(function (range) { | ||
return start < range.end && end > range.start; | ||
return sideToConsider <= range.end && sideToConsider >= range.start; | ||
}); | ||
@@ -69,2 +100,10 @@ | ||
// remove. | ||
if (!isEntireSelectionWithinEntity) { | ||
if (isEntityAtStart) { | ||
end = entityRange.end; | ||
} else { | ||
start = entityRange.start; | ||
} | ||
} | ||
var removalRange = DraftEntitySegments.getRemovalRange(start, end, block.getText().slice(entityRange.start, entityRange.end), entityRange.start, direction); | ||
@@ -71,0 +110,0 @@ |
@@ -33,11 +33,22 @@ /** | ||
if (rects.length) { | ||
var _rects$ = rects[0]; | ||
top = _rects$.top; | ||
right = _rects$.right; | ||
bottom = _rects$.bottom; | ||
left = _rects$.left; | ||
// If the first rectangle has 0 width, we use the second, this is needed | ||
// because Chrome renders a 0 width rectangle when the selection contains | ||
// a line break. | ||
if (rects.length > 1 && rects[0].width === 0) { | ||
var _rects$ = rects[1]; | ||
top = _rects$.top; | ||
right = _rects$.right; | ||
bottom = _rects$.bottom; | ||
left = _rects$.left; | ||
} else { | ||
var _rects$2 = rects[0]; | ||
top = _rects$2.top; | ||
right = _rects$2.right; | ||
bottom = _rects$2.bottom; | ||
left = _rects$2.left; | ||
} | ||
for (var ii = 1; ii < rects.length; ii++) { | ||
var rect = rects[ii]; | ||
if (rect.height !== 0 || rect.width !== 0) { | ||
if (rect.height !== 0 && rect.width !== 0) { | ||
top = Math.min(top, rect.top); | ||
@@ -44,0 +55,0 @@ right = Math.max(right, rect.right); |
@@ -17,2 +17,4 @@ /** | ||
var invariant = require('fbjs/lib/invariant'); | ||
var isOldIE = UserAgent.isBrowser('IE <= 9'); | ||
@@ -30,2 +32,3 @@ | ||
doc = document.implementation.createHTMLDocument('foo'); | ||
!doc.documentElement ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Missing doc.documentElement') : invariant(false) : void 0; | ||
doc.documentElement.innerHTML = html; | ||
@@ -32,0 +35,0 @@ root = doc.getElementsByTagName('body')[0]; |
@@ -16,5 +16,54 @@ /** | ||
var DraftJsDebugLogging = require('./DraftJsDebugLogging'); | ||
var containsNode = require('fbjs/lib/containsNode'); | ||
var getActiveElement = require('fbjs/lib/getActiveElement'); | ||
var invariant = require('fbjs/lib/invariant'); | ||
function getAnonymizedDOM(node) { | ||
if (!node) { | ||
return '[empty]'; | ||
} | ||
var anonymized = anonymizeText(node); | ||
if (anonymized.nodeType === Node.TEXT_NODE) { | ||
return anonymized.textContent; | ||
} | ||
!(anonymized instanceof Element) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Node must be an Element if it is not a text node.') : invariant(false) : void 0; | ||
return anonymized.innerHTML; | ||
} | ||
function anonymizeText(node) { | ||
if (node.nodeType === Node.TEXT_NODE) { | ||
var length = node.textContent.length; | ||
return document.createTextNode('[text ' + length + ']'); | ||
} | ||
var clone = node.cloneNode(); | ||
var childNodes = node.childNodes; | ||
for (var ii = 0; ii < childNodes.length; ii++) { | ||
clone.appendChild(anonymizeText(childNodes[ii])); | ||
} | ||
return clone; | ||
} | ||
function getAnonymizedEditorDOM(node) { | ||
// grabbing the DOM content of the Draft editor | ||
var currentNode = node; | ||
while (currentNode) { | ||
if (currentNode instanceof Element && currentNode.hasAttribute('contenteditable')) { | ||
// found the Draft editor container | ||
return getAnonymizedDOM(currentNode); | ||
} else { | ||
currentNode = currentNode.parentNode; | ||
} | ||
} | ||
return 'Could not find contentEditable parent of node'; | ||
} | ||
function getNodeLength(node) { | ||
return node.nodeValue === null ? node.childNodes.length : node.nodeValue.length; | ||
} | ||
/** | ||
@@ -63,4 +112,4 @@ * In modern non-IE browsers, we can support both forward and backward | ||
selection.removeAllRanges(); | ||
addPointToSelection(selection, node, anchorOffset - nodeStart); | ||
addFocusToSelection(selection, node, focusOffset - nodeStart); | ||
addPointToSelection(selection, node, anchorOffset - nodeStart, selectionState); | ||
addFocusToSelection(selection, node, focusOffset - nodeStart, selectionState); | ||
return; | ||
@@ -73,3 +122,3 @@ } | ||
selection.removeAllRanges(); | ||
addPointToSelection(selection, node, anchorOffset - nodeStart); | ||
addPointToSelection(selection, node, anchorOffset - nodeStart, selectionState); | ||
} | ||
@@ -81,3 +130,3 @@ | ||
if (hasFocus) { | ||
addFocusToSelection(selection, node, focusOffset - nodeStart); | ||
addFocusToSelection(selection, node, focusOffset - nodeStart, selectionState); | ||
} | ||
@@ -90,3 +139,3 @@ } else { | ||
selection.removeAllRanges(); | ||
addPointToSelection(selection, node, focusOffset - nodeStart); | ||
addPointToSelection(selection, node, focusOffset - nodeStart, selectionState); | ||
} | ||
@@ -103,4 +152,4 @@ | ||
selection.removeAllRanges(); | ||
addPointToSelection(selection, node, anchorOffset - nodeStart); | ||
addFocusToSelection(selection, storedFocusNode, storedFocusOffset); | ||
addPointToSelection(selection, node, anchorOffset - nodeStart, selectionState); | ||
addFocusToSelection(selection, storedFocusNode, storedFocusOffset, selectionState); | ||
} | ||
@@ -113,3 +162,3 @@ } | ||
*/ | ||
function addFocusToSelection(selection, node, offset) { | ||
function addFocusToSelection(selection, node, offset, selectionState) { | ||
if (selection.extend && containsNode(getActiveElement(), node)) { | ||
@@ -121,2 +170,12 @@ // If `extend` is called while another element has focus, an error is | ||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=921444. | ||
// logging to catch bug that is being reported in t16250795 | ||
if (offset > getNodeLength(node)) { | ||
// the call to 'selection.extend' is about to throw | ||
DraftJsDebugLogging.logSelectionStateFailure({ | ||
anonymizedDom: getAnonymizedEditorDOM(node), | ||
extraParams: JSON.stringify({ offset: offset }), | ||
selectionState: JSON.stringify(selectionState.toJS()) | ||
}); | ||
} | ||
selection.extend(node, offset); | ||
@@ -135,4 +194,13 @@ } else { | ||
function addPointToSelection(selection, node, offset) { | ||
function addPointToSelection(selection, node, offset, selectionState) { | ||
var range = document.createRange(); | ||
// logging to catch bug that is being reported in t16250795 | ||
if (offset > getNodeLength(node)) { | ||
// in this case we know that the call to 'range.setStart' is about to throw | ||
DraftJsDebugLogging.logSelectionStateFailure({ | ||
anonymizedDom: getAnonymizedEditorDOM(node), | ||
extraParams: JSON.stringify({ offset: offset }), | ||
selectionState: JSON.stringify(selectionState.toJS()) | ||
}); | ||
} | ||
range.setStart(node, offset); | ||
@@ -139,0 +207,0 @@ selection.addRange(range); |
{ | ||
"name": "draft-js", | ||
"description": "A React framework for building text editors.", | ||
"version": "0.10.0", | ||
"version": "0.10.1", | ||
"keywords": [ | ||
@@ -11,3 +11,3 @@ "draftjs", | ||
], | ||
"homepage": "https://facebook.github.io/draft-js", | ||
"homepage": "http://draftjs.org/", | ||
"bugs": "https://github.com/facebook/draft-js/issues", | ||
@@ -55,3 +55,3 @@ "files": [ | ||
"fbjs-scripts": "^0.7.0", | ||
"flow-bin": "^0.32.0", | ||
"flow-bin": "^0.42.0", | ||
"gulp": "^3.9.0", | ||
@@ -71,2 +71,3 @@ "gulp-babel": "^6.1.2", | ||
"react-dom": "^15.0.0-rc.1", | ||
"react-test-renderer": "^15.5.4", | ||
"run-sequence": "^1.1.2", | ||
@@ -73,0 +74,0 @@ "through2": "^2.0.1", |
@@ -1,2 +0,2 @@ | ||
# [Draft.js](https://facebook.github.io/draft-js/) [![Build Status](https://img.shields.io/travis/facebook/draft-js/master.svg?style=flat)](https://travis-ci.org/facebook/draft-js) [![npm version](https://img.shields.io/npm/v/draft-js.svg?style=flat)](https://www.npmjs.com/package/draft-js) | ||
# [Draft.js](http://draftjs.org/) [![Build Status](https://img.shields.io/travis/facebook/draft-js/master.svg?style=flat)](https://travis-ci.org/facebook/draft-js) [![npm version](https://img.shields.io/npm/v/draft-js.svg?style=flat)](https://www.npmjs.com/package/draft-js) | ||
@@ -18,7 +18,11 @@ Draft.js is a JavaScript rich text editor framework, built for React and | ||
[Learn how to use Draft.js in your own project.](https://facebook.github.io/draft-js/docs/overview.html) | ||
[Learn how to use Draft.js in your own project.](http://draftjs.org/docs/overview.html) | ||
## API Notice | ||
Before getting started, please be aware that we are changing the API of Entity storage in Draft. Currently, the master branch supports both the old and new API. We hope to release this soon, as `v0.10.0`. Following that up will be `v0.11.0` which will remove the old API. This update will also include documentation on how to upgrade. If you are interested in helping out, or tracking the progress, please follow [issue 839](https://github.com/facebook/draft-js/issues/839). | ||
Before getting started, please be aware that we recently changed the API of | ||
Entity storage in Draft. The latest version, `v0.10.0`, supports both the old | ||
and new API. Following that up will be `v0.11.0` which will remove the old API. | ||
If you are interested in helping out, or tracking the progress, please follow | ||
[issue 839](https://github.com/facebook/draft-js/issues/839). | ||
@@ -31,2 +35,6 @@ ## Getting Started | ||
npm install --save draft-js react react-dom | ||
or | ||
yarn add draft-js react react-dom | ||
``` | ||
@@ -36,3 +44,3 @@ | ||
``` | ||
```javascript | ||
import React from 'react'; | ||
@@ -50,3 +58,3 @@ import ReactDOM from 'react-dom'; | ||
return ( | ||
<Editor editorState={this.state.editorState} onChange={this.onChange} /> | ||
<Editor editorState={this.state.editorState} onChange={this.onChange} /> | ||
); | ||
@@ -64,3 +72,3 @@ } | ||
``` | ||
```html | ||
<meta charset="utf-8" /> | ||
@@ -72,3 +80,3 @@ ``` | ||
Visit https://facebook.github.io/draft-js/ to try out a simple rich editor example. | ||
Visit http://draftjs.org/ to try out a simple rich editor example. | ||
@@ -75,0 +83,0 @@ The repository includes a variety of different editor examples to demonstrate |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
High entropy strings
Supply chain riskContains high entropy strings. This could be a sign of encrypted data, leaked secrets or obfuscated code.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
1299760
294
20420
115
32
36
2