add a tokenizer so choices can be automatically created and selected while user is typing or pasting into the field. fixes #101 #81 #292
This commit is contained in:
parent
c2fa04529b
commit
289226cf6c
92
select2.js
92
select2.js
@ -416,6 +416,59 @@
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default tokenizer. This function uses breaks the input on substring match of any string from the
|
||||||
|
* opts.tokenSeparators array and uses opts.createSearchChoice to create the choice object. Both of those
|
||||||
|
* two options have to be defined in order for the tokenizer to work.
|
||||||
|
*
|
||||||
|
* @param input text user has typed so far or pasted into the search field
|
||||||
|
* @param selection currently selected choices
|
||||||
|
* @param selectCallback function(choice) callback tho add the choice to selection
|
||||||
|
* @param opts select2's opts
|
||||||
|
* @return undefined/null to leave the current input unchanged, or a string to change the input to the returned value
|
||||||
|
*/
|
||||||
|
function defaultTokenizer(input, selection, selectCallback, opts) {
|
||||||
|
var original = input, // store the original so we can compare and know if we need to tell the search to update its text
|
||||||
|
dupe = false, // check for whether a token we extracted represents a duplicate selected choice
|
||||||
|
token, // token
|
||||||
|
index, // position at which the separator was found
|
||||||
|
i, l, // looping variables
|
||||||
|
separator; // the matched separator
|
||||||
|
|
||||||
|
if (!opts.createSearchChoice || !opts.tokenSeparators || opts.tokenSeparators.length < 1) return undefined;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
index = -1;
|
||||||
|
|
||||||
|
for (i = 0, l = opts.tokenSeparators.length; i < l; i++) {
|
||||||
|
separator = opts.tokenSeparators[i];
|
||||||
|
index = input.indexOf(separator);
|
||||||
|
if (index >= 0) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < 0) break; // did not find any token separator in the input string, bail
|
||||||
|
|
||||||
|
token = input.substring(0, index);
|
||||||
|
input = input.substring(index + separator.length);
|
||||||
|
|
||||||
|
if (token.length > 0) {
|
||||||
|
token = opts.createSearchChoice(token, selection);
|
||||||
|
if (token !== undefined && token !== null && opts.id(token) !== undefined && opts.id(token) !== null) {
|
||||||
|
dupe = false;
|
||||||
|
for (i = 0, l = selection.length; i < l; i++) {
|
||||||
|
if (equal(opts.id(token), opts.id(selection[i]))) {
|
||||||
|
dupe = true; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dupe) selectCallback(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (original.localeCompare(input) != 0) return input;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* blurs any Select2 container that has focus when an element outside them was clicked or received focus
|
* blurs any Select2 container that has focus when an element outside them was clicked or received focus
|
||||||
*
|
*
|
||||||
@ -599,9 +652,6 @@
|
|||||||
this.select = select = opts.element;
|
this.select = select = opts.element;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Custom tags separator.
|
|
||||||
opts.separator = opts.separator || ",";
|
|
||||||
|
|
||||||
if (select) {
|
if (select) {
|
||||||
// these options are not allowed when attached to a select because they are picked up off the element itself
|
// these options are not allowed when attached to a select because they are picked up off the element itself
|
||||||
$.each(["id", "multiple", "ajax", "query", "createSearchChoice", "initSelection", "data", "tags"], function () {
|
$.each(["id", "multiple", "ajax", "query", "createSearchChoice", "initSelection", "data", "tags"], function () {
|
||||||
@ -1074,12 +1124,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default tokenizer function which does nothing
|
||||||
|
*/
|
||||||
|
tokenize: function() {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param initial whether or not this is the call to this method right after the dropdown has been opened
|
* @param initial whether or not this is the call to this method right after the dropdown has been opened
|
||||||
*/
|
*/
|
||||||
// abstract
|
// abstract
|
||||||
updateResults: function (initial) {
|
updateResults: function (initial) {
|
||||||
var search = this.search, results = this.results, opts = this.opts, data, self=this;
|
var search = this.search, results = this.results, opts = this.opts, data, self=this, input;
|
||||||
|
|
||||||
// if the search is currently hidden we do not alter the results
|
// if the search is currently hidden we do not alter the results
|
||||||
if (initial !== true && (this.showSearchInput === false || !this.opened())) {
|
if (initial !== true && (this.showSearchInput === false || !this.opened())) {
|
||||||
@ -1115,6 +1172,12 @@
|
|||||||
render("<li class='select2-searching'>" + opts.formatSearching() + "</li>");
|
render("<li class='select2-searching'>" + opts.formatSearching() + "</li>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// give the tokenizer a chance to pre-process the input
|
||||||
|
input = this.tokenize();
|
||||||
|
if (input != undefined && input != null) {
|
||||||
|
search.val(input);
|
||||||
|
}
|
||||||
|
|
||||||
this.resultsPage = 1;
|
this.resultsPage = 1;
|
||||||
opts.query({
|
opts.query({
|
||||||
term: search.val(),
|
term: search.val(),
|
||||||
@ -1889,6 +1952,18 @@
|
|||||||
self.postprocessResults();
|
self.postprocessResults();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
tokenize: function() {
|
||||||
|
var input = this.search.val();
|
||||||
|
input = this.opts.tokenizer(input, this.data(), this.bind(this.onSelect), this.opts);
|
||||||
|
if (input != null && input != undefined) {
|
||||||
|
this.search.val(input);
|
||||||
|
if (input.length > 0) {
|
||||||
|
this.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
// multi
|
// multi
|
||||||
onSelect: function (data) {
|
onSelect: function (data) {
|
||||||
this.addSelectedChoice(data);
|
this.addSelectedChoice(data);
|
||||||
@ -1898,10 +1973,9 @@
|
|||||||
this.close();
|
this.close();
|
||||||
this.search.width(10);
|
this.search.width(10);
|
||||||
} else {
|
} else {
|
||||||
|
if (this.countSelectableResults()>0) {
|
||||||
this.search.width(10);
|
this.search.width(10);
|
||||||
this.resizeSearch();
|
this.resizeSearch();
|
||||||
|
|
||||||
if (this.countSelectableResults()>0) {
|
|
||||||
this.positionDropdown();
|
this.positionDropdown();
|
||||||
} else {
|
} else {
|
||||||
// if nothing left to select close
|
// if nothing left to select close
|
||||||
@ -2032,7 +2106,6 @@
|
|||||||
containerLeft = this.selection.offset().left;
|
containerLeft = this.selection.offset().left;
|
||||||
|
|
||||||
searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding;
|
searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding;
|
||||||
|
|
||||||
if (searchWidth < minimumWidth) {
|
if (searchWidth < minimumWidth) {
|
||||||
searchWidth = maxWidth - sideBorderPadding;
|
searchWidth = maxWidth - sideBorderPadding;
|
||||||
}
|
}
|
||||||
@ -2232,7 +2305,10 @@
|
|||||||
id: function (e) { return e.id; },
|
id: function (e) { return e.id; },
|
||||||
matcher: function(term, text) {
|
matcher: function(term, text) {
|
||||||
return text.toUpperCase().indexOf(term.toUpperCase()) >= 0;
|
return text.toUpperCase().indexOf(term.toUpperCase()) >= 0;
|
||||||
}
|
},
|
||||||
|
separator: ",",
|
||||||
|
tokenSeparators: [],
|
||||||
|
tokenizer: defaultTokenizer
|
||||||
};
|
};
|
||||||
|
|
||||||
// exports
|
// exports
|
||||||
|
Loading…
Reference in New Issue
Block a user