CKEDITOR.config.mentions = [ { feed: function( options, callback ) { var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if ( xhr.readyState == 4 ) { if ( xhr.status == 200 ) { callback( JSON.parse( this.responseText ) ); } else { callback( [] ); } } } xhr.open( 'GET', '/ajax/searchmember?search=' + encodeURIComponent( options.query ) ); xhr.send(); }, itemTemplate: '
  • ' + '{fullname}
    ' + '@{username}' + '
  • ', outputTemplate: '@{username} ', minChars: 1 } ]; ( function() { CKEDITOR._.mentions = { cache: {} }; var MARKER = '@', MIN_CHARS = 0, cache = CKEDITOR._.mentions.cache; CKEDITOR.plugins.add( 'member', { requires: 'autocomplete,textmatch,ajax', instances: [], init: function( editor ) { var self = this; editor.on( 'instanceReady', function() { CKEDITOR.tools.array.forEach( editor.config.mentions || [], function( config ) { self.instances.push( new Mentions( editor, config ) ); } ); } ); } } ); function Mentions( editor, config ) { var feed = config.feed; this.caseSensitive = config.caseSensitive; this.marker = config.hasOwnProperty( 'marker' ) ? config.marker : MARKER; this.minChars = config.minChars !== null && config.minChars !== undefined ? config.minChars : MIN_CHARS; this.pattern = config.pattern || createPattern( this.marker, this.minChars ); this.cache = config.cache !== undefined ? config.cache : true; this.throttle = config.throttle !== undefined ? config.throttle : 200; this._autocomplete = new CKEDITOR.plugins.autocomplete( editor, { textTestCallback: getTextTestCallback( this.marker, this.minChars, this.pattern ), dataCallback: getDataCallback( feed, this ), itemTemplate: config.itemTemplate, outputTemplate: config.outputTemplate, throttle: this.throttle, itemsLimit: config.itemsLimit } ); } Mentions.prototype = { /** * Destroys the mentions instance. * * The view element and event listeners will be removed from the DOM. */ destroy: function() { this._autocomplete.destroy(); } }; function createPattern( marker, minChars ) { minChars=0; // Match also diacritic characters (#2491). var pattern = '\\' + marker + '[_a-zA-Z0-9À-žА-Яа-я.]'; if ( minChars ) { pattern += '{' + minChars + ',}'; } else { pattern += '*'; } pattern += '$'; return new RegExp( pattern ); } function getTextTestCallback( marker, minChars, pattern ) { return function( range ) { if ( !range.collapsed ) { return null; } return CKEDITOR.plugins.textMatch.match( range, matchCallback ); }; function matchCallback( text, offset ) { var match = text.slice( 0, offset ) .match( pattern ); if ( !match ) { return null; } // Do not proceed if a query is a part of word. var prevChar = text[ match.index - 1]; if ( prevChar !== undefined && !prevChar.match( /\s+/ ) ) { return null; } return { start: match.index, end: offset }; } } function getDataCallback( feed, mentions ) { return function( matchInfo, callback ) { var query = matchInfo.query; // We are removing marker here to give clean query result for the endpoint callback. if ( mentions.marker ) { query = query.substring( mentions.marker.length ); } if ( CKEDITOR.tools.array.isArray( feed ) ) { createArrayFeed(); } else if ( typeof feed === 'string' ) { createUrlFeed(); } else { feed( { query: query, marker: mentions.marker }, resolveCallbackData ); } function createArrayFeed() { var data = indexArrayFeed( feed ).filter( function( item ) { var itemName = item.name; if ( !mentions.caseSensitive ) { itemName = itemName.toLowerCase(); query = query.toLowerCase(); } return itemName.indexOf( query ) === 0; } ); resolveCallbackData( data ); } function indexArrayFeed( feed ) { var index = 1; return CKEDITOR.tools.array.reduce( feed, function( current, name ) { current.push( { name: name, id: index++ } ); return current; }, [] ); } function createUrlFeed() { var encodedUrl = new CKEDITOR.template( feed ) .output( { encodedQuery: encodeURIComponent( query ) } ); if ( mentions.cache && cache[ encodedUrl ] ) { return resolveCallbackData( cache[ encodedUrl ] ); } CKEDITOR.ajax.load( encodedUrl, function( data ) { var items = JSON.parse( data ); // Cache URL responses for performance improvement (#1969). if ( mentions.cache && items !== null ) { cache[ encodedUrl ] = items; } resolveCallbackData( items ); } ); } function resolveCallbackData( data ) { if ( !data ) { return; } // We don't want to change item data, so lets create new one. var newData = CKEDITOR.tools.array.map( data, function( item ) { var name = mentions.marker + item.name; return CKEDITOR.tools.object.merge( item, { name: name } ); } ); callback( newData ); } }; } CKEDITOR.plugins.mentions = Mentions; } )(jQuery);