/** * Bookmark.js * * Released under LGPL License. * Copyright (c) 1999-2016 Ephox Corp. All rights reserved * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing */ /** * Returns a range bookmark. This will convert indexed bookmarks into temporary span elements with * index 0 so that they can be restored properly after the DOM has been modified. Text bookmarks will not have spans * added to them since they can be restored after a dom operation. * * So this:

||

* becomes:

||

* * @param {DOMRange} rng DOM Range to get bookmark on. * @return {Object} Bookmark object. */ const create = function (dom, rng) { const bookmark = {}; function setupEndPoint(start?) { let offsetNode, container, offset; container = rng[start ? 'startContainer' : 'endContainer']; offset = rng[start ? 'startOffset' : 'endOffset']; if (container.nodeType === 1) { offsetNode = dom.create('span', { 'data-mce-type': 'bookmark' }); if (container.hasChildNodes()) { offset = Math.min(offset, container.childNodes.length - 1); if (start) { container.insertBefore(offsetNode, container.childNodes[offset]); } else { dom.insertAfter(offsetNode, container.childNodes[offset]); } } else { container.appendChild(offsetNode); } container = offsetNode; offset = 0; } bookmark[start ? 'startContainer' : 'endContainer'] = container; bookmark[start ? 'startOffset' : 'endOffset'] = offset; } setupEndPoint(true); if (!rng.collapsed) { setupEndPoint(); } return bookmark; }; /** * Moves the selection to the current bookmark and removes any selection container wrappers. * * @param {Object} bookmark Bookmark object to move selection to. */ const resolve = function (dom, bookmark) { function restoreEndPoint(start?) { let container, offset, node; function nodeIndex(container) { let node = container.parentNode.firstChild, idx = 0; while (node) { if (node === container) { return idx; } // Skip data-mce-type=bookmark nodes if (node.nodeType !== 1 || node.getAttribute('data-mce-type') !== 'bookmark') { idx++; } node = node.nextSibling; } return -1; } container = node = bookmark[start ? 'startContainer' : 'endContainer']; offset = bookmark[start ? 'startOffset' : 'endOffset']; if (!container) { return; } if (container.nodeType === 1) { offset = nodeIndex(container); container = container.parentNode; dom.remove(node); } bookmark[start ? 'startContainer' : 'endContainer'] = container; bookmark[start ? 'startOffset' : 'endOffset'] = offset; } restoreEndPoint(true); restoreEndPoint(); const rng = dom.createRng(); rng.setStart(bookmark.startContainer, bookmark.startOffset); if (bookmark.endContainer) { rng.setEnd(bookmark.endContainer, bookmark.endOffset); } return rng; }; export default { create, resolve };