From 1a734e930c260ebf6bbf68de02650c51a79e0082 Mon Sep 17 00:00:00 2001 From: Greg Leppert Date: Wed, 21 Apr 2010 15:02:27 -0500 Subject: [PATCH 1/4] Adding support for multiple values triggered by a separator option --- ui/jquery.ui.autocomplete.js | 37 ++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/ui/jquery.ui.autocomplete.js b/ui/jquery.ui.autocomplete.js index 070045d16ab..593421b676c 100644 --- a/ui/jquery.ui.autocomplete.js +++ b/ui/jquery.ui.autocomplete.js @@ -17,7 +17,8 @@ $.widget( "ui.autocomplete", { options: { minLength: 1, - delay: 300 + delay: 300, + separator: false }, _create: function() { var self = this, @@ -108,14 +109,14 @@ $.widget( "ui.autocomplete", { if ( false !== self._trigger( "focus", null, { item: item } ) ) { // use value to match what will end up in the input, if it was a key event if ( /^key/.test(event.originalEvent.type) ) { - self.element.val( item.value ); + self.element.val( self.options.separator ? self._insertValue(item.value) : item.value ); } } }, selected: function( event, ui ) { var item = ui.item.data( "item.autocomplete" ); if ( false !== self._trigger( "select", event, { item: item } ) ) { - self.element.val( item.value ); + self.element.val( self.options.separator ? self._insertValue(item.value) : item.value ); } self.close( event ); // only trigger when focus was lost (click on menu) @@ -198,7 +199,35 @@ $.widget( "ui.autocomplete", { // always save the actual value, not the one passed as an argument .val(); - this.source( { term: value }, this.response ); + if( this.options.separator ){ + value = this._getValue( value ); + } + if( value ){ + this.source( { term: value }, this.response ); + } + }, + + _getValue: function( value ){ + this.caretPos = this.element[0].selectionStart; + var begin = value.substr( 0, this.caretPos ).split( this.options.separator ), + end = value.substr( this.caretPos ).split( this.options.separator ); + return begin[ begin.length-1 ] + end[0]; + }, + + _insertValue: function( value ){ + var begin = this.term.substr( 0, this.caretPos ).split( this.options.separator ), + end = this.term.substr( this.caretPos ).split( this.options.separator ), + result = ''; + begin.pop(); + end.shift(); + if(begin.length){ + result = begin.join( this.options.separator ) + this.options.separator; + } + result += value; + if(end.length){ + result += this.options.separator + end.join( this.options.separator ); + } + return result; }, _response: function( content ) { From 50288dbe459cfe3f19be916941e6a8ca0128b37e Mon Sep 17 00:00:00 2001 From: Greg Leppert Date: Wed, 21 Apr 2010 16:07:42 -0500 Subject: [PATCH 2/4] Code cleanup for autocomplete multiple word support --- ui/jquery.ui.autocomplete.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ui/jquery.ui.autocomplete.js b/ui/jquery.ui.autocomplete.js index 593421b676c..ed03c511faf 100644 --- a/ui/jquery.ui.autocomplete.js +++ b/ui/jquery.ui.autocomplete.js @@ -208,7 +208,13 @@ $.widget( "ui.autocomplete", { }, _getValue: function( value ){ + //find the caret this.caretPos = this.element[0].selectionStart; + /* + * cut the string in half at the carot, split at the separators, + * and then concat the outter array values so that editing the + * middle of a string still correctly triggers the autocomplete + */ var begin = value.substr( 0, this.caretPos ).split( this.options.separator ), end = value.substr( this.caretPos ).split( this.options.separator ); return begin[ begin.length-1 ] + end[0]; @@ -218,8 +224,11 @@ $.widget( "ui.autocomplete", { var begin = this.term.substr( 0, this.caretPos ).split( this.options.separator ), end = this.term.substr( this.caretPos ).split( this.options.separator ), result = ''; + + //clean the arrays of empty entries begin.pop(); end.shift(); + if(begin.length){ result = begin.join( this.options.separator ) + this.options.separator; } From 0cbd58a63e2d6479a4c0d3326382654b611f2b25 Mon Sep 17 00:00:00 2001 From: Greg Leppert Date: Wed, 21 Apr 2010 17:22:28 -0500 Subject: [PATCH 3/4] Integrated caret code from a previous jQuery UI mask plugin --- ui/jquery.ui.autocomplete.js | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/ui/jquery.ui.autocomplete.js b/ui/jquery.ui.autocomplete.js index ed03c511faf..7b575f684b8 100644 --- a/ui/jquery.ui.autocomplete.js +++ b/ui/jquery.ui.autocomplete.js @@ -208,10 +208,9 @@ $.widget( "ui.autocomplete", { }, _getValue: function( value ){ - //find the caret - this.caretPos = this.element[0].selectionStart; + this.caretPos = $.ui.autocomplete.caret(this.element).end; /* - * cut the string in half at the carot, split at the separators, + * cut the string in half at the caret, split at the separators, * and then concat the outter array values so that editing the * middle of a string still correctly triggers the autocomplete */ @@ -348,6 +347,34 @@ $.extend( $.ui.autocomplete, { return $.grep( array, function(value) { return matcher.test( value.label || value.value || value ); }); + }, + caret: function(element, begin, end) { //Helper Function for Caret positioning taken from http://dev.jqueryui.com/browser/branches/dev/mask/ui/ui.mask.js?rev=2371 + var input = element[0]; + if (typeof begin == 'number') { + end = (typeof end == 'number') ? end : begin; + if (input.setSelectionRange) { + input.focus(); + input.setSelectionRange(begin, end); + } else if (input.createTextRange) { + var range = input.createTextRange(); + range.collapse(true); + range.moveEnd('character', end); + range.moveStart('character', begin); + range.select(); + } + return element; + } else { + if (input.setSelectionRange) { + begin = input.selectionStart; + end = input.selectionEnd; + } + else if (document.selection && document.selection.createRange) { + var range = document.selection.createRange(); + begin = 0 - range.duplicate().moveStart('character', -100000); + end = begin + range.text.length; + } + return { begin: begin, end: end }; + } } }); From 2722c91177038e6e43e3ca0deb4274de7aa7413f Mon Sep 17 00:00:00 2001 From: Greg Leppert Date: Wed, 21 Apr 2010 18:08:44 -0500 Subject: [PATCH 4/4] Stored the beginning and end of the autocomplete term so that it isnt parsed at every event --- ui/jquery.ui.autocomplete.js | 64 +++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/ui/jquery.ui.autocomplete.js b/ui/jquery.ui.autocomplete.js index 7b575f684b8..1a38814569a 100644 --- a/ui/jquery.ui.autocomplete.js +++ b/ui/jquery.ui.autocomplete.js @@ -109,14 +109,26 @@ $.widget( "ui.autocomplete", { if ( false !== self._trigger( "focus", null, { item: item } ) ) { // use value to match what will end up in the input, if it was a key event if ( /^key/.test(event.originalEvent.type) ) { - self.element.val( self.options.separator ? self._insertValue(item.value) : item.value ); + if(self.options.separator){ + var val = self._insertValue(item.value); + self.element.val( val ); + $.ui.autocomplete.caret( self.element, val.length - self.valEndLength ); + } else { + self.element.val( item.value ); + } } } }, selected: function( event, ui ) { var item = ui.item.data( "item.autocomplete" ); if ( false !== self._trigger( "select", event, { item: item } ) ) { - self.element.val( self.options.separator ? self._insertValue(item.value) : item.value ); + if(self.options.separator){ + var val = self._insertValue(item.value); + self.element.val( val ); + $.ui.autocomplete.caret( self.element, val.length - self.valEndLength ); + } else { + self.element.val( item.value ); + } } self.close( event ); // only trigger when focus was lost (click on menu) @@ -200,40 +212,48 @@ $.widget( "ui.autocomplete", { .val(); if( this.options.separator ){ - value = this._getValue( value ); + var caretPos = $.ui.autocomplete.caret(this.element).end; + value = this._getValue( value, caretPos ); + this._storeTermBits( caretPos ); } if( value ){ this.source( { term: value }, this.response ); } }, - _getValue: function( value ){ - this.caretPos = $.ui.autocomplete.caret(this.element).end; + _getValue: function( value, caretPos ){ /* - * cut the string in half at the caret, split at the separators, + * Cut the string in half at the caret, split at the separators, * and then concat the outter array values so that editing the * middle of a string still correctly triggers the autocomplete - */ - var begin = value.substr( 0, this.caretPos ).split( this.options.separator ), - end = value.substr( this.caretPos ).split( this.options.separator ); - return begin[ begin.length-1 ] + end[0]; + */ + var begin = value.substr( 0, caretPos ).split( this.options.separator ), + end = value.substr( caretPos ); + this.valEndLength = end.length; + + return begin[ begin.length-1 ] + end.split( this.options.separator )[0]; }, - _insertValue: function( value ){ - var begin = this.term.substr( 0, this.caretPos ).split( this.options.separator ), - end = this.term.substr( this.caretPos ).split( this.options.separator ), - result = ''; - + _storeTermBits: function( caretPos ){ + /* + * Store the beginning and end of the original term so that + * the values can quickly be swapped in and out in _insertValue + */ + this.termBegin = this.term.substr( 0, caretPos ).split( this.options.separator ); + this.termEnd = this.term.substr( caretPos ).split( this.options.separator ); //clean the arrays of empty entries - begin.pop(); - end.shift(); - - if(begin.length){ - result = begin.join( this.options.separator ) + this.options.separator; + this.termBegin.pop(); + this.termEnd.shift(); + }, + + _insertValue: function( value ){ + var result = ''; + if( this.termBegin.length ){ + result = this.termBegin.join( this.options.separator ) + this.options.separator; } result += value; - if(end.length){ - result += this.options.separator + end.join( this.options.separator ); + if( this.termEnd.length ){ + result += this.options.separator + this.termEnd.join( this.options.separator ); } return result; },