/* * Copyright notice: * * Large parts of this file are heavily inspired if not downright copied from editor.js, * copyright MIT. * https://editorjs.io/ */ import { isElement } from './dom'; export const getSelection = globalThis.getSelection ? globalThis.getSelection : () => null; /** * Returns range from passed Selection object * * @param selection - Selection object to get Range from */ export const getRangeFromSelection = (selection: Selection): Range|null => { return selection && selection.rangeCount ? selection.getRangeAt(0) : null; } /** * Returns selected anchor element * * @returns {Element|null} */ export const getAnchorElementForSelection = (selection: Selection): Element | null => { const anchorNode = selection.anchorNode; if (!anchorNode) { return null; } if (!isElement(anchorNode)) { return anchorNode.parentElement; } else { return anchorNode; } } /** * Calculates position and size of selected text * * @returns {DOMRect} */ export const getRect = (selection: Selection): DOMRect => { const defaultRect = { x: 0, y: 0, width: 0, height: 0, } as DOMRect; if (selection.rangeCount === null || isNaN(selection.rangeCount)) { console.warn('Method SelectionUtils.rangeCount is not supported'); return defaultRect; } if (selection.rangeCount === 0) { return defaultRect; } const range = selection.getRangeAt(0).cloneRange() as Range; let rect = { ...defaultRect }; if (range.getBoundingClientRect) { rect = range.getBoundingClientRect() as DOMRect; } // Fall back to inserting a temporary element if (rect.x === 0 && rect.y === 0) { const span = document.createElement('span'); if (span.getBoundingClientRect) { // Ensure span has dimensions and position by // adding a zero-width space character span.appendChild(document.createTextNode('\u200b')); range.insertNode(span); rect = span.getBoundingClientRect() as DOMRect; const spanParent = span.parentNode; if (spanParent) { spanParent.removeChild(span); // Glue any broken text nodes back together spanParent.normalize(); } } } return rect; };