From 9a8c873766f8d0f578fb45df4e5269c9cfa8fcdf Mon Sep 17 00:00:00 2001 From: JiuqingSong Date: Sun, 24 Feb 2019 15:18:20 -0800 Subject: [PATCH 1/3] Selection management step 1: New core API selectRange --- .../lib/coreAPI/focus.ts | 13 +- .../lib/coreAPI/insertNode.ts | 7 +- .../lib/coreAPI/{select.ts => selectRange.ts} | 128 +++++++--------- .../lib/editor/Editor.ts | 20 ++- .../lib/editor/createEditorCore.ts | 3 +- .../lib/interfaces/EditorCore.ts | 22 +++ .../lib/test/coreApi/hasFocusTest.ts | 3 +- packages/roosterjs-editor-dom/lib/index.ts | 7 +- .../lib/selection/createRange.ts | 145 +++++++++++++++--- .../lib/selection/getSelectionPath.ts | 38 ----- .../test/selections/getSelectionPathTest.ts | 3 +- .../ContentEdit/features/autoLinkFeatures.ts | 3 +- 12 files changed, 234 insertions(+), 158 deletions(-) rename packages/roosterjs-editor-core/lib/coreAPI/{select.ts => selectRange.ts} (53%) diff --git a/packages/roosterjs-editor-core/lib/coreAPI/focus.ts b/packages/roosterjs-editor-core/lib/coreAPI/focus.ts index e39e0de1a48..25af6aa4007 100644 --- a/packages/roosterjs-editor-core/lib/coreAPI/focus.ts +++ b/packages/roosterjs-editor-core/lib/coreAPI/focus.ts @@ -1,5 +1,5 @@ import EditorCore, { Focus } from '../interfaces/EditorCore'; -import { getFirstLeafNode } from 'roosterjs-editor-dom'; +import { createRange, getFirstLeafNode } from 'roosterjs-editor-dom'; import { PositionType } from 'roosterjs-editor-types'; const focus: Focus = (core: EditorCore) => { @@ -11,9 +11,16 @@ const focus: Focus = (core: EditorCore) => { // So here we always do a live selection pull on DOM and make it point in Editor. The pitfall is, the cursor could be reset // to very begin to of editor since we don't really have last saved selection (created on blur which does not fire in this case). // It should be better than the case you cannot type - if (!core.cachedSelectionRange || !core.api.select(core, core.cachedSelectionRange)) { + if ( + !core.cachedSelectionRange || + !core.api.selectRange(core, core.cachedSelectionRange, true /*skipSameRange*/) + ) { let node = getFirstLeafNode(core.contentDiv) || core.contentDiv; - core.api.select(core, node, PositionType.Begin); + core.api.selectRange( + core, + createRange(node, PositionType.Begin), + true /*skipSameRange*/ + ); } } diff --git a/packages/roosterjs-editor-core/lib/coreAPI/insertNode.ts b/packages/roosterjs-editor-core/lib/coreAPI/insertNode.ts index 78bbd7159ee..edecb4cf211 100644 --- a/packages/roosterjs-editor-core/lib/coreAPI/insertNode.ts +++ b/packages/roosterjs-editor-core/lib/coreAPI/insertNode.ts @@ -116,10 +116,11 @@ const insertNode: InsertNode = (core: EditorCore, node: Node, option: InsertOpti range = createRange(pos); range.insertNode(node); if (option.updateCursor && nodeForCursor) { - core.api.select(core, new Position(nodeForCursor, PositionType.After).normalize()); - } else { - core.api.select(core, rangeToRestore); + rangeToRestore = createRange( + new Position(nodeForCursor, PositionType.After).normalize() + ); } + core.api.selectRange(core, rangeToRestore); break; case ContentPosition.Outside: diff --git a/packages/roosterjs-editor-core/lib/coreAPI/select.ts b/packages/roosterjs-editor-core/lib/coreAPI/selectRange.ts similarity index 53% rename from packages/roosterjs-editor-core/lib/coreAPI/select.ts rename to packages/roosterjs-editor-core/lib/coreAPI/selectRange.ts index 1f2ec177d4a..d1a35e66b71 100644 --- a/packages/roosterjs-editor-core/lib/coreAPI/select.ts +++ b/packages/roosterjs-editor-core/lib/coreAPI/selectRange.ts @@ -1,73 +1,55 @@ -import EditorCore, { Select } from '../interfaces/EditorCore'; -import hasFocus from './hasFocus'; -import { contains, createRange, Position } from 'roosterjs-editor-dom'; -import { NodePosition, PositionType } from 'roosterjs-editor-types'; - -const select: Select = (core: EditorCore, arg1: any, arg2?: any, arg3?: any, arg4?: any) => { - let range: Range; - - if (!arg1) { - return false; - } else if (arg1 instanceof Range) { - range = arg1; - } else { - if (arg1.node) { - range = createRange(new Position(arg1), arg2 && arg2.node ? new Position(arg2) : null); - } else if (arg1 instanceof Node) { - let start: NodePosition; - let end: NodePosition; - if (arg2 == undefined) { - start = new Position(arg1, PositionType.Before); - end = new Position(arg1, PositionType.After); - } else { - start = new Position(arg1, arg2); - end = - arg3 instanceof Node - ? new Position(arg3, arg4) - : null; - } - range = createRange(start, end); - } - } - - if (contains(core.contentDiv, range)) { - let selection = core.document.defaultView.getSelection(); - if (selection) { - let needAddRange = true; - - if (selection.rangeCount > 0) { - // Workaround IE exception 800a025e - try { - // Do not remove/add range if current selection is the same with target range - // Without this check, execCommand() may fail in Edge since we changed the selection - let currentRange = selection.rangeCount == 1 ? selection.getRangeAt(0) : null; - if ( - currentRange && - currentRange.startContainer == range.startContainer && - currentRange.startOffset == range.startOffset && - currentRange.endContainer == range.endContainer && - currentRange.endOffset == range.endOffset - ) { - needAddRange = false; - } else { - selection.removeAllRanges(); - } - } catch (e) {} - } - - if (needAddRange) { - selection.addRange(range); - } - - if (!hasFocus(core)) { - core.cachedSelectionRange = range; - } - - return true; - } - } - - return false; -}; - -export default select; +import EditorCore, { Select, SelectRange } from '../interfaces/EditorCore'; +import hasFocus from './hasFocus'; +import { Browser, contains, createRange } from 'roosterjs-editor-dom'; + +const selectRange: SelectRange = (core: EditorCore, range: Range, skipSameRange?: boolean) => { + if (contains(core.contentDiv, range)) { + let selection = core.document.defaultView.getSelection(); + if (selection) { + let needAddRange = true; + + if (selection.rangeCount > 0) { + // Workaround IE exception 800a025e + try { + // Do not remove/add range if current selection is the same with target range + // Without this check, execCommand() may fail in Edge since we changed the selection + let currentRange = selection.rangeCount == 1 ? selection.getRangeAt(0) : null; + if ( + (skipSameRange || Browser.isEdge) && + currentRange && + currentRange.startContainer == range.startContainer && + currentRange.startOffset == range.startOffset && + currentRange.endContainer == range.endContainer && + currentRange.endOffset == range.endOffset + ) { + needAddRange = false; + } else { + selection.removeAllRanges(); + } + } catch (e) {} + } + + if (needAddRange) { + selection.addRange(range); + } + + if (!hasFocus(core)) { + core.cachedSelectionRange = range; + } + + return true; + } + } + + return false; +}; + +export default selectRange; + +/** + * @deprecated Only for compatibility with existing code, don't use ths function, use selectRange instead + */ +export const select: Select = (core: EditorCore, arg1: any, arg2?: any, arg3?: any, arg4?: any) => { + let range = arg1 instanceof Range ? arg1 : createRange(arg1, arg2, arg3, arg4); + return core.api.selectRange(core, range); +}; diff --git a/packages/roosterjs-editor-core/lib/editor/Editor.ts b/packages/roosterjs-editor-core/lib/editor/Editor.ts index f58d1cb9d4d..b89b901f68e 100644 --- a/packages/roosterjs-editor-core/lib/editor/Editor.ts +++ b/packages/roosterjs-editor-core/lib/editor/Editor.ts @@ -1,7 +1,6 @@ import createEditorCore from './createEditorCore'; import EditorCore from '../interfaces/EditorCore'; import EditorOptions from '../interfaces/EditorOptions'; -import { Browser, getRangeFromSelectionPath, getSelectionPath } from 'roosterjs-editor-dom'; import { GenericContentEditFeature } from '../interfaces/ContentEditFeature'; import { BlockElement, @@ -22,19 +21,23 @@ import { Rect, } from 'roosterjs-editor-types'; import { - PositionContentSearcher, - ContentTraverser, - Position, + Browser, + collapseNodes, contains, + ContentTraverser, + createRange, + findClosestElementAncestor, fromHtml, getBlockElementAtNode, - findClosestElementAncestor, - getPositionRect, getInlineElementAtNode, + getPositionRect, + getRangeFromSelectionPath, + getSelectionPath, getTagOfNode, isNodeEmpty, + Position, + PositionContentSearcher, queryElements, - collapseNodes, wrap, } from 'roosterjs-editor-dom'; @@ -517,7 +520,8 @@ export default class Editor { ): boolean; public select(arg1: any, arg2?: any, arg3?: any, arg4?: any): boolean { - return this.core.api.select(this.core, arg1, arg2, arg3, arg4); + let range = arg1 instanceof Range ? arg1 : createRange(arg1, arg2, arg3, arg4); + return this.core.api.selectRange(this.core, range); } /** diff --git a/packages/roosterjs-editor-core/lib/editor/createEditorCore.ts b/packages/roosterjs-editor-core/lib/editor/createEditorCore.ts index 478fec9cf52..d4224ca2489 100644 --- a/packages/roosterjs-editor-core/lib/editor/createEditorCore.ts +++ b/packages/roosterjs-editor-core/lib/editor/createEditorCore.ts @@ -12,7 +12,7 @@ import getSelectionRange from '../coreAPI/getSelectionRange'; import hasFocus from '../coreAPI/hasFocus'; import insertNode from '../coreAPI/insertNode'; import MouseUpPlugin from '../corePlugins/MouseUpPlugin'; -import select from '../coreAPI/select'; +import selectRange, { select } from '../coreAPI/selectRange'; import triggerEvent from '../coreAPI/triggerEvent'; import TypeInContainerPlugin from '../corePlugins/TypeInContainerPlugin'; import Undo from '../undo/Undo'; @@ -87,6 +87,7 @@ function createCoreApiMap(map?: Partial): CoreApiMap { hasFocus: map.hasFocus || hasFocus, insertNode: map.insertNode || insertNode, select: map.select || select, + selectRange: map.selectRange || selectRange, triggerEvent: map.triggerEvent || triggerEvent, }; } diff --git a/packages/roosterjs-editor-core/lib/interfaces/EditorCore.ts b/packages/roosterjs-editor-core/lib/interfaces/EditorCore.ts index 02e8450a6d9..46fe341184c 100644 --- a/packages/roosterjs-editor-core/lib/interfaces/EditorCore.ts +++ b/packages/roosterjs-editor-core/lib/interfaces/EditorCore.ts @@ -160,11 +160,22 @@ export type HasFocus = (core: EditorCore) => boolean; export type InsertNode = (core: EditorCore, node: Node, option: InsertOption) => boolean; /** + * @deprecated Use SelectRange instead * Select content * @param core The EditorCore object */ export type Select = (core: EditorCore, arg1: any, arg2?: any, arg3?: any, arg4?: any) => boolean; +/** + * Change the editor selection to the given range + * @param core The EditorCore object + * @param range The range to select + * @param skipSameRange When set to true, do nothing if the given range is the same with current selection + * in editor, otherwise it will always remove current selection ranage and set to the given one. + * This parameter is always treat as true in Edge to avoid some weird runtime exception. + */ +export type SelectRange = (core: EditorCore, range: Range, skipSameRange?: boolean) => boolean; + /** * Trigger a plugin event * @param core The EditorCore object @@ -232,11 +243,22 @@ export interface CoreApiMap { insertNode: InsertNode; /** + * @deprecated Use SelectRange instead * Select content * @param core The EditorCore object */ select: Select; + /** + * Change the editor selection to the given range + * @param core The EditorCore object + * @param range The range to select + * @param skipSameRange When set to true, do nothing if the given range is the same with current selection + * in editor, otherwise it will always remove current selection ranage and set to the given one. + * This parameter is always treat as true in Edge to avoid some weird runtime exception. + */ + selectRange: SelectRange; + /** * Trigger a plugin event * @param core The EditorCore object diff --git a/packages/roosterjs-editor-core/lib/test/coreApi/hasFocusTest.ts b/packages/roosterjs-editor-core/lib/test/coreApi/hasFocusTest.ts index 09da972a312..ef8fc924a71 100644 --- a/packages/roosterjs-editor-core/lib/test/coreApi/hasFocusTest.ts +++ b/packages/roosterjs-editor-core/lib/test/coreApi/hasFocusTest.ts @@ -1,6 +1,7 @@ import createEditorCore from '../../editor/createEditorCore'; import EditorCore from '../../interfaces/EditorCore'; import hasFocus from '../../coreAPI/hasFocus'; +import { createRange } from 'roosterjs-editor-dom'; describe('hasFocus', () => { let div: HTMLDivElement; @@ -44,7 +45,7 @@ describe('hasFocus', () => { expect(hasFocus(core)).toBe(false); let span = core.contentDiv.querySelector('span'); - core.api.select(core, span.firstChild, 1); + core.api.selectRange(core, createRange(span.firstChild, 1)); expect(hasFocus(core)).toBe(true); }); }); diff --git a/packages/roosterjs-editor-dom/lib/index.ts b/packages/roosterjs-editor-dom/lib/index.ts index 32d2d295a30..48dad6f715d 100644 --- a/packages/roosterjs-editor-dom/lib/index.ts +++ b/packages/roosterjs-editor-dom/lib/index.ts @@ -38,10 +38,7 @@ export { getFirstLeafNode, getLastLeafNode } from './utils/getLeafNode'; export { default as VTable, VCell } from './table/VTable'; export { default as Position } from './selection/Position'; -export { default as createRange } from './selection/createRange'; +export { default as createRange, getRangeFromSelectionPath } from './selection/createRange'; export { default as getPositionRect } from './selection/getPositionRect'; export { default as isPositionAtBeginningOf } from './selection/isPositionAtBeginningOf'; -export { - default as getSelectionPath, - getRangeFromSelectionPath, -} from './selection/getSelectionPath'; +export { default as getSelectionPath } from './selection/getSelectionPath'; diff --git a/packages/roosterjs-editor-dom/lib/selection/createRange.ts b/packages/roosterjs-editor-dom/lib/selection/createRange.ts index a0de8444a40..9c44571e45d 100644 --- a/packages/roosterjs-editor-dom/lib/selection/createRange.ts +++ b/packages/roosterjs-editor-dom/lib/selection/createRange.ts @@ -1,38 +1,97 @@ import isVoidHtmlElement from '../utils/isVoidHtmlElement'; import Position from './Position'; -import { NodePosition, NodeType, PositionType } from 'roosterjs-editor-types'; +import { NodePosition, NodeType, PositionType, SelectionPath } from 'roosterjs-editor-types'; /** - * Create a Range object with the given Node(s) - * @param node The node to select - * @param endNode The optional end node to select. When specified, range will start before the node and end after endNode + * Create a range around the given node(s) + * @param startNode The start node to create range from + * @param endNode The end node to create range from. If specified, the range will start before startNode and + * end after endNode, otherwise, the range will start before and end after the start node + * @returns A range start before the given node and end after the given node */ -export default function createRange(node: Node, endNode?: Node): Range; +export default function createRange(startNode: Node, endNode?: Node): Range; /** - * Create a Range object using start and end position - * @param start The start position - * @param end The end position + * Create a collapsed range at the given node and offset + * @param node The container node of the range + * @param offset The offset of the range, can be a number or value of PositionType + * @returns A range at the given node and offset */ -export default function createRange(start: NodePosition, end?: NodePosition): Range; +export default function createRange(node: Node, offset: number | PositionType): Range; -export default function createRange(start: NodePosition | Node, end?: NodePosition | Node): Range { - if (!start) { - return null; - } else if (start instanceof Node) { - end = new Position(end || start, PositionType.After); - start = new Position(start, PositionType.Before); - } else { - end = end || start; +/** + * Create a range with the given start/end container node and offset + * @param startNode The start container node of the range + * @param startOffset The start offset of the range + * @param endNode The end container node of the range + * @param endOffset The end offset of the range + * @returns A range at the given start/end container node and offset + */ +export default function createRange( + startNode: Node, + startOffset: number | PositionType, + endNode: Node, + endOffset: number | PositionType +): Range; + +/** + * Create a range under the given rootNode with start and end selection paths + * @param rootNode The root node that the selection paths start from + * @param startPath The selection path of the start position of the range + * @param endPath The selection path of the end position of the range + * @returns A range with the given start and end selection paths + */ +export default function createRange(rootNode: Node, startPath: number[], endPath?: number[]): Range; + +/** + * Create a range with the start and end position + * @param startPosition The start position of the range + * @param endPosition The end position of the range, if not specified, the range will be collapsed at start position + * @returns A range start at startPosition, end at endPosition, or startPosition when endPosition is not specified + */ +export default function createRange(startPosition: NodePosition, endPosition?: NodePosition): Range; + +export default function createRange( + arg1: Node | NodePosition, + arg2?: number | number[] | Node | NodePosition, + arg3?: Node | number[], + arg4?: number +): Range { + let start: NodePosition; + let end: NodePosition; + + if (isNodePosition(arg1)) { + // function createRange(startPosition: NodePosition, endPosition?: NodePosition): Range; + start = arg1; + end = isNodePosition(arg2) ? arg2 : null; + } else if (arg1 instanceof Node) { + if (arg2 instanceof Array) { + // function createRange(rootNode: Node, startPath: number[], endPath?: number[]): Range; + start = getPositionFromPath(arg1, arg2); + end = arg3 instanceof Array ? getPositionFromPath(arg1, arg3) : null; + } else if (typeof arg2 == 'number') { + // function createRange(node: Node, offset: number | PositionType): Range; + // function createRange(startNode: Node, startOffset: number | PositionType, endNode: Node, endOffset: number | PositionType): Range; + start = new Position(arg1, arg2); + end = arg3 instanceof Node ? new Position(arg3, arg4) : null; + } else if (arg2 instanceof Node || !arg2) { + // function createRange(startNode: Node, endNode?: Node): Range; + start = new Position(arg1, PositionType.Before); + end = new Position(arg2 || arg1, PositionType.After); + } } - let range = start.node.ownerDocument.createRange(); - start = getFocusablePosition(start); - end = getFocusablePosition(end); - range.setStart(start.node, start.offset); - range.setEnd(end.node, end.offset); + if (start) { + let range = start.node.ownerDocument.createRange(); + start = getFocusablePosition(start); + end = getFocusablePosition(end || start); + range.setStart(start.node, start.offset); + range.setEnd(end.node, end.offset); - return range; + return range; + } else { + return null; + } } /** @@ -44,3 +103,43 @@ function getFocusablePosition(position: NodePosition) { ? new Position(position.node, position.isAtEnd ? PositionType.After : PositionType.Before) : position; } + +function isNodePosition(arg: any): arg is NodePosition { + return arg && arg.node; +} + +function getPositionFromPath(node: Node, path: number[]): NodePosition { + if (!node || !path) { + return null; + } + + // Iterate with a for loop to avoid mutating the passed in element path stack + // or needing to copy it. + let offset: number; + + for (let i = 0; i < path.length; i++) { + offset = path[i]; + if ( + i < path.length - 1 && + node && + node.nodeType == NodeType.Element && + node.childNodes.length > offset + ) { + node = node.childNodes[offset]; + } else { + break; + } + } + + return new Position(node, offset); +} + +/** + * @deprecated Use createRange instead + * Get range from the given selection path + * @param rootNode Root node of the selection path + * @param path The selection path which contains start and end position path + */ +export function getRangeFromSelectionPath(rootNode: HTMLElement, path: SelectionPath) { + return createRange(rootNode, path.start, path.end); +} diff --git a/packages/roosterjs-editor-dom/lib/selection/getSelectionPath.ts b/packages/roosterjs-editor-dom/lib/selection/getSelectionPath.ts index 22bd2b03242..b71a6d28507 100644 --- a/packages/roosterjs-editor-dom/lib/selection/getSelectionPath.ts +++ b/packages/roosterjs-editor-dom/lib/selection/getSelectionPath.ts @@ -1,5 +1,4 @@ import contains from '../utils/contains'; -import createRange from './createRange'; import Position from './Position'; import { NodePosition, NodeType, SelectionPath } from 'roosterjs-editor-types'; @@ -21,43 +20,6 @@ export default function getSelectionPath(rootNode: HTMLElement, range: Range): S return selectionPath; } -/** - * Get range from the given selection path - * @param rootNode Root node of the selection path - * @param path The selection path which contains start and end position path - */ -export function getRangeFromSelectionPath(rootNode: HTMLElement, path: SelectionPath) { - let start = getPositionFromPath(rootNode, path.start); - let end = getPositionFromPath(rootNode, path.end); - return createRange(start, end); -} - -function getPositionFromPath(node: Node, path: number[]): NodePosition { - if (!node || !path) { - return null; - } - - // Iterate with a for loop to avoid mutating the passed in element path stack - // or needing to copy it. - let offset: number; - - for (let i = 0; i < path.length; i++) { - offset = path[i]; - if ( - i < path.length - 1 && - node && - node.nodeType == NodeType.Element && - node.childNodes.length > offset - ) { - node = node.childNodes[offset]; - } else { - break; - } - } - - return new Position(node, offset); -} - /** * Get the path of the node relative to rootNode. * The path of the node is an array of integer indecies into the childNodes of the given node. diff --git a/packages/roosterjs-editor-dom/lib/test/selections/getSelectionPathTest.ts b/packages/roosterjs-editor-dom/lib/test/selections/getSelectionPathTest.ts index 3b26df7b2b8..60c8d866590 100644 --- a/packages/roosterjs-editor-dom/lib/test/selections/getSelectionPathTest.ts +++ b/packages/roosterjs-editor-dom/lib/test/selections/getSelectionPathTest.ts @@ -1,4 +1,5 @@ -import getSelectionPath, { getRangeFromSelectionPath } from '../../selection/getSelectionPath'; +import getSelectionPath from '../../selection/getSelectionPath'; +import { getRangeFromSelectionPath } from '../../selection/createRange'; // Slim polyfill of Range since it is not defined by jsdom // see open issue https://github.com/jsdom/jsdom/issues/317 diff --git a/packages/roosterjs-editor-plugins/lib/ContentEdit/features/autoLinkFeatures.ts b/packages/roosterjs-editor-plugins/lib/ContentEdit/features/autoLinkFeatures.ts index 5e6a036b06a..5619c43fe2e 100644 --- a/packages/roosterjs-editor-plugins/lib/ContentEdit/features/autoLinkFeatures.ts +++ b/packages/roosterjs-editor-plugins/lib/ContentEdit/features/autoLinkFeatures.ts @@ -90,7 +90,6 @@ function hasLinkBeforeCursor(event: PluginKeyboardEvent, editor: Editor): boolea } function autoLink(event: PluginEvent, editor: Editor) { - let searcher = cacheGetContentSearcher(event, editor); let anchor = editor.getDocument().createElement('a'); let linkData = cacheGetLinkData(event, editor); anchor.textContent = linkData.originalUrl; @@ -98,7 +97,7 @@ function autoLink(event: PluginEvent, editor: Editor) { editor.runAsync(() => { editor.performAutoComplete(() => { - replaceWithNode(editor, linkData.originalUrl, anchor, false /* exactMatch */, searcher); + replaceWithNode(editor, linkData.originalUrl, anchor, false /* exactMatch */); // The content at cursor has changed. Should also clear the cursor data cache clearContentSearcherCache(event); From b2780bde69cff7339e4cae941fefa8fb2edea7b0 Mon Sep 17 00:00:00 2001 From: JiuqingSong Date: Sun, 24 Feb 2019 15:24:55 -0800 Subject: [PATCH 2/3] Fix build --- packages/roosterjs-editor-core/lib/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/roosterjs-editor-core/lib/index.ts b/packages/roosterjs-editor-core/lib/index.ts index 6c00e3691a7..ca4a1e9fa0f 100644 --- a/packages/roosterjs-editor-core/lib/index.ts +++ b/packages/roosterjs-editor-core/lib/index.ts @@ -16,6 +16,7 @@ export { HasFocus, InsertNode, Select, + SelectRange, TriggerEvent, } from './interfaces/EditorCore'; export { default as EditorOptions } from './interfaces/EditorOptions'; From 1e9e8e593397f06bf1ea4305bcfd63beea01d2eb Mon Sep 17 00:00:00 2001 From: Jiuqing Song Date: Wed, 27 Feb 2019 11:43:36 -0800 Subject: [PATCH 3/3] fix comment --- package.json | 4 ++-- packages/roosterjs-editor-core/lib/coreAPI/selectRange.ts | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 70b0381a47f..5a000d00c32 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "roosterjs", - "version": "7.2.1", + "version": "7.2.2", "description": "Framework-independent javascript editor", "repository": { "type": "git", @@ -67,4 +67,4 @@ "sass-loader": "^7.1.0", "url-loader": "^1.1.2" } -} \ No newline at end of file +} diff --git a/packages/roosterjs-editor-core/lib/coreAPI/selectRange.ts b/packages/roosterjs-editor-core/lib/coreAPI/selectRange.ts index d1a35e66b71..98cce728101 100644 --- a/packages/roosterjs-editor-core/lib/coreAPI/selectRange.ts +++ b/packages/roosterjs-editor-core/lib/coreAPI/selectRange.ts @@ -11,12 +11,13 @@ const selectRange: SelectRange = (core: EditorCore, range: Range, skipSameRange? if (selection.rangeCount > 0) { // Workaround IE exception 800a025e try { + let currentRange: Range; // Do not remove/add range if current selection is the same with target range // Without this check, execCommand() may fail in Edge since we changed the selection - let currentRange = selection.rangeCount == 1 ? selection.getRangeAt(0) : null; if ( (skipSameRange || Browser.isEdge) && - currentRange && + (currentRange = + selection.rangeCount == 1 ? selection.getRangeAt(0) : null) && currentRange.startContainer == range.startContainer && currentRange.startOffset == range.startOffset && currentRange.endContainer == range.endContainer &&