UNPKG

@atlaskit/editor-core

Version:

A package contains Atlassian editor core functionality

187 lines 7.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = require("tslib"); var prosemirror_1 = require("../../prosemirror"); var utils_1 = require("../utils"); var utils_2 = require("../../utils"); var matcher; function inputRulePlugin(schema, providerFactory) { if (schema.nodes.emoji && providerFactory) { initMatcher(providerFactory); var asciiEmojiRule = utils_1.createInputRule(AsciiEmojiMatcher.REGEX, inputRuleHandler); return prosemirror_1.inputRules({ rules: [asciiEmojiRule] }); } } exports.inputRulePlugin = inputRulePlugin; function initMatcher(providerFactory) { var handleProvider = function (name, provider) { if (!provider) { return; } provider.then(function (emojiProvider) { emojiProvider.getAsciiMap().then(function (map) { matcher = new RecordingAsciiEmojiMatcher(emojiProvider, map); }); }); }; providerFactory.subscribe('emojiProvider', handleProvider); } function inputRuleHandler(state, matchParts, start, end) { if (!matcher) { return undefined; } if (!isEnabled(state)) { return undefined; } var match = matcher.match(matchParts); if (match) { var transactionCreator = new AsciiEmojiTransactionCreator(state, match, start, end); return transactionCreator.create(); } return undefined; } function isEnabled(state) { var emojiQuery = state.schema.marks.emojiQuery; var isEmojiQueryActive = state.selection.$from.marks().some(function (mark) { return mark.type === emojiQuery; }); return isEmojiQueryActive || utils_2.isMarkTypeAllowedAtCurrentPosition(emojiQuery, state); } var AsciiEmojiMatcher = (function () { function AsciiEmojiMatcher(asciiToEmojiMap) { this.asciiToEmojiMap = asciiToEmojiMap; } AsciiEmojiMatcher.prototype.match = function (matchParts) { var emoji = this.getEmoji(matchParts); if (emoji) { return { emoji: emoji, leadingString: AsciiEmojiMatcher.getLeadingString(matchParts), trailingString: AsciiEmojiMatcher.getTrailingString(matchParts), }; } }; AsciiEmojiMatcher.prototype.getEmoji = function (match) { var ascii = match[AsciiEmojiMatcher.REGEX_EMOJI_ASCII_CAPTURE_INDEX].trim(); if (ascii) { return this.asciiToEmojiMap.get(ascii); } return undefined; }; AsciiEmojiMatcher.getLeadingString = function (match) { return match[AsciiEmojiMatcher.REGEX_LEADING_CAPTURE_INDEX]; }; AsciiEmojiMatcher.getTrailingString = function (match) { return match[AsciiEmojiMatcher.REGEX_TRAILING_CAPTURE_INDEX] || ''; }; return AsciiEmojiMatcher; }()); /** * This regex matches 2 scenarios: * 1. an emoticon starting with a colon character (e.g. :D => 😃) * 2. an emoticon not starting with a colon character (e.g. 8-D => 😎) * * Explanation (${leafNodeReplacementCharacter} is replaced with character \ufffc) * * 1st Capturing Group ((?:^|[\s\ufffc])(?:\(*?)) * Non-capturing group (?:^|[\s\ufffc]) * 1st Alternative ^ * ^ asserts position at start of the string * 2nd Alternative [\s\ufffc] * matches a single character present in [\s\ufffc] * Non-capturing group (?:\(*?) * matches the character ( literally between zero and unlimited times, as few times as possible, expanding as needed (lazy) * 2nd Capturing Group (\(y\)|[^:\s\ufffc\(]\S{1,3}|:\S{1,3}( )) * 1st Alternative \(y\) * matches the string (y) literally (case sensitive) * 2nd Alternative [^:\s\ufffc\(]\S{1,3} * matches a single character not present in [^:\s\ufffc\(] between 1 and 3 times, as many times as possible, giving back as needed (greedy) * 3rd Alternative :\S{1,3}( ) * : matches the character : literally * \S{1,3} matches any non-whitespace character between 1 and 3 times, as many times as possible, giving back as needed (greedy) * 3rd Capturing Group ( ) * matches the character [space] literally * $ asserts position at the end of the string * * See https://regex101.com/r/HRS9O2/2 */ AsciiEmojiMatcher.REGEX = new RegExp("((?:^|[\\s" + utils_1.leafNodeReplacementCharacter + "])(?:\\(*?))(\\(y\\)|[^:\\s" + utils_1.leafNodeReplacementCharacter + "\\(]\\S{1,3}|:\\S{1,3}( ))$"); AsciiEmojiMatcher.REGEX_LEADING_CAPTURE_INDEX = 1; AsciiEmojiMatcher.REGEX_EMOJI_ASCII_CAPTURE_INDEX = 2; AsciiEmojiMatcher.REGEX_TRAILING_CAPTURE_INDEX = 3; /** * A matcher that will record ascii matches as usages of the matched emoji. */ var RecordingAsciiEmojiMatcher = (function (_super) { tslib_1.__extends(RecordingAsciiEmojiMatcher, _super); function RecordingAsciiEmojiMatcher(emojiProvider, asciiToEmojiMap) { var _this = _super.call(this, asciiToEmojiMap) || this; _this.emojiProvider = emojiProvider; return _this; } RecordingAsciiEmojiMatcher.prototype.match = function (matchParts) { var match = _super.prototype.match.call(this, matchParts); if (match && this.emojiProvider.recordSelection) { this.emojiProvider.recordSelection(match.emoji); } return match; }; return RecordingAsciiEmojiMatcher; }(AsciiEmojiMatcher)); var AsciiEmojiTransactionCreator = (function () { function AsciiEmojiTransactionCreator(state, match, start, end) { this.state = state; this.match = match; this.start = start; this.end = end; } AsciiEmojiTransactionCreator.prototype.create = function () { return this.state.tr.replaceWith(this.from, this.to, this.createNodes()); }; Object.defineProperty(AsciiEmojiTransactionCreator.prototype, "from", { get: function () { return this.start + this.match.leadingString.length; }, enumerable: true, configurable: true }); Object.defineProperty(AsciiEmojiTransactionCreator.prototype, "to", { get: function () { return this.end; }, enumerable: true, configurable: true }); AsciiEmojiTransactionCreator.prototype.createNodes = function () { var nodes = [this.createEmojiNode()]; if (this.trailingTextNodeRequired()) { nodes.push(this.createTrailingTextNode()); } return nodes; }; AsciiEmojiTransactionCreator.prototype.createEmojiNode = function () { var emojiTypeNode = this.state.schema.nodes.emoji; return emojiTypeNode.create(this.getEmojiNodeAttrs()); }; AsciiEmojiTransactionCreator.prototype.getEmojiNodeAttrs = function () { var emoji = this.match.emoji; return { id: emoji.id, shortName: emoji.shortName, text: emoji.fallback || emoji.shortName }; }; AsciiEmojiTransactionCreator.prototype.trailingTextNodeRequired = function () { return this.match.trailingString.length > 0; }; AsciiEmojiTransactionCreator.prototype.createTrailingTextNode = function () { return this.state.schema.text(this.match.trailingString); }; return AsciiEmojiTransactionCreator; }()); exports.stateKey = new prosemirror_1.PluginKey('asciiEmojiPlugin'); var plugins = function (schema, providerFactory) { return [inputRulePlugin(schema, providerFactory)].filter(function (plugin) { return !!plugin; }); }; exports.default = plugins; //# sourceMappingURL=ascii-input-rules.js.map