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);