Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@ckeditor/ckeditor5-mention

Package Overview
Dependencies
Maintainers
1
Versions
690
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ckeditor/ckeditor5-mention - npm Package Compare versions

Comparing version 10.0.0 to 11.0.0

src/featuredetection.js

19

CHANGELOG.md
Changelog
=========
## [11.0.0](https://github.com/ckeditor/ckeditor5-mention/compare/v10.0.0...v11.0.0) (2019-06-05)
### Bug fixes
* A mention can now be preceded by characters such as brackets, quotes, soft break, etc. Closes [#44](https://github.com/ckeditor/ckeditor5-mention/issues/44). ([86262d1](https://github.com/ckeditor/ckeditor5-mention/commit/86262d1))
* The mention plugin should not throw errors when another `ContextualBalloon` is already visible. Closes [#67](https://github.com/ckeditor/ckeditor5-mention/issues/67). ([de9ee71](https://github.com/ckeditor/ckeditor5-mention/commit/de9ee71))
* The mention panel should have precedence over all other panels. Closes [#74](https://github.com/ckeditor/ckeditor5-mention/issues/74). ([3e8a84c](https://github.com/ckeditor/ckeditor5-mention/commit/3e8a84c))
* The Mention UI will use `ContextualBalloon` plugin to display to prevent balloon collisions with other features. Closes [#27](https://github.com/ckeditor/ckeditor5-mention/issues/27). ([9ae7f30](https://github.com/ckeditor/ckeditor5-mention/commit/9ae7f30))
### Other changes
* Remove unknown stack option from `ContextualBalloon#add()` method call. ([b6a50cf](https://github.com/ckeditor/ckeditor5-mention/commit/b6a50cf))
* Use `Model#insertContent()` instead of `model.Writer#insertText()`. Closes [#69](https://github.com/ckeditor/ckeditor5-mention/issues/69). ([ee973bb](https://github.com/ckeditor/ckeditor5-mention/commit/ee973bb))
### BREAKING CHANGES
* The `MentionUI#panelView` property is removed. The mention feature now uses the `ContextualBalloon` plugin.
## [10.0.0](https://github.com/ckeditor/ckeditor5-mention/tree/v10.0.0) (2019-04-10)
The initial release.

28

package.json
{
"name": "@ckeditor/ckeditor5-mention",
"version": "10.0.0",
"version": "11.0.0",
"description": "Mention feature for CKEditor 5.",

@@ -13,15 +13,19 @@ "keywords": [

"dependencies": {
"@ckeditor/ckeditor5-core": "^12.1.0",
"@ckeditor/ckeditor5-ui": "^12.1.0",
"@ckeditor/ckeditor5-utils": "^12.1.0"
"@ckeditor/ckeditor5-core": "^12.1.1",
"@ckeditor/ckeditor5-ui": "^13.0.0",
"@ckeditor/ckeditor5-utils": "^12.1.1"
},
"devDependencies": {
"@ckeditor/ckeditor5-basic-styles": "^11.1.0",
"@ckeditor/ckeditor5-block-quote": "^11.0.1",
"@ckeditor/ckeditor5-clipboard": "^11.0.1",
"@ckeditor/ckeditor5-editor-classic": "^12.1.0",
"@ckeditor/ckeditor5-engine": "^13.1.0",
"@ckeditor/ckeditor5-font": "^11.1.0",
"@ckeditor/ckeditor5-paragraph": "^11.0.1",
"@ckeditor/ckeditor5-undo": "^11.0.1",
"@ckeditor/ckeditor5-basic-styles": "^11.1.1",
"@ckeditor/ckeditor5-block-quote": "^11.1.0",
"@ckeditor/ckeditor5-clipboard": "^11.0.2",
"@ckeditor/ckeditor5-editor-classic": "^12.1.1",
"@ckeditor/ckeditor5-engine": "^13.1.1",
"@ckeditor/ckeditor5-font": "^11.1.1",
"@ckeditor/ckeditor5-link": "^11.0.2",
"@ckeditor/ckeditor5-paragraph": "^11.0.2",
"@ckeditor/ckeditor5-table": "^13.0.0",
"@ckeditor/ckeditor5-typing": "^12.0.2",
"@ckeditor/ckeditor5-undo": "^11.0.2",
"@ckeditor/ckeditor5-widget": "^11.0.2",
"eslint": "^5.5.0",

@@ -28,0 +32,0 @@ "eslint-config-ckeditor5": "^1.0.11",

@@ -13,4 +13,8 @@ CKEditor 5 mention feature

This package implements mention support for CKEditor 5.
This package implements mention support for CKEditor 5 and brings smart autocompletion based on user input.
## Demo
Check out the demo in the [Mentions (autocomplete) feature](https://ckeditor.com/docs/ckeditor5/latest/features/mentions.html) guide.
## Documentation

@@ -22,2 +26,2 @@

Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html). For full details about the license, please check the `LICENSE.md` file.
Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html). For full details about the license, please check the `LICENSE.md` file or [https://ckeditor.com/legal/ckeditor-oss-license](https://ckeditor.com/legal/ckeditor-oss-license).
/**
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

@@ -30,3 +30,3 @@

*
* // for a viewElement: <span data-mention="@joe">@John Doe</span>
* // For a view element: <span data-mention="@joe">@John Doe</span>
* // it will return:

@@ -74,3 +74,3 @@ * // { id: '@joe', userId: '1234', _uid: '7a7bc7...', _text: '@John Doe' }

* .create( editorElement, {
* mention: ... // Media embed feature options.
* mention: ... // Mention feature options.
* } )

@@ -104,3 +104,3 @@ * .then( ... )

*
* You can provide as many mention feeds but they must use different `marker`s.
* You can provide many mention feeds but they must use different `marker`s.
* For example, you can use `'@'` to autocomplete people and `'#'` to autocomplete tags.

@@ -123,3 +123,3 @@ *

*
* // Simple, synchronous callback.
* // Simple synchronous callback.
* const mentionFeedTags = {

@@ -161,7 +161,7 @@ * marker: '#',

* @property {String} [marker] The character which triggers autocompletion for mention. It must be a single character.
* @property {Array.<module:mention/mention~MentionFeedItem>|Function} feed The autocomplete items. Provide an array for
* @property {Array.<module:mention/mention~MentionFeedItem>|Function} feed Autocomplete items. Provide an array for
* a static configuration (the mention feature will show matching items automatically) or a function which returns an array of
* matching items (directly, or via a promise).
* @property {Number} [minimumCharacters=0] Specifies after how many characters the autocomplete panel should be shown.
* @property {Function} [itemRenderer] Function that renders a {@link module:mention/mention~MentionFeedItem}
* @property {Function} [itemRenderer] A function that renders a {@link module:mention/mention~MentionFeedItem}
* to the autocomplete panel.

@@ -173,3 +173,3 @@ */

*
* When defining a feed item as a plain object, the `id` property is obligatory. The additional properties
* When defining a feed item as a plain object, the `id` property is obligatory. Additional properties
* can be used when customizing the mention feature bahavior

@@ -224,3 +224,3 @@ * (see {@glink features/mentions#customizing-the-autocomplete-list "Customizing the autocomplete list"}

* @typedef {Object|String} module:mention/mention~MentionFeedItem
* @property {String} id Unique id of the mention. It must start with the marker character.
* @property {String} id A unique ID of the mention. It must start with the marker character.
* @property {String} [text] Text inserted into the editor when creating a mention.

@@ -230,3 +230,3 @@ */

/**
* Represents mention in the model.
* Represents a mention in the model.
*

@@ -236,6 +236,7 @@ * See {@link module:mention/mention~Mention#toMentionAttribute `Mention#toMentionAttribute()`}.

* @interface module:mention/mention~MentionAttribute
* @property {String} id Id of a mention – identifies the mention item in mention feed.
* @property {String} _uid Internal mention view item id. Should be passed as an `option.id` when using
* @property {String} id The ID of a mention. It identifies the mention item in the mention feed.
* @property {String} _uid An internal mention view item ID. Should be passed as an `option.id` when using
* {@link module:engine/view/downcastwriter~DowncastWriter#createAttributeElement writer.createAttributeElement()}.
* @property {String} _text Helper property that holds text of inserted mention. Used for detecting broken mention in the editing area.
* @property {String} _text Helper property that stores the text of the inserted mention. Used for detecting a broken mention
* in the editing area.
*/
/**
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

@@ -18,10 +18,10 @@

*
* The command is registered by the {@link module:mention/mentionediting~MentionEditing} as `'mention'`.
* The command is registered by {@link module:mention/mentionediting~MentionEditing} as `'mention'`.
*
* To insert a mention on a range, execute the command and specify a mention object and a range to replace:
* To insert a mention onto a range, execute the command and specify a mention object with a range to replace:
*
* const focus = editor.model.document.selection.focus;
*
* // It will replace one character before selection focus with '#1234' text
* // with mention attribute filled with passed attributes.
* // It will replace one character before the selection focus with the '#1234' text
* // with the mention attribute filled with passed attributes.
* editor.execute( 'mention', {

@@ -37,4 +37,4 @@ * marker: '#',

*
* // It will replace one character before selection focus with 'Teh "Big Foo"' text
* // with attribute filled with passed attributes.
* // It will replace one character before the selection focus with the 'The "Big Foo"' text
* // with the mention attribute filled with passed attributes.
* editor.execute( 'mention', {

@@ -68,8 +68,9 @@ * marker: '#',

* @param {Object} [options] Options for the executed command.
* @param {Object|String} options.mention Mention object to insert. If passed a string it will be used to create a plain object with
* name attribute equal to passed string.
* @param {Object|String} options.mention The mention object to insert. When a string is passed, it will be used to create a plain
* object with the name attribute that equals the passed string.
* @param {String} options.marker The marker character (e.g. `'@'`).
* @param {String} [options.text] The text of inserted mention. Defaults to full mention string composed from `marker` and
* `mention` string or `mention.id` if object is passed.
* @param {String} [options.range] Range to replace. Note that replace range might be shorter then inserted text with mention attribute.
* @param {String} [options.text] The text of the inserted mention. Defaults to the full mention string composed from `marker` and
* `mention` string or `mention.id` if an object is passed.
* @param {String} [options.range] The range to replace. Note that the replaced range might be shorter than the inserted text with the
* mention attribute.
* @fires execute

@@ -108,3 +109,3 @@ */

/**
* The feed item id must start with the marker character.
* The feed item ID must start with the marker character.
*

@@ -142,10 +143,7 @@ * Correct mention feed setting:

// Replace range with a text with mention.
writer.remove( range );
writer.insertText( mentionText, attributesWithMention, range.start );
// Insert space after a mention.
writer.insertText( ' ', currentAttributes, model.document.selection.focus );
// Replace a range with the text with a mention.
model.insertContent( writer.createText( mentionText, attributesWithMention ), range );
model.insertContent( writer.createText( ' ', currentAttributes ), range.start.getShiftedBy( mentionText.length ) );
} );
}
}
/**
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

@@ -40,3 +40,3 @@

// Allow mention attribute on all text nodes.
// Allow the mention attribute on all text nodes.
model.schema.extend( '$text', { allowAttributes: 'mention' } );

@@ -74,3 +74,3 @@

/**
* Creates mention attribute value from provided view element and optional data.
* Creates a mention attribute value from the provided view element and optional data.
*

@@ -103,3 +103,3 @@ * This function is exposed as

// Creates mention element from mention data.
// Creates a mention element from the mention data.
//

@@ -127,7 +127,7 @@ // @param {Object} mention

// Model post-fixer that disallows typing with selection when selection is placed after the text node with mention attribute.
// Model post-fixer that disallows typing with selection when the selection is placed after the text node with the mention attribute.
//
// @param {module:engine/model/writer~Writer} writer
// @param {module:engine/model/document~Document} doc
// @returns {Boolean} Returns true if selection was fixed.
// @returns {Boolean} Returns `true` if the selection was fixed.
function selectionMentionAttributePostFixer( writer, doc ) {

@@ -148,7 +148,7 @@ const selection = doc.selection;

// Model post-fixer that removes mention attribute from modified text node.
// Model post-fixer that removes the mention attribute from the modified text node.
//
// @param {module:engine/model/writer~Writer} writer
// @param {module:engine/model/document~Document} doc
// @returns {Boolean} Returns true if selection was fixed.
// @returns {Boolean} Returns `true` if the selection was fixed.
function removePartialMentionPostFixer( writer, doc, schema ) {

@@ -160,3 +160,3 @@ const changes = doc.differ.getChanges();

for ( const change of changes ) {
// Check text node on current position;
// Checks the text node on the current position.
const position = change.position;

@@ -167,6 +167,6 @@

// Check textNode where the change occurred.
// Checks the text node where the change occurred.
wasChanged = checkAndFix( position.textNode, writer ) || wasChanged;
// Occurs on paste occurs inside a text node with mention.
// Occurs on paste inside a text node with mention.
wasChanged = checkAndFix( nodeAfterInsertedTextNode, writer ) || wasChanged;

@@ -177,3 +177,3 @@ wasChanged = checkAndFix( position.nodeBefore, writer ) || wasChanged;

// Check text nodes in inserted elements (might occur when splitting paragraph or pasting content inside text with mention).
// Checks text nodes in inserted elements (might occur when splitting a paragraph or pasting content inside text with mention).
if ( change.name != '$text' && change.type == 'insert' ) {

@@ -199,7 +199,8 @@ const insertedNode = position.nodeAfter;

// This post-fixer will extend attribute applied on part of a mention so a whole text node of a mention will have added attribute.
// This post-fixer will extend the attribute applied on the part of the mention so the whole text node of the mention will have
// the added attribute.
//
// @param {module:engine/model/writer~Writer} writer
// @param {module:engine/model/document~Document} doc
// @returns {Boolean} Returns true if selection was fixed.
// @returns {Boolean} Returns `true` if the selection was fixed.
function extendAttributeOnMentionPostFixer( writer, doc ) {

@@ -212,5 +213,5 @@ const changes = doc.differ.getChanges();

if ( change.type === 'attribute' && change.attributeKey != 'mention' ) {
// Check node at the left side of a range...
// Checks the node on the left side of the range...
const nodeBefore = change.range.start.nodeBefore;
// ... and on right side of range.
// ... and on the right side of the range.
const nodeAfter = change.range.end.nodeAfter;

@@ -231,6 +232,6 @@

// Checks if node has correct mention attribute if present.
// Returns true if node is text and has a mention attribute which text does not match expected mention text.
// Checks if a node has a correct mention attribute if present.
// Returns `true` if the node is text and has a mention attribute whose text does not match the expected mention text.
//
// @param {module:engine/model/node~Node} node a node to check
// @param {module:engine/model/node~Node} node The node to check.
// @returns {Boolean}

@@ -250,3 +251,3 @@ function isBrokenMentionNode( node ) {

// Fixes mention on text node it needs a fix.
// Fixes a mention on a text node if it needs a fix.
//

@@ -253,0 +254,0 @@ // @param {module:engine/model/text~Text} textNode

/**
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

@@ -13,7 +13,8 @@

import Collection from '@ckeditor/ckeditor5-utils/src/collection';
import BalloonPanelView from '@ckeditor/ckeditor5-ui/src/panel/balloon/balloonpanelview';
import clickOutsideHandler from '@ckeditor/ckeditor5-ui/src/bindings/clickoutsidehandler';
import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
import featureDetection from './featuredetection';
import Rect from '@ckeditor/ckeditor5-utils/src/dom/rect';
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
import ContextualBalloon from '@ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon';

@@ -48,11 +49,4 @@ import TextWatcher from './textwatcher';

/**
* The balloon panel view, containing the mention view.
* The mention view.
*
* @type {module:ui/panel/balloon/balloonpanelview~BalloonPanelView}
*/
this.panelView = this._creatPanelView();
/**
* The mentions view.
*
* @type {module:mention/ui/mentionsview~MentionsView}

@@ -64,3 +58,3 @@ * @private

/**
* Stores mentions feeds configurations.
* Stores mention feeds configurations.
*

@@ -79,7 +73,17 @@ * @type {Map<String, Object>}

init() {
const editor = this.editor;
/**
* The contextual balloon plugin instance.
*
* @private
* @member {module:ui/panel/balloon/contextualballoon~ContextualBalloon}
*/
this._balloon = editor.plugins.get( ContextualBalloon );
// Key listener that handles navigation in mention view.
this.editor.editing.view.document.on( 'keydown', ( evt, data ) => {
if ( isHandledKey( data.keyCode ) && this.panelView.isVisible ) {
editor.editing.view.document.on( 'keydown', ( evt, data ) => {
if ( isHandledKey( data.keyCode ) && this._isUIVisible ) {
data.preventDefault();
evt.stop(); // Required for enter overriding.
evt.stop(); // Required for Enter key overriding.

@@ -99,16 +103,16 @@ if ( data.keyCode == keyCodes.arrowdown ) {

if ( data.keyCode == keyCodes.esc ) {
this._hidePanelAndRemoveMarker();
this._hideUIAndRemoveMarker();
}
}
}, { priority: 'highest' } ); // Required to override enter.
}, { priority: 'highest' } ); // Required to override the Enter key.
// Close the #panelView upon clicking outside of the plugin UI.
// Close the dropdown upon clicking outside of the plugin UI.
clickOutsideHandler( {
emitter: this.panelView,
contextElements: [ this.panelView.element ],
activator: () => this.panelView.isVisible,
callback: () => this._hidePanelAndRemoveMarker()
emitter: this._mentionsView,
activator: () => this._isUIVisible,
contextElements: [ this._balloon.view.element ],
callback: () => this._hideUIAndRemoveMarker()
} );
const feeds = this.editor.config.get( 'mention.feeds' );
const feeds = editor.config.get( 'mention.feeds' );

@@ -132,3 +136,3 @@ for ( const mentionDescription of feeds ) {

*/
throw new CKEditorError( 'mentionconfig-incorrect-marker: The marker must be provided and be a single character.' );
throw new CKEditorError( 'mentionconfig-incorrect-marker: The marker must be provided and it must be a single character.' );
}

@@ -154,20 +158,22 @@

// Destroy created UI components as they are not automatically destroyed (see ckeditor5#1341).
this.panelView.destroy();
this._mentionsView.destroy();
}
/**
* Creates the {@link #panelView}.
* @inheritDoc
*/
static get requires() {
return [ ContextualBalloon ];
}
/**
* Returns true when {@link #_mentionsView} is in the {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon} and it is
* currently visible.
*
* @private
* @returns {module:ui/panel/balloon/balloonpanelview~BalloonPanelView}
* @readonly
* @protected
* @type {Boolean}
*/
_creatPanelView() {
const panelView = new BalloonPanelView( this.editor.locale );
panelView.withArrow = false;
panelView.render();
this.editor.ui.view.body.add( panelView );
return panelView;
get _isUIVisible() {
return this._balloon.visibleView === this._mentionsView;
}

@@ -188,4 +194,2 @@

this.panelView.content.add( mentionsView );
mentionsView.items.bindTo( this._items ).using( data => {

@@ -233,3 +237,3 @@ const { item, marker } = data;

this._hidePanelAndRemoveMarker();
this._hideUIAndRemoveMarker();

@@ -250,3 +254,3 @@ editor.execute( 'mention', {

/**
* Returns item renderer for marker.
* Returns item renderer for the marker.
*

@@ -264,3 +268,3 @@ * @private

/**
* Returns a promise that resolves with autocomplete items for given text.
* Returns a promise that resolves with autocomplete items for a given text.
*

@@ -279,3 +283,3 @@ * @param {String} marker

/**
* Registers a text watcher for marker.
* Registers a text watcher for the marker.
*

@@ -297,6 +301,14 @@ * @private

const hasMention = selection.hasAttribute( 'mention' );
const nodeBefore = selection.focus.nodeBefore;
const focus = selection.focus;
// The text watcher listens only to changed range in selection - so the selection attributes are not yet available
// and you cannot use selection.hasAttribute( 'mention' ) just yet.
// See https://github.com/ckeditor/ckeditor5-engine/issues/1723.
const hasMention = focus.textNode && focus.textNode.hasAttribute( 'mention' );
const nodeBefore = focus.nodeBefore;
if ( hasMention || nodeBefore && nodeBefore.is( 'text' ) && nodeBefore.hasAttribute( 'mention' ) ) {
this._hideUIAndRemoveMarker();
return;

@@ -309,5 +321,5 @@ }

// create marker range
const start = selection.focus.getShiftedBy( -matchedTextLength );
const end = selection.focus.getShiftedBy( -feedText.length );
// Create a marker range.
const start = focus.getShiftedBy( -matchedTextLength );
const end = focus.getShiftedBy( -feedText.length );

@@ -339,5 +351,5 @@ const markerRange = editor.model.createRange( start, end );

if ( this._items.length ) {
this._showPanel( mentionMarker );
this._showUI( mentionMarker );
} else {
this._hidePanelAndRemoveMarker();
this._hideUIAndRemoveMarker();
}

@@ -348,3 +360,3 @@ } );

watcher.on( 'unmatched', () => {
this._hidePanelAndRemoveMarker();
this._hideUIAndRemoveMarker();
} );

@@ -356,3 +368,3 @@

/**
* Returns registered text watcher for marker.
* Returns the registered text watcher for the marker.
*

@@ -370,9 +382,21 @@ * @private

/**
* Shows the {@link #panelView}. If panel is already visible it will reposition it.
* Shows the mentions balloon. If the panel is already visible, it will reposition it.
*
* @private
*/
_showPanel( markerMarker ) {
this.panelView.pin( this._getBalloonPanelPositionData( markerMarker, this.panelView.position ) );
this.panelView.show();
_showUI( markerMarker ) {
if ( this._isUIVisible ) {
// Update balloon position as the mention list view may change its size.
this._balloon.updatePosition( this._getBalloonPanelPositionData( markerMarker, this._mentionsView.position ) );
} else {
this._balloon.add( {
view: this._mentionsView,
position: this._getBalloonPanelPositionData( markerMarker, this._mentionsView.position ),
withArrow: false,
singleViewMode: true
} );
}
this._mentionsView.position = this._balloon.view.position;
this._mentionsView.selectFirst();

@@ -382,7 +406,12 @@ }

/**
* Hides the {@link #panelView} and remove 'mention' marker from markers collection.
* Hides the mentions balloon and removes the 'mention' marker from the markers collection.
*
* @private
*/
_hidePanelAndRemoveMarker() {
_hideUIAndRemoveMarker() {
// Remove the mention view from balloon before removing marker - it is used by balloon position target().
if ( this._balloon.hasView( this._mentionsView ) ) {
this._balloon.remove( this._mentionsView );
}
if ( this.editor.model.markers.has( 'mention' ) ) {

@@ -392,6 +421,5 @@ this.editor.model.change( writer => writer.removeMarker( 'mention' ) );

this.panelView.unpin();
// Make last matched position on panel view undefined so the #_getBalloonPanelPositionData() will return all positions on next call.
this.panelView.position = undefined;
this.panelView.hide();
// Make the last matched position on panel view undefined so the #_getBalloonPanelPositionData() method will return all positions
// on the next call.
this._mentionsView.position = undefined;
}

@@ -438,10 +466,11 @@

/**
* Creates position options object used to position the balloon panel.
* Creates a position options object used to position the balloon panel.
*
* @param {module:engine/model/markercollection~Marker} mentionMarker
* @param {String|undefined} positionName Name of last matched position name.
* @param {String|undefined} preferredPosition The name of the last matched position name.
* @returns {module:utils/dom/position~Options}
* @private
*/
_getBalloonPanelPositionData( mentionMarker, positionName ) {
_getBalloonPanelPositionData( mentionMarker, preferredPosition ) {
const editor = this.editor;
const editing = this.editor.editing;

@@ -453,4 +482,11 @@ const domConverter = editing.view.domConverter;

target: () => {
const viewRange = mapper.toViewRange( mentionMarker.getRange() );
let modelRange = mentionMarker.getRange();
// Target the UI to the model selection range - the marker has been removed so probably the UI will not be shown anyway.
// The logic is used by ContextualBalloon to display another panel in the same place.
if ( modelRange.start.root.rootName == '$graveyard' ) {
modelRange = editor.model.document.selection.getFirstRange();
}
const viewRange = mapper.toViewRange( modelRange );
const rangeRects = Rect.getDomRangeRects( domConverter.viewRangeToDom( viewRange ) );

@@ -471,4 +507,3 @@

},
positions: getBalloonPanelPositions( positionName ),
fitInViewport: true
positions: getBalloonPanelPositions( preferredPosition )
};

@@ -478,8 +513,9 @@ }

// Returns balloon positions data callbacks.
// Returns the balloon positions data callbacks.
//
// @param {String} preferredPosition
// @returns {Array.<module:utils/dom/position~Position>}
function getBalloonPanelPositions( positionName ) {
function getBalloonPanelPositions( preferredPosition ) {
const positions = {
// Positions panel to the south of caret rect.
// Positions the panel to the southeast of the caret rectangle.
'caret_se': targetRect => {

@@ -493,3 +529,3 @@ return {

// Positions panel to the north of caret rect.
// Positions the panel to the northeast of the caret rectangle.
'caret_ne': ( targetRect, balloonRect ) => {

@@ -503,3 +539,3 @@ return {

// Positions panel to the south of caret rect.
// Positions the panel to the southwest of the caret rectangle.
'caret_sw': ( targetRect, balloonRect ) => {

@@ -513,3 +549,3 @@ return {

// Positions panel to the north of caret rect.
// Positions the panel to the northwest of the caret rect.
'caret_nw': ( targetRect, balloonRect ) => {

@@ -524,14 +560,14 @@ return {

// Return only last position if it was matched to prevent panel from jumping after first match.
if ( positions.hasOwnProperty( positionName ) ) {
// Returns only the last position if it was matched to prevent the panel from jumping after the first match.
if ( positions.hasOwnProperty( preferredPosition ) ) {
return [
positions[ positionName ]
positions[ preferredPosition ]
];
}
// As default return all positions callbacks.
// By default return all position callbacks.
return [
positions.caret_se,
positions.caret_sw,
positions.caret_ne,
positions.caret_sw,
positions.caret_nw

@@ -541,20 +577,33 @@ ];

// Creates a regex pattern for marker.
// Creates a RegExp pattern for the marker.
//
// Function has to be exported to achieve 100% code coverage.
//
// @param {String} marker
// @param {Number} minimumCharacters
// @returns {String}
function createPattern( marker, minimumCharacters ) {
// @returns {RegExp}
export function createRegExp( marker, minimumCharacters ) {
const numberOfCharacters = minimumCharacters == 0 ? '*' : `{${ minimumCharacters },}`;
const patternBase = featureDetection.isPunctuationGroupSupported ? '\\p{Ps}\\p{Pi}"\'' : '\\(\\[{"\'';
return `(^| )(\\${ marker })([_a-zA-Z0-9À-ž]${ numberOfCharacters }?)$`;
return new RegExp( buildPattern( patternBase, marker, numberOfCharacters ), 'u' );
}
// Creates a test callback for marker to be used in text watcher instance.
// Helper to build a RegExp pattern string for the marker.
//
// @param {String} whitelistedCharacters
// @param {String} marker
// @param {Number} minimumCharacters
// @returns {String}
function buildPattern( whitelistedCharacters, marker, numberOfCharacters ) {
return `(^|[ ${ whitelistedCharacters }])([${ marker }])([_a-zA-Z0-9À-ž]${ numberOfCharacters }?)$`;
}
// Creates a test callback for the marker to be used in the text watcher instance.
//
// @param {String} marker
// @param {Number} minimumCharacters
// @returns {Function}
function createTestCallback( marker, minimumCharacters ) {
const regExp = new RegExp( createPattern( marker, minimumCharacters ) );
const regExp = createRegExp( marker, minimumCharacters );

@@ -564,3 +613,3 @@ return text => regExp.test( text );

// Creates a text watcher matcher for marker.
// Creates a text matcher from the marker.
//

@@ -570,3 +619,3 @@ // @param {String} marker

function createTextMatcher( marker ) {
const regExp = new RegExp( createPattern( marker, 0 ) );
const regExp = createRegExp( marker, 0 );

@@ -583,7 +632,7 @@ return text => {

// Default feed callback
// The default feed callback.
function createFeedCallback( feedItems ) {
return feedText => {
const filteredItems = feedItems
// Make default mention feed case-insensitive.
// Make the default mention feed case-insensitive.
.filter( item => {

@@ -603,3 +652,3 @@ // Item might be defined as object.

// Checks if given key code is handled by the mention ui.
// Checks if a given key code is handled by the mention UI.
//

@@ -606,0 +655,0 @@ // @param {Number}

/**
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

@@ -14,6 +14,6 @@

/**
* Text watcher feature.
* The text watcher feature.
*
* Fires {@link module:mention/textwatcher~TextWatcher#event:matched matched} and
* {@link module:mention/textwatcher~TextWatcher#event:unmatched unmatched} events on typing or selection changes.
* Fires {@link module:mention/textwatcher~TextWatcher#event:matched `matched`} and
* {@link module:mention/textwatcher~TextWatcher#event:unmatched `unmatched`} events on typing or selection changes.
*

@@ -26,4 +26,4 @@ * @private

* @param {module:core/editor/editor~Editor} editor
* @param {Function} testCallback Function used to match the text.
* @param {Function} textMatcherCallback Function used to process matched text.
* @param {Function} testCallback The function used to match the text.
* @param {Function} textMatcherCallback The function used to process matched text.
*/

@@ -41,3 +41,3 @@ constructor( editor, testCallback, textMatcherCallback ) {

/**
* Last matched text.
* The last matched text.
*

@@ -51,3 +51,3 @@ * @property {String}

/**
* Starts listening the editor for typing & selection events.
* Starts listening to the editor for typing and selection events.
*

@@ -59,4 +59,4 @@ * @private

editor.model.document.selection.on( 'change', ( evt, { directChange } ) => {
// The indirect changes (ie on typing) are handled in document's change event.
editor.model.document.selection.on( 'change:range', ( evt, { directChange } ) => {
// Indirect changes (i.e. when the user types or external changes are applied) are handled in the document's change event.
if ( !directChange ) {

@@ -93,3 +93,3 @@ return;

/**
* Fired whenever text doesn't match anymore. Fired only when text matcher was matched.
* Fired whenever the text does not match anymore. Fired only when the text watcher found a match.
*

@@ -107,3 +107,3 @@ * @event unmatched

/**
* Fired whenever text matcher was matched.
* Fired whenever the text watcher found a match.
*

@@ -119,3 +119,3 @@ * @event matched

*
* @returns {String|undefined} Text from block or undefined if selection is not collapsed.
* @returns {String|undefined} The text from the block or undefined if the selection is not collapsed.
* @private

@@ -125,5 +125,6 @@ */

const editor = this.editor;
const selection = editor.model.document.selection;
const model = editor.model;
const selection = model.document.selection;
// Do nothing if selection is not collapsed.
// Do nothing if the selection is not collapsed.
if ( !selection.isCollapsed ) {

@@ -133,5 +134,5 @@ return;

const block = selection.focus.parent;
const rangeBeforeSelection = model.createRange( model.createPositionAt( selection.focus.parent, 0 ), selection.focus );
return _getText( editor.model.createRangeIn( block ) ).slice( 0, selection.focus.offset );
return _getText( rangeBeforeSelection );
}

@@ -141,3 +142,3 @@ }

/**
* Returns whole text from given range by adding all data from text nodes together.
* Returns the whole text from a given range by adding all data from the text nodes together.
*

@@ -149,3 +150,10 @@ * @protected

export function _getText( range ) {
return Array.from( range.getItems() ).reduce( ( a, b ) => a + b.data, '' );
return Array.from( range.getItems() ).reduce( ( rangeText, node ) => {
if ( node.is( 'softBreak' ) ) {
// Trim text to softBreak
return '';
}
return rangeText + node.data;
}, '' );
}

@@ -152,0 +160,0 @@

/**
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

@@ -5,0 +5,0 @@

/**
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

@@ -5,0 +5,0 @@

/**
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

@@ -88,9 +88,9 @@

const item = this.items.get( indexToGet );
item.highlight();
// Scroll the mentions view to the selected element.
if ( !this._isItemVisibleInScrolledArea( item ) ) {
this.element.scrollTop = item.element.offsetTop;
// Return early if item is already selected.
if ( this.selected === item ) {
return;
}
// Remove highlight of previously selected item.
if ( this.selected ) {

@@ -100,3 +100,9 @@ this.selected.removeHighlight();

item.highlight();
this.selected = item;
// Scroll the mentions view to the selected element.
if ( !this._isItemVisibleInScrolledArea( item ) ) {
this.element.scrollTop = item.element.offsetTop;
}
}

@@ -103,0 +109,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc