@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
305 lines (297 loc) • 11.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.bracketTyped = bracketTyped;
exports.getChangedNodes = getChangedNodes;
exports.getStepRange = void 0;
exports.hasDocAsParent = hasDocAsParent;
exports.hasVisibleContent = hasVisibleContent;
exports.isEmptyDocument = isEmptyDocument;
exports.isSelectionEndOfParagraph = void 0;
exports.nodesBetweenChanged = nodesBetweenChanged;
exports.processRawValue = processRawValue;
var _transforms = require("@atlaskit/adf-utils/transforms");
var _model = require("@atlaskit/editor-prosemirror/model");
var _analytics = require("../analytics");
var _editorCoreUtils = require("./editor-core-utils");
var _privacyFilter = require("./filter/privacy-filter");
var _trackUnsupportedContent = require("./track-unsupported-content");
var _validateUsingSpec = require("./validate-using-spec");
var getStepRange = exports.getStepRange = function getStepRange(transaction) {
var from = -1;
var to = -1;
transaction.mapping.maps.forEach(function (stepMap, index) {
stepMap.forEach(function (oldStart, oldEnd) {
var newStart = transaction.mapping.slice(index).map(oldStart, -1);
var newEnd = transaction.mapping.slice(index).map(oldEnd);
from = newStart < from || from === -1 ? newStart : from;
to = newEnd > to || to === -1 ? newEnd : to;
});
});
if (from !== -1) {
return {
from: from,
to: to
};
}
return null;
};
// Checks to see if the parent node is the document, ie not contained within another entity
function hasDocAsParent($anchor) {
return $anchor.depth === 1;
}
/**
* Checks if a node looks like an empty document
*/
function isEmptyDocument(node) {
var nodeChild = node.content.firstChild;
if (node.childCount !== 1 || !nodeChild) {
return false;
}
return (0, _editorCoreUtils.isEmptyParagraph)(nodeChild);
}
function bracketTyped(state) {
var selection = state.selection;
var _ref = selection,
$cursor = _ref.$cursor,
$anchor = _ref.$anchor;
if (!$cursor) {
return false;
}
var node = $cursor.nodeBefore;
if (!node) {
return false;
}
if (node.type.name === 'text' && node.text === '{') {
var paragraphNode = $anchor.node();
return paragraphNode.marks.length === 0 && hasDocAsParent($anchor);
}
return false;
}
function nodesBetweenChanged(tr, f, startPos) {
var stepRange = getStepRange(tr);
if (!stepRange) {
return;
}
tr.doc.nodesBetween(stepRange.from, stepRange.to, f, startPos);
}
function processRawValue(schema, value, providerFactory, sanitizePrivateContent, contentTransformer, dispatchAnalyticsEvent) {
if (!value) {
return;
}
var node;
if (typeof value === 'string') {
try {
if (contentTransformer) {
var doc = contentTransformer.parse(value);
node = doc.toJSON();
} else {
node = JSON.parse(value);
}
} catch (e) {
// eslint-disable-next-line no-console
console.error("Error processing value: ".concat(value, " isn't a valid JSON"));
return;
}
} else {
node = value;
}
if (Array.isArray(node)) {
// eslint-disable-next-line no-console
console.error("Error processing value: ".concat(node, " is an array, but it must be an object."));
return;
}
try {
// ProseMirror always require a child under doc
if (node.type === 'doc') {
if (Array.isArray(node.content) && node.content.length === 0) {
node.content.push({
type: 'paragraph',
content: []
});
}
// Just making sure doc is always valid
if (!node.version) {
node.version = 1;
}
}
if (contentTransformer) {
return _model.Node.fromJSON(schema, node);
}
// link mark on mediaSingle is deprecated, need to move link mark to child media node
// https://product-fabric.atlassian.net/browse/ED-14043
var _transformMediaLinkMa = (0, _transforms.transformMediaLinkMarks)(node),
transformedAdf = _transformMediaLinkMa.transformedAdf,
isTransformed = _transformMediaLinkMa.isTransformed;
if (isTransformed && dispatchAnalyticsEvent) {
dispatchAnalyticsEvent({
action: _analytics.ACTION.MEDIA_LINK_TRANSFORMED,
actionSubject: _analytics.ACTION_SUBJECT.EDITOR,
eventType: _analytics.EVENT_TYPE.OPERATIONAL
});
}
// See: HOT-97965 https://product-fabric.atlassian.net/browse/ED-14400
// We declared in code mark spec that links and marks should not co-exist on
// text nodes. This util strips code marks from bad text nodes and preserves links.
// Otherwise, prosemirror will try to repair the invalid document by stripping links
// and preserving code marks during content changes.
var _transformTextLinkCod = (0, _transforms.transformTextLinkCodeMarks)(transformedAdf);
transformedAdf = _transformTextLinkCod.transformedAdf;
isTransformed = _transformTextLinkCod.isTransformed;
if (isTransformed && dispatchAnalyticsEvent) {
dispatchAnalyticsEvent({
action: _analytics.ACTION.TEXT_LINK_MARK_TRANSFORMED,
actionSubject: _analytics.ACTION_SUBJECT.EDITOR,
eventType: _analytics.EVENT_TYPE.OPERATIONAL
});
}
var discardedMarks = [];
var _transformDedupeMarks = (0, _transforms.transformDedupeMarks)(transformedAdf);
transformedAdf = _transformDedupeMarks.transformedAdf;
isTransformed = _transformDedupeMarks.isTransformed;
discardedMarks = _transformDedupeMarks.discardedMarks;
if (isTransformed && dispatchAnalyticsEvent) {
dispatchAnalyticsEvent({
action: _analytics.ACTION.DEDUPE_MARKS_TRANSFORMED_V2,
actionSubject: _analytics.ACTION_SUBJECT.EDITOR,
eventType: _analytics.EVENT_TYPE.OPERATIONAL,
attributes: {
/** UGC WARNING
*
* DO NOT include the `mark` attributes inside, we map here to only
* extract the mark type as that is the only non-UGC safe information
* that we can add to event-attributes
*
*/
discardedMarkTypes: discardedMarks.map(function (mark) {
return mark.type;
})
}
});
}
var _transformNodesMissin = (0, _transforms.transformNodesMissingContent)(transformedAdf);
transformedAdf = _transformNodesMissin.transformedAdf;
isTransformed = _transformNodesMissin.isTransformed;
if (isTransformed && dispatchAnalyticsEvent) {
dispatchAnalyticsEvent({
action: _analytics.ACTION.NODES_MISSING_CONTENT_TRANSFORMED,
actionSubject: _analytics.ACTION_SUBJECT.EDITOR,
eventType: _analytics.EVENT_TYPE.OPERATIONAL
});
}
var _transformIndentation = (0, _transforms.transformIndentationMarks)(transformedAdf);
transformedAdf = _transformIndentation.transformedAdf;
isTransformed = _transformIndentation.isTransformed;
if (isTransformed && dispatchAnalyticsEvent) {
dispatchAnalyticsEvent({
action: _analytics.ACTION.INDENTATION_MARKS_TRANSFORMED,
actionSubject: _analytics.ACTION_SUBJECT.EDITOR,
eventType: _analytics.EVENT_TYPE.OPERATIONAL
});
}
var _transformInvalidMedi = (0, _transforms.transformInvalidMediaContent)(transformedAdf);
transformedAdf = _transformInvalidMedi.transformedAdf;
isTransformed = _transformInvalidMedi.isTransformed;
if (isTransformed && dispatchAnalyticsEvent) {
dispatchAnalyticsEvent({
action: _analytics.ACTION.INVALID_MEDIA_CONTENT_TRANSFORMED,
actionSubject: _analytics.ACTION_SUBJECT.EDITOR,
eventType: _analytics.EVENT_TYPE.OPERATIONAL
});
}
var entity = (0, _validateUsingSpec.validateADFEntity)(schema, transformedAdf || node, dispatchAnalyticsEvent);
var newEntity = maySanitizePrivateContent(entity, providerFactory, sanitizePrivateContent);
var parsedDoc = _model.Node.fromJSON(schema, newEntity);
// throws an error if the document is invalid
try {
parsedDoc.check();
} catch (err) {
if (dispatchAnalyticsEvent) {
dispatchAnalyticsEvent({
action: _analytics.ACTION.INVALID_PROSEMIRROR_DOCUMENT,
actionSubject: _analytics.ACTION_SUBJECT.EDITOR,
eventType: _analytics.EVENT_TYPE.OPERATIONAL
});
}
throw err;
}
if (dispatchAnalyticsEvent) {
(0, _trackUnsupportedContent.findAndTrackUnsupportedContentNodes)(parsedDoc, schema, dispatchAnalyticsEvent);
}
return parsedDoc;
} catch (e) {
if (dispatchAnalyticsEvent) {
dispatchAnalyticsEvent({
action: _analytics.ACTION.DOCUMENT_PROCESSING_ERROR,
actionSubject: _analytics.ACTION_SUBJECT.EDITOR,
eventType: _analytics.EVENT_TYPE.OPERATIONAL
});
}
// eslint-disable-next-line no-console
console.error("Error processing document:\n".concat(e instanceof Error ? e.message : String(e), "\n\n"), JSON.stringify(node));
if (isProseMirrorSchemaCheckError(e)) {
throw e;
}
return;
}
}
function isProseMirrorSchemaCheckError(error) {
return error instanceof RangeError && (!!error.message.match(/^Invalid collection of marks for node/) || !!error.message.match(/^Invalid content for node/));
}
var maySanitizePrivateContent = function maySanitizePrivateContent(entity, providerFactory, sanitizePrivateContent) {
if (sanitizePrivateContent && providerFactory) {
return (0, _privacyFilter.sanitizeNodeForPrivacy)(entity, providerFactory);
}
return entity;
};
/**
* Returns false if node contains only empty inline nodes and hardBreaks.
*/
function hasVisibleContent(node) {
var isInlineNodeHasVisibleContent = function isInlineNodeHasVisibleContent(inlineNode) {
return inlineNode.isText ? !!inlineNode.textContent.trim() : inlineNode.type.name !== 'hardBreak';
};
if (node.isInline) {
return isInlineNodeHasVisibleContent(node);
} else if (node.isBlock && (node.isLeaf || node.isAtom)) {
return true;
} else if (!node.childCount) {
return false;
}
for (var _index = 0; _index < node.childCount; _index++) {
var child = node.child(_index);
var invisibleNodeTypes = ['paragraph', 'text', 'hardBreak'];
if (!invisibleNodeTypes.includes(child.type.name) || hasVisibleContent(child)) {
return true;
}
}
return false;
}
var isSelectionEndOfParagraph = exports.isSelectionEndOfParagraph = function isSelectionEndOfParagraph(state) {
return state.selection.$to.parent.type === state.schema.nodes.paragraph && state.selection.$to.pos === state.doc.resolve(state.selection.$to.pos).end();
};
function getChangedNodesIn(_ref2) {
var tr = _ref2.tr,
doc = _ref2.doc;
var nodes = [];
var stepRange = getStepRange(tr);
if (!stepRange) {
return nodes;
}
var from = Math.min(doc.nodeSize - 2, stepRange.from);
var to = Math.min(doc.nodeSize - 2, stepRange.to);
doc.nodesBetween(from, to, function (node, pos) {
nodes.push({
node: node,
pos: pos
});
});
return nodes;
}
function getChangedNodes(tr) {
return getChangedNodesIn({
tr: tr,
doc: tr.doc
});
}