UNPKG

wed

Version:

Wed is a schema-aware editor for XML documents.

155 lines 6.14 kB
/** * @author Louis-Dominique Dubeau * @license MPL 2.0 * @copyright Mangalam Research Center for Buddhist Languages */ define(["require", "exports", "./dloc", "./domtypeguards", "./guiroot"], function (require, exports, dloc_1, domtypeguards_1, guiroot_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); // Utility function for boundaryXY. function parentBoundary(node, root) { const parent = node.parentNode; // Cannot find a sensible boundary if (!root.contains(parent)) { return { left: 0, top: 0, bottom: 0 }; } return boundaryXY(dloc_1.DLoc.mustMakeDLoc(root, node)); } // tslint:disable-next-line:max-func-body-length function boundaryXY(boundary) { const node = boundary.node; let offset = boundary.offset; const nodeIsElement = domtypeguards_1.isElement(node); const nodeIsText = domtypeguards_1.isText(node); let nodeLen; if (nodeIsElement) { nodeLen = node.childNodes.length; } else if (nodeIsText) { nodeLen = node.length; } else { throw new Error(`unexpected node type: ${node.nodeType}`); } // The node is empty ... if (nodeLen === 0) { return parentBoundary(node, boundary.root); } const range = node.ownerDocument.createRange(); let rect; let child; while (offset < nodeLen) { // The array is empty if the node is a text node, and child will be // undefined. child = node.childNodes[offset]; // We use getClientRects()[0] so that when we are working with an inline // node, we get only the first rect of the node. If the node is a block, // then there should be only one rect anyway. if (domtypeguards_1.isElement(child)) { rect = child.getClientRects()[0]; } else { range.setStart(node, offset); range.setEnd(node, offset + 1); rect = range.getClientRects()[0]; } // If the element that covers the range is invisible, then getClientRects // can return undefined. A 0, 0, 0, 0 rect is also theoretically possible. if (rect != null && (rect.left !== 0 || rect.right !== 0 || rect.top !== 0 || rect.bottom !== 0)) { return { left: rect.left, top: rect.top, bottom: rect.bottom }; } offset++; } // We failed to find something after our offset from which to get // coordinates. Try again. offset = boundary.offset; const win = node.ownerDocument.defaultView; while (offset !== 0) { offset--; child = undefined; // We check whether the thing we are going to cover with the range is // inline. let inline; if (nodeIsText) { inline = true; } else if (nodeIsElement) { child = node.childNodes[offset]; if (domtypeguards_1.isText(child)) { inline = true; } else if (domtypeguards_1.isElement(child)) { const display = win.getComputedStyle(child).getPropertyValue("display"); inline = (display === "inline" || display === "inline-block"); } else { throw new Error(`unexpected node type: ${child.nodeType}`); } } else { throw new Error(`unexpected node type: ${node.nodeType}`); } // If it is not empty, and offset is at the end of the contents, then there // must be something *before* the point indicated by offset. Get a rectangle // around that and return the right side as the left value. let rects; if (domtypeguards_1.isElement(child)) { rects = child.getClientRects(); } else { range.setStart(node, offset); range.setEnd(node, offset + 1); rects = range.getClientRects(); } rect = rects[rects.length - 1]; if (rect != null) { return (inline ? // Yep, we use the right side when it is inline. { left: rect.right, top: rect.top, bottom: rect.bottom } : { left: rect.left, top: rect.top, bottom: rect.bottom }); } } // We can get here with an offset of 0. In this case, we have to move to the // parent. return parentBoundary(node, boundary.root); } exports.boundaryXY = boundaryXY; function getAttrValueNode(attrVal) { if (!attrVal.classList.contains("_attribute_value")) { throw new Error("getAttrValueNode operates only on attribute values"); } let ret = attrVal; let child = attrVal.firstChild; if (child !== null) { while (child !== null && !domtypeguards_1.isText(child)) { child = child.nextSibling; } if (child !== null) { ret = child; } } return ret; } exports.getAttrValueNode = getAttrValueNode; function getGUINodeIfExists(editor, node) { if (node == null) { return undefined; } try { const caret = editor.caretManager.fromDataLocation(node, 0); return caret != null ? caret.node : undefined; } catch (ex) { if (ex instanceof guiroot_1.AttributeNotFound) { return undefined; } throw ex; } } exports.getGUINodeIfExists = getGUINodeIfExists; }); // LocalWords: MPL domutil util boundaryXY nodeType getClientRects rect // LocalWords: getAttrValueNode //# sourceMappingURL=wed-util.js.map