@atlaskit/editor-plugin-paste
Version:
Paste plugin for @atlaskit/editor-core
149 lines (142 loc) • 8.55 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.handleSyncBlocksPaste = void 0;
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _adfSchema = require("@atlaskit/adf-schema");
var _analytics = require("@atlaskit/editor-common/analytics");
var _utils = require("@atlaskit/editor-common/utils");
var _actions = require("../../editor-actions/actions");
var _pastePluginType = require("../../pastePluginType");
var _pluginFactory = require("../../pm-plugins/plugin-factory");
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
// Ignored via go/ees005
// eslint-disable-next-line require-unicode-regexp
var RESOURCE_ID_REGEX = /data-resource-id="(confluence-page|jira-work-item)\//;
var FLAG_ID = /*#__PURE__*/function (FLAG_ID) {
FLAG_ID["CANNOT_PASTE_CONTENT"] = "cannot-paste-content";
return FLAG_ID;
}(FLAG_ID || {});
var transformSyncBlockNode = function transformSyncBlockNode(node, schema, isFromEditor) {
// if copying from renderer, flatten out the content and remove the sync block
if (!isFromEditor) {
return node.content;
}
// sync blocks need a unique localId to function correctly
var newAttrs = _objectSpread(_objectSpread({}, node.attrs), {}, {
localId: _adfSchema.uuid.generate()
});
return schema.nodes.syncBlock.create(newAttrs, null, (0, _toConsumableArray2.default)(node.marks));
};
var transformBodiedSyncBlockNode = function transformBodiedSyncBlockNode(node, isFromEditor) {
// if copying from renderer, flatten out the content and remove the bodied sync block
if (!isFromEditor) {
return node.content;
}
// this is not possible as all bodiedSyncBlocks have already been converted into a syncBlock by now.
return node;
};
var showWarningFlag = function showWarningFlag(_ref) {
var api = _ref.api,
title = _ref.title,
description = _ref.description,
urlText = _ref.urlText,
urlHref = _ref.urlHref;
// Use setTimeout to dispatch transaction in next tick and avoid re-entrant dispatch
setTimeout(function () {
api === null || api === void 0 || api.core.actions.execute(function (_ref2) {
var tr = _ref2.tr;
var flag = {
id: FLAG_ID.CANNOT_PASTE_CONTENT,
description: description,
title: title,
urlText: urlText,
urlHref: urlHref,
type: _pastePluginType.FLAG_TYPE.WARNING
};
return tr.setMeta(_pluginFactory.pluginKey, {
type: _actions.PastePluginActionTypes.SET_ACTIVE_FLAG,
activeFlag: flag
});
});
}, 0);
};
// Check if rawHtml contains a synced block
// example: "<meta charset='utf-8'><html><head></head><body><div data-sync-block=\"\" data-local-id=\"\" data-resource-id=\"d64883c8-1270-431d-a1d3-51d36a1ed5f4\" data-prosemirror-content-type=\"node\" data-prosemirror-node-name=\"syncBlock\" data-prosemirror-node-block=\"true\" data-pm-slice=\"0 0 []\"></div></body></html>"
var hasSyncedBlockInRawHtml = function hasSyncedBlockInRawHtml(rawHtml) {
return Boolean(rawHtml === null || rawHtml === void 0 ? void 0 : rawHtml.includes('data-sync-block="'));
};
/**
* Extracts the source product of a synced block reference from the pasted raw HTML.
*
* A reference block's `data-resource-id` is prefixed with the source product, e.g.
* `confluence-page/5769323474/cdf6a1bc-...`. We only ever emit a controlled,
* enumerated value (`confluence-page` | `jira-work-item`) so the resulting
* analytics attribute can never carry user-generated content. Returns `undefined`
* when the marker is missing or the prefix is unrecognised.
*
* Note: this intentionally mirrors `getSourceProductFromResourceIdSafe` from
* `@atlaskit/editor-synced-block-provider/utils` but is duplicated locally to
* avoid adding a cross-package dependency from the paste plugin onto the
* synced-block provider.
*/
var getSourceProductFromRawHtml = function getSourceProductFromRawHtml(rawHtml) {
// The capture group already constrains the match to exactly these two literals,
// so the assertion is safe. This mirrors `getContentIdAndProductFromResourceId`
// in `@atlaskit/editor-synced-block-provider`.
var match = rawHtml.match(RESOURCE_ID_REGEX);
return match === null || match === void 0 ? void 0 : match[1];
};
/**
* If we are copying from editor, transform the copied source or reference sync block to a new reference sync block
* Otherwise, (e.g. if copying from renderer), flatten out the content and remove the sync block
* Also, show a warning flag if the pasted content contains a synced block and the paste warning options are configured.
*/
var handleSyncBlocksPaste = exports.handleSyncBlocksPaste = function handleSyncBlocksPaste(slice, schema, pasteSource, rawHtml, pasteWarningOptions, api) {
var isFromEditor = pasteSource === 'fabric-editor';
var isSyncedBlockInRawHtml = hasSyncedBlockInRawHtml(rawHtml);
var hasSyncedBlockInSlice = false;
slice = (0, _utils.mapSlice)(slice, function (node) {
if (node.type === schema.nodes.syncBlock) {
hasSyncedBlockInSlice = true;
return transformSyncBlockNode(node, schema, isFromEditor);
} else if (node.type === schema.nodes.bodiedSyncBlock) {
hasSyncedBlockInSlice = true;
return transformBodiedSyncBlockNode(node, isFromEditor);
}
return node;
});
// A synced block reference was on the clipboard (`isSyncedBlockInRawHtml`) but the
// destination schema dropped it (`!hasSyncedBlockInSlice`) — i.e. the user attempted
// to insert a synced block into a surface that does not support synced blocks (e.g.
// Bitbucket). Emit a track event so this can be measured directly instead of relying
// on the "copied-but-never-landed" proxy. See EDITOR-7749.
if (!hasSyncedBlockInSlice && isSyncedBlockInRawHtml) {
var _api$analytics;
api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 || _api$analytics.fireAnalyticsEvent({
eventType: _analytics.EVENT_TYPE.TRACK,
action: _analytics.ACTION.INSERT_ATTEMPTED,
actionSubject: _analytics.ACTION_SUBJECT.SYNCED_BLOCK,
actionSubjectId: _analytics.ACTION_SUBJECT_ID.UNSUPPORTED_SURFACE,
attributes: {
sourceProduct: getSourceProductFromRawHtml(rawHtml),
pasteSource: pasteSource
}
});
if (pasteWarningOptions !== null && pasteWarningOptions !== void 0 && pasteWarningOptions.cannotPasteSyncedBlock) {
var _pasteWarningOptions$, _pasteWarningOptions$2, _pasteWarningOptions$3, _pasteWarningOptions$4;
showWarningFlag({
api: api,
title: pasteWarningOptions === null || pasteWarningOptions === void 0 || (_pasteWarningOptions$ = pasteWarningOptions.cannotPasteSyncedBlock) === null || _pasteWarningOptions$ === void 0 ? void 0 : _pasteWarningOptions$.title,
description: pasteWarningOptions === null || pasteWarningOptions === void 0 || (_pasteWarningOptions$2 = pasteWarningOptions.cannotPasteSyncedBlock) === null || _pasteWarningOptions$2 === void 0 ? void 0 : _pasteWarningOptions$2.description,
urlText: pasteWarningOptions === null || pasteWarningOptions === void 0 || (_pasteWarningOptions$3 = pasteWarningOptions.cannotPasteSyncedBlock) === null || _pasteWarningOptions$3 === void 0 ? void 0 : _pasteWarningOptions$3.urlText,
urlHref: pasteWarningOptions === null || pasteWarningOptions === void 0 || (_pasteWarningOptions$4 = pasteWarningOptions.cannotPasteSyncedBlock) === null || _pasteWarningOptions$4 === void 0 ? void 0 : _pasteWarningOptions$4.urlHref
});
}
}
return slice;
};