2015-08-17 17:00:26 -07:00
/ * *
* @ file
* Autocomplete based on jQuery UI .
* /
( function ( $ , Drupal ) {
2015-10-21 21:44:50 -07:00
'use strict' ;
2015-08-17 17:00:26 -07:00
var autocomplete ;
/ * *
* Helper splitting terms from the autocomplete value .
*
* @ function Drupal . autocomplete . splitValues
*
* @ param { string } value
*
* @ return { Array }
* /
function autocompleteSplitValues ( value ) {
// We will match the value against comma-separated terms.
var result = [ ] ;
var quote = false ;
var current = '' ;
var valueLength = value . length ;
var character ;
for ( var i = 0 ; i < valueLength ; i ++ ) {
character = value . charAt ( i ) ;
if ( character === '"' ) {
current += character ;
quote = ! quote ;
}
else if ( character === ',' && ! quote ) {
result . push ( current . trim ( ) ) ;
current = '' ;
}
else {
current += character ;
}
}
if ( value . length > 0 ) {
result . push ( $ . trim ( current ) ) ;
}
return result ;
}
/ * *
* Returns the last value of an multi - value textfield .
*
* @ function Drupal . autocomplete . extractLastTerm
*
* @ param { string } terms
*
* @ return { string }
* /
function extractLastTerm ( terms ) {
return autocomplete . splitValues ( terms ) . pop ( ) ;
}
/ * *
* The search handler is called before a search is performed .
*
* @ function Drupal . autocomplete . options . search
*
* @ param { object } event
*
* @ return { bool }
* /
function searchHandler ( event ) {
var options = autocomplete . options ;
var term = autocomplete . extractLastTerm ( event . target . value ) ;
// Abort search if the first character is in firstCharacterBlacklist.
if ( term . length > 0 && options . firstCharacterBlacklist . indexOf ( term [ 0 ] ) !== - 1 ) {
return false ;
}
// Only search when the term is at least the minimum length.
return term . length >= options . minLength ;
}
/ * *
* JQuery UI autocomplete source callback .
*
* @ param { object } request
* @ param { function } response
* /
function sourceData ( request , response ) {
var elementId = this . element . attr ( 'id' ) ;
if ( ! ( elementId in autocomplete . cache ) ) {
autocomplete . cache [ elementId ] = { } ;
}
/ * *
* Filter through the suggestions removing all terms already tagged and
* display the available terms to the user .
*
* @ param { object } suggestions
* /
function showSuggestions ( suggestions ) {
var tagged = autocomplete . splitValues ( request . term ) ;
var il = tagged . length ;
for ( var i = 0 ; i < il ; i ++ ) {
var index = suggestions . indexOf ( tagged [ i ] ) ;
if ( index >= 0 ) {
suggestions . splice ( index , 1 ) ;
}
}
response ( suggestions ) ;
}
/ * *
* Transforms the data object into an array and update autocomplete results .
*
* @ param { object } data
* /
function sourceCallbackHandler ( data ) {
autocomplete . cache [ elementId ] [ term ] = data ;
// Send the new string array of terms to the jQuery UI list.
showSuggestions ( data ) ;
}
// Get the desired term and construct the autocomplete URL for it.
var term = autocomplete . extractLastTerm ( request . term ) ;
// Check if the term is already cached.
if ( autocomplete . cache [ elementId ] . hasOwnProperty ( term ) ) {
showSuggestions ( autocomplete . cache [ elementId ] [ term ] ) ;
}
else {
var options = $ . extend ( { success : sourceCallbackHandler , data : { q : term } } , autocomplete . ajax ) ;
$ . ajax ( this . element . attr ( 'data-autocomplete-path' ) , options ) ;
}
}
/ * *
* Handles an autocompletefocus event .
*
* @ return { bool }
* /
function focusHandler ( ) {
return false ;
}
/ * *
* Handles an autocompleteselect event .
*
* @ param { jQuery . Event } event
* @ param { object } ui
*
* @ return { bool }
* /
function selectHandler ( event , ui ) {
var terms = autocomplete . splitValues ( event . target . value ) ;
// Remove the current input.
terms . pop ( ) ;
// Add the selected item.
2015-10-21 21:44:50 -07:00
if ( ui . item . value . search ( ',' ) > 0 ) {
2015-08-17 17:00:26 -07:00
terms . push ( '"' + ui . item . value + '"' ) ;
}
else {
terms . push ( ui . item . value ) ;
}
event . target . value = terms . join ( ', ' ) ;
// Return false to tell jQuery UI that we've filled in the value already.
return false ;
}
/ * *
* Override jQuery UI _renderItem function to output HTML by default .
*
* @ param { object } ul
* @ param { object } item
*
* @ return { object }
* /
function renderItem ( ul , item ) {
2015-10-21 21:44:50 -07:00
return $ ( '<li>' )
. append ( $ ( '<a>' ) . html ( item . label ) )
2015-08-17 17:00:26 -07:00
. appendTo ( ul ) ;
}
/ * *
* Attaches the autocomplete behavior to all required fields .
*
* @ type { Drupal ~ behavior }
* /
Drupal . behaviors . autocomplete = {
attach : function ( context ) {
// Act on textfields with the "form-autocomplete" class.
var $autocomplete = $ ( context ) . find ( 'input.form-autocomplete' ) . once ( 'autocomplete' ) ;
if ( $autocomplete . length ) {
// Allow options to be overriden per instance.
var blacklist = $autocomplete . attr ( 'data-autocomplete-first-character-blacklist' ) ;
$ . extend ( autocomplete . options , {
firstCharacterBlacklist : ( blacklist ) ? blacklist : ''
} ) ;
// Use jQuery UI Autocomplete on the textfield.
$autocomplete . autocomplete ( autocomplete . options )
2015-10-21 21:44:50 -07:00
. data ( 'ui-autocomplete' )
2015-08-17 17:00:26 -07:00
. _renderItem = autocomplete . options . renderItem ;
}
} ,
detach : function ( context , settings , trigger ) {
if ( trigger === 'unload' ) {
$ ( context ) . find ( 'input.form-autocomplete' )
. removeOnce ( 'autocomplete' )
. autocomplete ( 'destroy' ) ;
}
}
} ;
/ * *
* Autocomplete object implementation .
*
* @ namespace Drupal . autocomplete
* /
autocomplete = {
cache : { } ,
// Exposes options to allow overriding by contrib.
splitValues : autocompleteSplitValues ,
extractLastTerm : extractLastTerm ,
// jQuery UI autocomplete options.
/ * *
* JQuery UI option object .
*
* @ name Drupal . autocomplete . options
* /
options : {
source : sourceData ,
focus : focusHandler ,
search : searchHandler ,
select : selectHandler ,
renderItem : renderItem ,
minLength : 1 ,
// Custom options, used by Drupal.autocomplete.
firstCharacterBlacklist : ''
} ,
ajax : {
dataType : 'json'
}
} ;
Drupal . autocomplete = autocomplete ;
} ) ( jQuery , Drupal ) ;