UNPKG

@atlaskit/editor-core

Version:

A package contains Atlassian editor core functionality

389 lines • 14.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var prosemirror_1 = require("../prosemirror"); var commands = require("../commands"); var keymaps_1 = require("../keymaps"); var json_1 = require("../renderer/json"); var error_reporter_1 = require("./error-reporter"); exports.ErrorReporter = error_reporter_1.default; var filter_1 = require("./filter"); exports.filterContentByType = filter_1.filterContentByType; function validateNode(node) { return false; } function isMarkTypeExcludedFromMark(markType, mark) { return mark.type.excludes(markType); } function isMarkTypeAllowedInNode(markType, state) { return commands.toggleMark(markType)(state); } function canMoveUp(state) { var selection = state.selection; if (selection instanceof prosemirror_1.TextSelection) { if (!selection.empty) { return true; } } return !atTheBeginningOfDoc(state); } exports.canMoveUp = canMoveUp; function canMoveDown(state) { var selection = state.selection; if (selection instanceof prosemirror_1.TextSelection) { if (!selection.empty) { return true; } } return !atTheEndOfDoc(state); } exports.canMoveDown = canMoveDown; function atTheEndOfDoc(state) { var selection = state.selection, doc = state.doc; return doc.nodeSize - selection.$to.pos - 2 === selection.$to.depth; } exports.atTheEndOfDoc = atTheEndOfDoc; function atTheBeginningOfDoc(state) { var selection = state.selection; return selection.$from.pos === selection.$from.depth; } exports.atTheBeginningOfDoc = atTheBeginningOfDoc; function atTheEndOfBlock(state) { var selection = state.selection; var $to = selection.$to; if (selection instanceof prosemirror_1.NodeSelection && selection.node.isBlock) { return true; } return endPositionOfParent($to) === $to.pos + 1; } exports.atTheEndOfBlock = atTheEndOfBlock; function atTheBeginningOfBlock(state) { var selection = state.selection; var $from = selection.$from; if (selection instanceof prosemirror_1.NodeSelection && selection.node.isBlock) { return true; } return startPositionOfParent($from) === $from.pos; } exports.atTheBeginningOfBlock = atTheBeginningOfBlock; function startPositionOfParent(resolvedPos) { return resolvedPos.start(resolvedPos.depth); } exports.startPositionOfParent = startPositionOfParent; function endPositionOfParent(resolvedPos) { return resolvedPos.end(resolvedPos.depth) + 1; } exports.endPositionOfParent = endPositionOfParent; /** * Check if a mark is allowed at the current position based on a given state. * This method looks both at the currently active marks as well as the node and marks * at the current position to determine if the given mark type is allowed. * If there's a non-empty selection, the current position corresponds to the start * of the selection. */ function isMarkTypeAllowedAtCurrentPosition(markType, state) { if (!isMarkTypeAllowedInNode(markType, state)) { return false; } var allowedInActiveMarks = true; var excludesMarkType = function (mark) { return isMarkTypeExcludedFromMark(markType, mark); }; if (state.tr.storedMarks) { allowedInActiveMarks = !state.tr.storedMarks.some(excludesMarkType); } else { allowedInActiveMarks = !state.selection.$from.marks().some(excludesMarkType); } return allowedInActiveMarks; } exports.isMarkTypeAllowedAtCurrentPosition = isMarkTypeAllowedAtCurrentPosition; /** * Step through block-nodes between $from and $to and returns false if a node is * found that isn't of the specified type */ function isRangeOfType(doc, $from, $to, nodeType) { return getAncestorNodesBetween(doc, $from, $to).filter(function (node) { return node.type !== nodeType; }).length === 0; } exports.isRangeOfType = isRangeOfType; function createSliceWithContent(content, state) { return new prosemirror_1.Slice(prosemirror_1.Fragment.from(state.schema.text(content)), 0, 0); } exports.createSliceWithContent = createSliceWithContent; /** * Determines if content inside a selection can be joined with the next block. * We need this check since the built-in method for "joinDown" will join a orderedList with bulletList. */ function canJoinDown(selection, doc, nodeType) { var res = doc.resolve(selection.$to.after(findAncestorPosition(doc, selection.$to).depth)); return res.nodeAfter && res.nodeAfter.type === nodeType; } exports.canJoinDown = canJoinDown; exports.setNodeSelection = function (view, pos) { var state = view.state, dispatch = view.dispatch; var tr = state.tr.setSelection(prosemirror_1.NodeSelection.create(state.doc, pos)); dispatch(tr); }; function setTextSelection(view, anchor, head) { var state = view.state; var tr = state.tr.setSelection(prosemirror_1.TextSelection.create(state.doc, anchor, head)); view.dispatch(tr); } exports.setTextSelection = setTextSelection; function moveCursorToTheEnd(view) { var state = view.state; var anchor = Math.max(state.doc.nodeSize - 2, 0); var tr = state.tr.setSelection(prosemirror_1.TextSelection.create(state.doc, anchor)).scrollIntoView(); view.dispatch(tr); } exports.moveCursorToTheEnd = moveCursorToTheEnd; /** * Determines if content inside a selection can be joined with the previous block. * We need this check since the built-in method for "joinUp" will join a orderedList with bulletList. */ function canJoinUp(selection, doc, nodeType) { var res = doc.resolve(selection.$from.before(findAncestorPosition(doc, selection.$from).depth)); return res.nodeBefore && res.nodeBefore.type === nodeType; } exports.canJoinUp = canJoinUp; /** * Returns all top-level ancestor-nodes between $from and $to */ function getAncestorNodesBetween(doc, $from, $to) { var nodes = Array(); var maxDepth = findAncestorPosition(doc, $from).depth; var current = doc.resolve($from.start(maxDepth)); while (current.pos <= $to.start($to.depth)) { var depth = Math.min(current.depth, maxDepth); var node = current.node(depth); if (node) { nodes.push(node); } if (depth === 0) { break; } var next = doc.resolve(current.after(depth)); if (next.start(depth) >= doc.nodeSize - 2) { break; } if (next.depth !== current.depth) { next = doc.resolve(next.pos + 2); } if (next.depth) { current = doc.resolve(next.start(next.depth)); } else { current = doc.resolve(next.end(next.depth)); } } return nodes; } exports.getAncestorNodesBetween = getAncestorNodesBetween; /** * Finds all "selection-groups" within a range. A selection group is based on ancestors. * * Example: * Given the following document and selection ({<} = start of selection and {>} = end) * doc * blockquote * ul * li * li{<} * li * p * p{>} * * The output will be two selection-groups. One within the ul and one with the two paragraphs. */ function getGroupsInRange(doc, $from, $to, isNodeValid) { if (isNodeValid === void 0) { isNodeValid = validateNode; } var groups = Array(); var commonAncestor = hasCommonAncestor(doc, $from, $to); var fromAncestor = findAncestorPosition(doc, $from); if (commonAncestor || (fromAncestor.depth === 1 && isNodeValid($from.node(1)))) { groups.push({ $from: $from, $to: $to }); } else { var current = $from; while (current.pos < $to.pos) { var ancestorPos = findAncestorPosition(doc, current); while (ancestorPos.depth > 1) { ancestorPos = findAncestorPosition(doc, ancestorPos); } var endPos = doc.resolve(Math.min( // should not be smaller then start position in case of an empty paragpraph for example. Math.max(ancestorPos.start(ancestorPos.depth), ancestorPos.end(ancestorPos.depth) - 3), $to.pos)); groups.push({ $from: current, $to: endPos }); current = doc.resolve(Math.min(endPos.after(1) + 1, doc.nodeSize - 2)); } } return groups; } exports.getGroupsInRange = getGroupsInRange; /** * Traverse the document until an "ancestor" is found. Any nestable block can be an ancestor. */ function findAncestorPosition(doc, pos) { var nestableBlocks = ['blockquote', 'bulletList', 'orderedList']; if (pos.depth === 1) { return pos; } var node = pos.node(pos.depth); var newPos = pos; while (pos.depth >= 1) { pos = doc.resolve(pos.before(pos.depth)); node = pos.node(pos.depth); if (node && nestableBlocks.indexOf(node.type.name) !== -1) { newPos = pos; } } return newPos; } exports.findAncestorPosition = findAncestorPosition; /** * Determine if two positions have a common ancestor. */ function hasCommonAncestor(doc, $from, $to) { var current; var target; if ($from.depth > $to.depth) { current = findAncestorPosition(doc, $from); target = findAncestorPosition(doc, $to); } else { current = findAncestorPosition(doc, $to); target = findAncestorPosition(doc, $from); } while (current.depth > target.depth && current.depth > 1) { current = findAncestorPosition(doc, current); } return current.node(current.depth) === target.node(target.depth); } exports.hasCommonAncestor = hasCommonAncestor; /** * Takes a selection $from and $to and lift all text nodes from their parents to document-level */ function liftSelection(tr, doc, $from, $to) { var startPos = $from.start($from.depth); var endPos = $to.end($to.depth); var target = Math.max(0, findAncestorPosition(doc, $from).depth - 1); tr.doc.nodesBetween(startPos, endPos, function (node, pos) { if (node.isText || (node.isTextblock && !node.textContent) // Empty paragraph ) { var res = tr.doc.resolve(tr.mapping.map(pos)); var sel = new prosemirror_1.NodeSelection(res); var range = sel.$from.blockRange(sel.$to); if (prosemirror_1.liftTarget(range) !== undefined) { tr.lift(range, target); } } }); startPos = tr.mapping.map(startPos); endPos = tr.mapping.map(endPos); endPos = tr.doc.resolve(endPos).end(tr.doc.resolve(endPos).depth); // We want to select the entire node tr.setSelection(new prosemirror_1.TextSelection(tr.doc.resolve(startPos), tr.doc.resolve(endPos))); return { tr: tr, $from: tr.doc.resolve(startPos), $to: tr.doc.resolve(endPos) }; } exports.liftSelection = liftSelection; /** * Lift nodes in block to one level above. */ function liftSiblingNodes(view) { var tr = view.state.tr; var _a = view.state.selection, $from = _a.$from, $to = _a.$to; var blockStart = tr.doc.resolve($from.start($from.depth - 1)); var blockEnd = tr.doc.resolve($to.end($to.depth - 1)); var range = blockStart.blockRange(blockEnd); view.dispatch(tr.lift(range, blockStart.depth - 1)); } exports.liftSiblingNodes = liftSiblingNodes; /** * Lift sibling nodes to document-level and select them. */ function liftAndSelectSiblingNodes(view) { var tr = view.state.tr; var _a = view.state.selection, $from = _a.$from, $to = _a.$to; var blockStart = tr.doc.resolve($from.start($from.depth - 1)); var blockEnd = tr.doc.resolve($to.end($to.depth - 1)); var range = blockStart.blockRange(blockEnd); tr.setSelection(new prosemirror_1.TextSelection(blockStart, blockEnd)); tr.lift(range, blockStart.depth - 1); return tr; } exports.liftAndSelectSiblingNodes = liftAndSelectSiblingNodes; function wrapIn(nodeType, tr, $from, $to) { var range = $from.blockRange($to); var wrapping = range && prosemirror_1.findWrapping(range, nodeType); if (wrapping) { tr = tr.wrap(range, wrapping).scrollIntoView(); } return tr; } exports.wrapIn = wrapIn; function toJSON(node) { return new json_1.default().serializeFragment(node.content); } exports.toJSON = toJSON; /** * Repeating string for multiple times */ function stringRepeat(text, length) { var result = ''; for (var x = 0; x < length; x++) { result += text; } return result; } exports.stringRepeat = stringRepeat; /** * A replacement for `Array.from` until it becomes widely implemented. */ function arrayFrom(obj) { return Array.prototype.slice.call(obj); } exports.arrayFrom = arrayFrom; function moveLeft(view) { var event = new CustomEvent('keydown', { bubbles: true, cancelable: true, }); event.keyCode = keymaps_1.LEFT; view.dispatchEvent(event); } exports.moveLeft = moveLeft; /** * Function will create a list of wrapper blocks present in a selection. */ function getSelectedWrapperNodes(state) { var nodes = []; if (state.selection) { var _a = state.selection, $from = _a.$from, $to = _a.$to; var _b = state.schema.nodes, blockquote_1 = _b.blockquote, panel_1 = _b.panel, orderedList_1 = _b.orderedList, bulletList_1 = _b.bulletList, listItem_1 = _b.listItem, codeBlock_1 = _b.codeBlock; state.doc.nodesBetween($from.pos, $to.pos, function (node, pos) { if ((node.isBlock && [blockquote_1, panel_1, orderedList_1, bulletList_1, listItem_1].indexOf(node.type) >= 0) || node.type === codeBlock_1) { nodes.push(node.type); } }); } return nodes; } /** * Function will check if changing block types: Paragraph, Heading is enabled. */ function areBlockTypesDisabled(state) { var nodesTypes = getSelectedWrapperNodes(state); var panel = state.schema.nodes.panel; return nodesTypes.filter(function (type) { return type !== panel; }).length > 0; } exports.areBlockTypesDisabled = areBlockTypesDisabled; exports.isTemporary = function (id) { return id.indexOf('temporary:') === 0; }; //# sourceMappingURL=index.js.map