OXIESEC PANEL
- Current Dir:
/
/
var
/
www
/
reader
/
_backup
/
tinymce
/
tinymce
/
src
/
core
/
main
/
ts
/
bookmark
Server IP: 139.59.38.164
Upload:
Create Dir:
Name
Size
Modified
Perms
📁
..
-
02/20/2020 05:44:43 AM
rwxr-xr-x
📄
BookmarkTypes.ts
1.4 KB
02/20/2020 05:42:14 AM
rw-r--r--
📄
Bookmarks.ts
1.07 KB
02/20/2020 05:42:14 AM
rw-r--r--
📄
CaretBookmark.ts
6.29 KB
02/20/2020 05:42:15 AM
rw-r--r--
📄
GetBookmark.ts
7.11 KB
02/20/2020 05:42:16 AM
rw-r--r--
📄
ResolveBookmark.ts
8.02 KB
02/20/2020 05:42:16 AM
rw-r--r--
Editing: ResolveBookmark.ts
Close
/** * Copyright (c) Tiny Technologies, Inc. All rights reserved. * Licensed under the LGPL or a commercial license. * For LGPL see License.txt in the project root for license information. * For commercial licenses see https://www.tiny.cloud/ */ import { HTMLElement, Node, Element, Range, Text } from '@ephox/dom-globals'; import { Option, Options } from '@ephox/katamari'; import Env from '../api/Env'; import * as CaretBookmark from './CaretBookmark'; import CaretPosition from '../caret/CaretPosition'; import NodeType from '../dom/NodeType'; import Tools from '../api/util/Tools'; import Selection from '../api/dom/Selection'; import { getParentCaretContainer } from '../fmt/FormatContainer'; import Zwsp from '../text/Zwsp'; import DOMUtils from '../api/dom/DOMUtils'; import CaretFinder from '../caret/CaretFinder'; import { isPathBookmark, isStringPathBookmark, isIdBookmark, isIndexBookmark, isRangeBookmark, PathBookmark, IdBookmark, Bookmark, IndexBookmark } from './BookmarkTypes'; const addBogus = (dom: DOMUtils, node: HTMLElement) => { // Adds a bogus BR element for empty block elements if (dom.isBlock(node) && !node.innerHTML && !Env.ie) { node.innerHTML = '<br data-mce-bogus="1" />'; } return node; }; const resolveCaretPositionBookmark = (dom: DOMUtils, bookmark) => { let rng, pos; rng = dom.createRng(); pos = CaretBookmark.resolve(dom.getRoot(), bookmark.start); rng.setStart(pos.container(), pos.offset()); pos = CaretBookmark.resolve(dom.getRoot(), bookmark.end); rng.setEnd(pos.container(), pos.offset()); return rng; }; const insertZwsp = (node: Node, rng: Range) => { const textNode = node.ownerDocument.createTextNode(Zwsp.ZWSP); node.appendChild(textNode); rng.setStart(textNode, 0); rng.setEnd(textNode, 0); }; const isEmpty = (node: Node) => node.hasChildNodes() === false; const tryFindRangePosition = (node: Element, rng: Range) => { return CaretFinder.lastPositionIn(node).fold( () => false, (pos) => { rng.setStart(pos.container(), pos.offset()); rng.setEnd(pos.container(), pos.offset()); return true; } ); }; // Since we trim zwsp from undo levels the caret format containers // may be empty if so pad them with a zwsp and move caret there const padEmptyCaretContainer = (root: HTMLElement, node: Node, rng: Range): boolean => { if (isEmpty(node) && getParentCaretContainer(root, node)) { insertZwsp(node, rng); return true; } else { return false; } }; const setEndPoint = (dom: DOMUtils, start: boolean, bookmark: PathBookmark, rng: Range) => { const point = bookmark[start ? 'start' : 'end']; let i, node, offset, children; const root = dom.getRoot(); if (point) { offset = point[0]; // Find container node for (node = root, i = point.length - 1; i >= 1; i--) { children = node.childNodes; if (padEmptyCaretContainer(root, node, rng)) { return true; } if (point[i] > children.length - 1) { if (padEmptyCaretContainer(root, node, rng)) { return true; } return tryFindRangePosition(node, rng); } node = children[point[i]]; } // Move text offset to best suitable location if (node.nodeType === 3) { offset = Math.min(point[0], node.nodeValue.length); } // Move element offset to best suitable location if (node.nodeType === 1) { offset = Math.min(point[0], node.childNodes.length); } // Set offset within container node if (start) { rng.setStart(node, offset); } else { rng.setEnd(node, offset); } } return true; }; const isValidTextNode = (node: Node): node is Text => NodeType.isText(node) && node.data.length > 0; const restoreEndPoint = (dom: DOMUtils, suffix: string, bookmark: IdBookmark) => { let marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev; const keep = bookmark.keep; let container, offset; if (marker) { node = marker.parentNode; if (suffix === 'start') { if (!keep) { idx = dom.nodeIndex(marker); } else { if (marker.hasChildNodes()) { node = marker.firstChild; idx = 1; } else if (isValidTextNode(marker.nextSibling)) { node = marker.nextSibling; idx = 0; } else if (isValidTextNode(marker.previousSibling)) { node = marker.previousSibling; idx = marker.previousSibling.data.length; } else { node = marker.parentNode; idx = dom.nodeIndex(marker) + 1; } } container = node; offset = idx; } else { if (!keep) { idx = dom.nodeIndex(marker); } else { if (marker.hasChildNodes()) { node = marker.firstChild; idx = 1; } else if (isValidTextNode(marker.previousSibling)) { node = marker.previousSibling; idx = marker.previousSibling.data.length; } else { node = marker.parentNode; idx = dom.nodeIndex(marker); } } container = node; offset = idx; } if (!keep) { prev = marker.previousSibling; next = marker.nextSibling; // Remove all marker text nodes Tools.each(Tools.grep(marker.childNodes), (node) => { if (NodeType.isText(node)) { node.nodeValue = node.nodeValue.replace(/\uFEFF/g, ''); } }); // Remove marker but keep children if for example contents where inserted into the marker // Also remove duplicated instances of the marker for example by a // split operation or by WebKit auto split on paste feature while ((marker = dom.get(bookmark.id + '_' + suffix))) { dom.remove(marker, true); } // If siblings are text nodes then merge them unless it's Opera since it some how removes the node // and we are sniffing since adding a lot of detection code for a browser with 3% of the market // isn't worth the effort. Sorry, Opera but it's just a fact if (prev && next && prev.nodeType === next.nodeType && NodeType.isText(prev) && !Env.opera) { idx = prev.nodeValue.length; prev.appendData(next.nodeValue); dom.remove(next); if (suffix === 'start') { container = prev; offset = idx; } else { container = prev; offset = idx; } } } return Option.some(CaretPosition(container, offset)); } else { return Option.none(); } }; const alt = <A>(o1: Option<A>, o2: Option<A>): Option<A> => o1.isSome() ? o1 : o2; const resolvePaths = (dom: DOMUtils, bookmark: PathBookmark): Option<Range> => { const rng = dom.createRng(); if (setEndPoint(dom, true, bookmark, rng) && setEndPoint(dom, false, bookmark, rng)) { return Option.some(rng); } else { return Option.none(); } }; const resolveId = (dom: DOMUtils, bookmark: IdBookmark) => { const startPos = restoreEndPoint(dom, 'start', bookmark); const endPos = restoreEndPoint(dom, 'end', bookmark); return Options.liftN([ startPos, alt(endPos, startPos) ], (spos, epos) => { const rng = dom.createRng(); rng.setStart(addBogus(dom, spos.container()), spos.offset()); rng.setEnd(addBogus(dom, epos.container()), epos.offset()); return rng; }); }; const resolveIndex = (dom: DOMUtils, bookmark: IndexBookmark) => { return Option.from(dom.select(bookmark.name)[bookmark.index]).map((elm) => { const rng = dom.createRng(); rng.selectNode(elm); return rng; }); }; const resolve = (selection: Selection, bookmark: Bookmark): Option<Range> => { const dom = selection.dom; if (bookmark) { if (isPathBookmark(bookmark)) { return resolvePaths(dom, bookmark); } else if (isStringPathBookmark(bookmark)) { return Option.some(resolveCaretPositionBookmark(dom, bookmark)); } else if (isIdBookmark(bookmark)) { return resolveId(dom, bookmark); } else if (isIndexBookmark(bookmark)) { return resolveIndex(dom, bookmark); } else if (isRangeBookmark(bookmark)) { return Option.some(bookmark.rng); } } return Option.none(); }; export default { resolve };