UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

225 lines (219 loc) • 8.34 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.findFilepaths = exports.canLinkBeCreatedInRange = exports.LinkMatcher = exports.FILEPATH_REGEXP = exports.DONTLINKIFY_REGEXP = void 0; exports.getLinkCreationAnalyticsEvent = getLinkCreationAnalyticsEvent; exports.getLinkDomain = getLinkDomain; exports.isFromCurrentDomain = isFromCurrentDomain; exports.isLinkInMatches = void 0; exports.linkifyContent = linkifyContent; exports.normalizeUrl = normalizeUrl; var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _adfSchema = require("@atlaskit/adf-schema"); var _platformFeatureFlags = require("@atlaskit/platform-feature-flags"); var _types = require("../analytics/types"); var _shouldAutoLinkifyTld = require("./should-auto-linkify-tld"); var _slice = require("./slice"); // File has been copied to packages/editor/editor-plugin-ai/src/provider/markdown-transformer/utils/hyperlink.ts // If changes are made to this file, please make the same update in the linked file. // Regular expression for a windows filepath in the format <DRIVE LETTER>:\<folder name>\ var FILEPATH_REGEXP = exports.FILEPATH_REGEXP = /([a-zA-Z]:|\\)([^\/:*?<>"|]+\\)?([^\/:*?<>"| ]+(?=\s?))?/gim; // Don't linkify if starts with $ or { var DONTLINKIFY_REGEXP = exports.DONTLINKIFY_REGEXP = /^(\$|{)/; /** * Instance of class LinkMatcher are used in autoformatting in place of Regex. * Hence it has been made similar to regex with an exec method. * Extending it directly from class Regex was introducing some issues, thus that has been avoided. */ var LinkMatcher = exports.LinkMatcher = /*#__PURE__*/function () { function LinkMatcher() { (0, _classCallCheck2.default)(this, LinkMatcher); } (0, _createClass2.default)(LinkMatcher, null, [{ key: "create", value: function create() { var LinkMatcherRegex = /*#__PURE__*/function () { function LinkMatcherRegex() { (0, _classCallCheck2.default)(this, LinkMatcherRegex); } (0, _createClass2.default)(LinkMatcherRegex, [{ key: "exec", value: function exec(str) { var stringsBySpace = str.slice(0, str.length - 1).split(' '); var lastStringBeforeSpace = stringsBySpace[stringsBySpace.length - 1]; var isLastStringValid = lastStringBeforeSpace.length > 0; if (!str.endsWith(' ') || !isLastStringValid) { return null; } if (DONTLINKIFY_REGEXP.test(lastStringBeforeSpace)) { return null; } var links = _adfSchema.linkify.match(lastStringBeforeSpace); if (!links || links.length === 0) { return null; } var lastMatch = links[links.length - 1]; var lastLink = links[links.length - 1]; lastLink.input = str.substring(lastMatch.index); lastLink.length = lastLink.lastIndex - lastLink.index + 1; lastLink.index = str.lastIndexOf(lastStringBeforeSpace) + lastMatch.index; return lastLink; } }]); return LinkMatcherRegex; }(); return new LinkMatcherRegex(); } }]); return LinkMatcher; }(); /** * Adds protocol to url if needed. */ function normalizeUrl(url) { if (!url) { return ''; } if ((0, _adfSchema.isSafeUrl)(url)) { return url; } return (0, _adfSchema.normalizeUrl)(url); } /** * Linkify content in a slice (eg. after a rich text paste) */ function linkifyContent(schema) { return function (slice) { return (0, _slice.mapSlice)(slice, function (node, parent) { var isAllowedInParent = !parent || parent.type !== schema.nodes.codeBlock; var link = node.type.schema.marks.link; if (link === undefined) { throw new Error('Link not in schema - unable to linkify content'); } if (isAllowedInParent && node.isText && !link.isInSet(node.marks)) { var linkified = []; var text = node.text; var matches = (0, _platformFeatureFlags.getBooleanFF)('platform.linking-platform.prevent-suspicious-linkification') ? findLinkMatches(text).filter(function (match) { return (0, _shouldAutoLinkifyTld.shouldAutoLinkifyTld)(match.title); }) : findLinkMatches(text); var pos = 0; var filepaths = findFilepaths(text); matches.forEach(function (match) { if (isLinkInMatches(match.start, filepaths)) { return; } if (match.start > 0) { linkified.push(node.cut(pos, match.start)); } linkified.push(node.cut(match.start, match.end).mark(link.create({ href: normalizeUrl(match.href) }).addToSet(node.marks))); pos = match.end; }); if (pos < text.length) { linkified.push(node.cut(pos)); } return linkified; } return node; }); }; } function getLinkDomain(url) { // Remove protocol and www., if either exists var withoutProtocol = url.toLowerCase().replace(/^(.*):\/\//, ''); var withoutWWW = withoutProtocol.replace(/^(www\.)/, ''); // Remove port, fragment, path, query string return withoutWWW.replace(/[:\/?#](.*)$/, ''); } function isFromCurrentDomain(url) { if (!window || !window.location) { return false; } var currentDomain = window.location.hostname; var linkDomain = getLinkDomain(url); return currentDomain === linkDomain; } function findLinkMatches(text) { var matches = []; var linkMatches = text && _adfSchema.linkify.match(text); if (linkMatches && linkMatches.length > 0) { linkMatches.forEach(function (match) { matches.push({ start: match.index, end: match.lastIndex, title: match.raw, href: match.url }); }); } return matches; } var findFilepaths = exports.findFilepaths = function findFilepaths(text) { var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; // Creation of a copy of the RegExp is necessary as lastIndex is stored on it when we run .exec() var localRegExp = new RegExp(FILEPATH_REGEXP); var match; var matchesList = []; var maxFilepathSize = 260; while ((match = localRegExp.exec(text)) !== null) { var start = match.index + offset; var end = localRegExp.lastIndex + offset; if (end - start > maxFilepathSize) { end = start + maxFilepathSize; } // We don't care about big strings of text that are pretending to be filepaths!! matchesList.push({ startIndex: start, endIndex: end }); } return matchesList; }; var isLinkInMatches = exports.isLinkInMatches = function isLinkInMatches(linkStart, matchesList) { for (var i = 0; i < matchesList.length; i++) { if (linkStart >= matchesList[i].startIndex && linkStart < matchesList[i].endIndex) { return true; } } return false; }; function getLinkCreationAnalyticsEvent(inputMethod, url) { return { action: _types.ACTION.INSERTED, actionSubject: _types.ACTION_SUBJECT.DOCUMENT, actionSubjectId: _types.ACTION_SUBJECT_ID.LINK, attributes: { inputMethod: inputMethod, fromCurrentDomain: isFromCurrentDomain(url) }, eventType: _types.EVENT_TYPE.TRACK, nonPrivacySafeAttributes: { linkDomain: getLinkDomain(url) } }; } var canLinkBeCreatedInRange = exports.canLinkBeCreatedInRange = function canLinkBeCreatedInRange(from, to) { return function (state) { if (!state.doc.rangeHasMark(from, to, state.schema.marks.link)) { var $from = state.doc.resolve(from); var $to = state.doc.resolve(to); var link = state.schema.marks.link; if ($from.parent === $to.parent && $from.parent.isTextblock) { if ($from.parent.type.allowsMarkType(link)) { var allowed = true; state.doc.nodesBetween(from, to, function (node) { allowed = allowed && !node.marks.some(function (m) { return m.type.excludes(link); }); return allowed; }); return allowed; } } } return false; }; };