diff --git a/component.json b/component.json
index 29f0ada9..d81f1438 100644
--- a/component.json
+++ b/component.json
@@ -1,6 +1,6 @@
{
"name" : "jquery-tokeninput",
- "version" : "1.7.1",
+ "version" : "1.7.2",
"description" : "Tokeninput is a jQuery plugin which allows your users to select multiple items from a predefined list, using autocompletion as they type to find each item.",
"main" : [ "./src/jquery.tokeninput.js", "./styles/token-input.css" ],
"homepage" : "http://loopj.com/jquery-tokeninput",
diff --git a/package.json b/package.json
index d8e84fc5..d78c89cf 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "jquery-tokeninput",
- "version": "2.0.1",
+ "version": "1.7.3",
"devDependencies": {
"grunt": "~0.4.2",
"grunt-contrib-jshint": "~0.6.3",
diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js
index 3e9cf519..22f54149 100755
--- a/src/jquery.tokeninput.js
+++ b/src/jquery.tokeninput.js
@@ -1,6 +1,6 @@
/*
* jQuery Plugin: Tokenizing Autocomplete Text Entry
- * Version 1.6.2
+ * Version 1.7.2
*
* Copyright (c) 2009 James Smith (http://loopj.com)
* Licensed jointly under the GPL and MIT licenses,
@@ -8,1100 +8,1103 @@
*
*/
;(function ($) {
- var DEFAULT_SETTINGS = {
- // Search settings
- method: "GET",
- queryParam: "q",
- searchDelay: 300,
- minChars: 1,
- caching: true,
- propertyToSearch: "name",
- jsonContainer: null,
- contentType: "json",
- excludeCurrent: false,
- excludeCurrentParameter: "x",
-
- // Prepopulation settings
- prePopulate: null,
- processPrePopulate: false,
-
- // Display settings
- hintText: "Type in a search term",
- noResultsText: "No results",
- searchingText: "Searching...",
- deleteText: "×",
- animateDropdown: true,
- placeholder: null,
- theme: null,
- zindex: 999,
- resultsLimit: null,
-
- enableHTML: false,
-
- resultsFormatter: function(item) {
- var string = item[this.propertyToSearch];
- return "
" + (this.enableHTML ? string : _escapeHTML(string)) + "";
- },
-
- tokenFormatter: function(item) {
- var string = item[this.propertyToSearch];
- return "" + (this.enableHTML ? string : _escapeHTML(string)) + "
";
- },
-
- // Tokenization settings
- tokenLimit: null,
- tokenDelimiter: ",",
- preventDuplicates: false,
- tokenValue: "id",
-
- // Behavioral settings
- allowFreeTagging: false,
- allowTabOut: false,
- autoSelectFirstResult: false,
-
- // Callbacks
- onResult: null,
- onCachedResult: null,
- onAdd: null,
- onFreeTaggingAdd: null,
- onDelete: null,
- onReady: null,
-
- // Other settings
- idPrefix: "token-input-",
-
- // Keep track if the input is currently in disabled mode
- disabled: false
- };
-
- // Default classes to use when theming
- var DEFAULT_CLASSES = {
- tokenList : "token-input-list",
- token : "token-input-token",
- tokenReadOnly : "token-input-token-readonly",
- tokenDelete : "token-input-delete-token",
- selectedToken : "token-input-selected-token",
- highlightedToken : "token-input-highlighted-token",
- dropdown : "token-input-dropdown",
- dropdownItem : "token-input-dropdown-item",
- dropdownItem2 : "token-input-dropdown-item2",
- selectedDropdownItem : "token-input-selected-dropdown-item",
- inputToken : "token-input-input-token",
- focused : "token-input-focused",
- disabled : "token-input-disabled"
- };
-
- // Input box position "enum"
- var POSITION = {
- BEFORE : 0,
- AFTER : 1,
- END : 2
- };
-
- // Keys "enum"
- var KEY = {
- BACKSPACE : 8,
- TAB : 9,
- ENTER : 13,
- ESCAPE : 27,
- SPACE : 32,
- PAGE_UP : 33,
- PAGE_DOWN : 34,
- END : 35,
- HOME : 36,
- LEFT : 37,
- UP : 38,
- RIGHT : 39,
- DOWN : 40,
- NUMPAD_ENTER : 108,
- COMMA : 188
- };
-
- var HTML_ESCAPES = {
- '&' : '&',
- '<' : '<',
- '>' : '>',
- '"' : '"',
- "'" : ''',
- '/' : '/'
- };
-
- var HTML_ESCAPE_CHARS = /[&<>"'\/]/g;
-
- function coerceToString(val) {
- return String((val === null || val === undefined) ? '' : val);
- }
-
- function _escapeHTML(text) {
- return coerceToString(text).replace(HTML_ESCAPE_CHARS, function(match) {
- return HTML_ESCAPES[match];
- });
- }
-
- // Additional public (exposed) methods
- var methods = {
- init: function(url_or_data_or_function, options) {
- var settings = $.extend({}, DEFAULT_SETTINGS, options || {});
-
- return this.each(function () {
- $(this).data("settings", settings);
- $(this).data("tokenInputObject", new $.TokenList(this, url_or_data_or_function, settings));
- });
- },
- clear: function() {
- this.data("tokenInputObject").clear();
- return this;
- },
- add: function(item) {
- this.data("tokenInputObject").add(item);
- return this;
- },
- remove: function(item) {
- this.data("tokenInputObject").remove(item);
- return this;
- },
- get: function() {
- return this.data("tokenInputObject").getTokens();
- },
- toggleDisabled: function(disable) {
- this.data("tokenInputObject").toggleDisabled(disable);
- return this;
- },
- setOptions: function(options){
- $(this).data("settings", $.extend({}, $(this).data("settings"), options || {}));
- return this;
- },
- destroy: function () {
- if (this.data("tokenInputObject")) {
- this.data("tokenInputObject").clear();
- var tmpInput = this;
- var closest = this.parent();
- closest.empty();
- tmpInput.show();
- closest.append(tmpInput);
- return tmpInput;
+ var DEFAULT_SETTINGS = {
+ // Search settings
+ method: "GET",
+ queryParam: "q",
+ searchDelay: 300,
+ minChars: 1,
+ caching: true,
+ propertyToSearch: "name",
+ jsonContainer: null,
+ contentType: "json",
+ excludeCurrent: false,
+ excludeCurrentParameter: "x",
+
+ // Prepopulation settings
+ prePopulate: null,
+ processPrePopulate: false,
+
+ // Display settings
+ hintText: "Type in a search term",
+ noResultsText: "No results",
+ searchingText: "Searching...",
+ deleteText: "×",
+ animateDropdown: true,
+ placeholder: null,
+ theme: null,
+ zindex: 999,
+ resultsLimit: null,
+
+ enableHTML: false,
+
+ resultsFormatter: function(item) {
+ var string = item[this.propertyToSearch];
+ return "" + (this.enableHTML ? string : _escapeHTML(string)) + "";
+ },
+
+ tokenFormatter: function(item) {
+ var string = item[this.propertyToSearch];
+ return "" + (this.enableHTML ? string : _escapeHTML(string)) + "
";
+ },
+
+ // Tokenization settings
+ tokenLimit: null,
+ tokenDelimiter: ",",
+ preventDuplicates: false,
+ tokenValue: "id",
+
+ // Behavioral settings
+ allowFreeTagging: false,
+ allowTabOut: false,
+ autoSelectFirstResult: false,
+
+ // Callbacks
+ onResult: null,
+ onCachedResult: null,
+ onAdd: null,
+ onFreeTaggingAdd: null,
+ onDelete: null,
+ onReady: null,
+
+ // Other settings
+ idPrefix: "token-input-",
+
+ // Keep track if the input is currently in disabled mode
+ disabled: false
+ };
+
+ // Default classes to use when theming
+ var DEFAULT_CLASSES = {
+ tokenList : "token-input-list",
+ token : "token-input-token",
+ tokenReadOnly : "token-input-token-readonly",
+ tokenDelete : "token-input-delete-token",
+ selectedToken : "token-input-selected-token",
+ highlightedToken : "token-input-highlighted-token",
+ dropdown : "token-input-dropdown",
+ dropdownItem : "token-input-dropdown-item",
+ dropdownItem2 : "token-input-dropdown-item2",
+ selectedDropdownItem : "token-input-selected-dropdown-item",
+ inputToken : "token-input-input-token",
+ focused : "token-input-focused",
+ disabled : "token-input-disabled"
+ };
+
+ // Input box position "enum"
+ var POSITION = {
+ BEFORE : 0,
+ AFTER : 1,
+ END : 2
+ };
+
+ // Keys "enum"
+ var KEY = {
+ BACKSPACE : 8,
+ TAB : 9,
+ ENTER : 13,
+ ESCAPE : 27,
+ SPACE : 32,
+ PAGE_UP : 33,
+ PAGE_DOWN : 34,
+ END : 35,
+ HOME : 36,
+ LEFT : 37,
+ UP : 38,
+ RIGHT : 39,
+ DOWN : 40,
+ NUMPAD_ENTER : 108,
+ COMMA : 188
+ };
+
+ var HTML_ESCAPES = {
+ '&' : '&',
+ '<' : '<',
+ '>' : '>',
+ '"' : '"',
+ "'" : ''',
+ '/' : '/'
+ };
+
+ var HTML_ESCAPE_CHARS = /[&<>"'\/]/g;
+
+ function coerceToString(val) {
+ return String((val === null || val === undefined) ? '' : val);
+ }
+
+ function _escapeHTML(text) {
+ return coerceToString(text).replace(HTML_ESCAPE_CHARS, function(match) {
+ return HTML_ESCAPES[match];
+ });
+ }
+
+ // Additional public (exposed) methods
+ var methods = {
+ init: function(url_or_data_or_function, options) {
+ var settings = $.extend({}, DEFAULT_SETTINGS, options || {});
+
+ return this.each(function () {
+ $(this).data("settings", settings);
+ $(this).data("tokenInputObject", new $.TokenList(this, url_or_data_or_function, settings));
+ });
+ },
+ clear: function() {
+ this.data("tokenInputObject").clear();
+ return this;
+ },
+ add: function(item) {
+ this.data("tokenInputObject").add(item);
+ return this;
+ },
+ remove: function(item) {
+ this.data("tokenInputObject").remove(item);
+ return this;
+ },
+ get: function() {
+ return this.data("tokenInputObject").getTokens();
+ },
+ toggleDisabled: function(disable) {
+ this.data("tokenInputObject").toggleDisabled(disable);
+ return this;
+ },
+ setOptions: function(options){
+ $(this).data("settings", $.extend({}, $(this).data("settings"), options || {}));
+ return this;
+ },
+ destroy: function () {
+ if (this.data("tokenInputObject")) {
+ this.data("tokenInputObject").clear();
+ var tmpInput = this;
+ var closest = this.parent();
+ closest.empty();
+ tmpInput.show();
+ closest.append(tmpInput);
+ return tmpInput;
+ }
}
- }
- };
-
- // Expose the .tokenInput function to jQuery as a plugin
- $.fn.tokenInput = function (method) {
- // Method calling and initialization logic
- if (methods[method]) {
- return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
- } else {
- return methods.init.apply(this, arguments);
- }
- };
-
- // TokenList class for each input
- $.TokenList = function (input, url_or_data, settings) {
- //
- // Initialization
- //
-
- // Configure the data source
- if (typeof(url_or_data) === "string" || typeof(url_or_data) === "function") {
- // Set the url to query against
- $(input).data("settings").url = url_or_data;
-
- // If the URL is a function, evaluate it here to do our initalization work
- var url = computeURL();
-
- // Make a smart guess about cross-domain if it wasn't explicitly specified
- if ($(input).data("settings").crossDomain === undefined && typeof url === "string") {
- if(url.indexOf("://") === -1) {
- $(input).data("settings").crossDomain = false;
- } else {
- $(input).data("settings").crossDomain = (location.href.split(/\/+/g)[1] !== url.split(/\/+/g)[1]);
- }
- }
- } else if (typeof(url_or_data) === "object") {
- // Set the local data to search through
- $(input).data("settings").local_data = url_or_data;
- }
-
- // Build class names
- if($(input).data("settings").classes) {
- // Use custom class names
- $(input).data("settings").classes = $.extend({}, DEFAULT_CLASSES, $(input).data("settings").classes);
- } else if($(input).data("settings").theme) {
- // Use theme-suffixed default class names
- $(input).data("settings").classes = {};
- $.each(DEFAULT_CLASSES, function(key, value) {
- $(input).data("settings").classes[key] = value + "-" + $(input).data("settings").theme;
- });
- } else {
- $(input).data("settings").classes = DEFAULT_CLASSES;
- }
-
- // Save the tokens
- var saved_tokens = [];
-
- // Keep track of the number of tokens in the list
- var token_count = 0;
-
- // Basic cache to save on db hits
- var cache = new $.TokenList.Cache();
-
- // Keep track of the timeout, old vals
- var timeout;
- var input_val;
-
- // Create a new text input an attach keyup events
- var input_box = $("")
- .css({
- outline: "none"
- })
- .attr("id", $(input).data("settings").idPrefix + input.id)
- .focus(function () {
- if ($(input).data("settings").disabled) {
- return false;
- } else
- if ($(input).data("settings").tokenLimit === null || $(input).data("settings").tokenLimit !== token_count) {
- show_dropdown_hint();
- }
- token_list.addClass($(input).data("settings").classes.focused);
- })
- .blur(function () {
- hide_dropdown();
-
- if ($(input).data("settings").allowFreeTagging) {
- add_freetagging_tokens();
- }
-
- $(this).val("");
- token_list.removeClass($(input).data("settings").classes.focused);
- })
- .bind("keyup keydown blur update", resize_input)
- .keydown(function (event) {
- var previous_token;
- var next_token;
-
- switch(event.keyCode) {
- case KEY.LEFT:
- case KEY.RIGHT:
- case KEY.UP:
- case KEY.DOWN:
- if(this.value.length === 0) {
- previous_token = input_token.prev();
- next_token = input_token.next();
+ };
+
+ // Expose the .tokenInput function to jQuery as a plugin
+ $.fn.tokenInput = function (method) {
+ // Method calling and initialization logic
+ if (methods[method]) {
+ return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+ } else {
+ return methods.init.apply(this, arguments);
+ }
+ };
+
+ // TokenList class for each input
+ $.TokenList = function (input, url_or_data, settings) {
+ //
+ // Initialization
+ //
+
+ // Configure the data source
+ if (typeof(url_or_data) === "string" || typeof(url_or_data) === "function") {
+ // Set the url to query against
+ $(input).data("settings").url = url_or_data;
+
+ // If the URL is a function, evaluate it here to do our initalization work
+ var url = computeURL();
+
+ // Make a smart guess about cross-domain if it wasn't explicitly specified
+ if ($(input).data("settings").crossDomain === undefined && typeof url === "string") {
+ if(url.indexOf("://") === -1) {
+ $(input).data("settings").crossDomain = false;
+ } else {
+ $(input).data("settings").crossDomain = (location.href.split(/\/+/g)[1] !== url.split(/\/+/g)[1]);
+ }
+ }
+ } else if (typeof(url_or_data) === "object") {
+ // Set the local data to search through
+ $(input).data("settings").local_data = url_or_data;
+ }
+
+ // Build class names
+ if($(input).data("settings").classes) {
+ // Use custom class names
+ $(input).data("settings").classes = $.extend({}, DEFAULT_CLASSES, $(input).data("settings").classes);
+ } else if($(input).data("settings").theme) {
+ // Use theme-suffixed default class names
+ $(input).data("settings").classes = {};
+ $.each(DEFAULT_CLASSES, function(key, value) {
+ $(input).data("settings").classes[key] = value + "-" + $(input).data("settings").theme;
+ });
+ } else {
+ $(input).data("settings").classes = DEFAULT_CLASSES;
+ }
+
+ // Save the tokens
+ var saved_tokens = [];
+
+ // Keep track of the number of tokens in the list
+ var token_count = 0;
+
+ // Basic cache to save on db hits
+ var cache = new $.TokenList.Cache();
+
+ // Keep track of the timeout, old vals
+ var timeout;
+ var input_val;
+
+ // Create a new text input an attach keyup events
+ var input_box = $("")
+ .css({
+ outline: "none"
+ })
+ .attr("id", $(input).data("settings").idPrefix + input.id)
+ .focus(function () {
+ if ($(input).data("settings").disabled) {
+ return false;
+ } else
+ if ($(input).data("settings").tokenLimit === null || $(input).data("settings").tokenLimit !== token_count) {
+ show_dropdown_hint();
+ }
+ token_list.addClass($(input).data("settings").classes.focused);
+ })
+ .blur(function () {
+ hide_dropdown();
+
+ if ($(input).data("settings").allowFreeTagging) {
+ add_freetagging_tokens();
+ }
+
+ $(this).val("");
+ token_list.removeClass($(input).data("settings").classes.focused);
+ })
+ .bind("keyup keydown blur update", resize_input)
+ .keydown(function (event) {
+ var previous_token;
+ var next_token;
+
+ switch(event.keyCode) {
+ case KEY.LEFT:
+ case KEY.RIGHT:
+ case KEY.UP:
+ case KEY.DOWN:
+ if(this.value.length === 0) {
+ previous_token = input_token.prev();
+ next_token = input_token.next();
+
+ if((previous_token.length && previous_token.get(0) === selected_token) ||
+ (next_token.length && next_token.get(0) === selected_token)) {
+ // Check if there is a previous/next token and it is selected
+ if(event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) {
+ deselect_token($(selected_token), POSITION.BEFORE);
+ } else {
+ deselect_token($(selected_token), POSITION.AFTER);
+ }
+ } else if((event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) && previous_token.length) {
+ // We are moving left, select the previous token if it exists
+ select_token($(previous_token.get(0)));
+ } else if((event.keyCode === KEY.RIGHT || event.keyCode === KEY.DOWN) && next_token.length) {
+ // We are moving right, select the next token if it exists
+ select_token($(next_token.get(0)));
+ }
+ } else {
+ var dropdown_item = null;
+
+ if (event.keyCode === KEY.DOWN || event.keyCode === KEY.RIGHT) {
+ dropdown_item = $(dropdown).find('li').first();
- if((previous_token.length && previous_token.get(0) === selected_token) ||
- (next_token.length && next_token.get(0) === selected_token)) {
- // Check if there is a previous/next token and it is selected
- if(event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) {
- deselect_token($(selected_token), POSITION.BEFORE);
+ if (selected_dropdown_item) {
+ dropdown_item = $(selected_dropdown_item).next();
+ }
} else {
- deselect_token($(selected_token), POSITION.AFTER);
+ dropdown_item = $(dropdown).find('li').last();
+
+ if (selected_dropdown_item) {
+ dropdown_item = $(selected_dropdown_item).prev();
+ }
}
- } else if((event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) && previous_token.length) {
- // We are moving left, select the previous token if it exists
- select_token($(previous_token.get(0)));
- } else if((event.keyCode === KEY.RIGHT || event.keyCode === KEY.DOWN) && next_token.length) {
- // We are moving right, select the next token if it exists
- select_token($(next_token.get(0)));
+
+ select_dropdown_item(dropdown_item);
}
- } else {
- var dropdown_item = null;
- if (event.keyCode === KEY.DOWN || event.keyCode === KEY.RIGHT) {
- dropdown_item = $(dropdown).find('li').first();
+ break;
+
+ case KEY.BACKSPACE:
+ previous_token = input_token.prev();
+
+ if (this.value.length === 0) {
+ if (selected_token) {
+ delete_token($(selected_token));
+ hiddenInput.change();
+ } else if(previous_token.length) {
+ select_token($(previous_token.get(0)));
+ }
+
+ return false;
+ } else if($(this).val().length === 1) {
+ hide_dropdown();
+ } else {
+ // set a timeout just long enough to let this function finish.
+ setTimeout(function(){ do_search(); }, 5);
+ }
+ break;
+
+ case KEY.TAB:
+ case KEY.ENTER:
+ case KEY.NUMPAD_ENTER:
+ case KEY.COMMA:
+ if(selected_dropdown_item) {
+ add_token($(selected_dropdown_item).data("tokeninput"));
+ hiddenInput.change();
+ } else {
+ if ($(input).data("settings").allowFreeTagging) {
+ if($(input).data("settings").allowTabOut && $(this).val() === "") {
+ return true;
+ } else {
+ add_freetagging_tokens();
+ }
+ } else {
+ $(this).val("");
+ if($(input).data("settings").allowTabOut) {
+ return true;
+ }
+ }
+ focusWithTimeout(input_box);
+ event.stopPropagation();
+ event.preventDefault();
- if (selected_dropdown_item) {
- dropdown_item = $(selected_dropdown_item).next();
}
- } else {
- dropdown_item = $(dropdown).find('li').last();
+ return false;
+
+ case KEY.ESCAPE:
+ hide_dropdown();
+ return true;
- if (selected_dropdown_item) {
- dropdown_item = $(selected_dropdown_item).prev();
+ default:
+ if (String.fromCharCode(event.which)) {
+ // set a timeout just long enough to let this function finish.
+ setTimeout(function(){ do_search(); }, 5);
}
- }
+ break;
+ }
+ });
- select_dropdown_item(dropdown_item);
+ // Keep reference for placeholder
+ if (settings.placeholder) {
+ input_box.attr("placeholder", settings.placeholder);
+ }
+
+ // Keep a reference to the original input box
+ var hiddenInput = $(input)
+ .hide()
+ .val("")
+ .focus(function () {
+ focusWithTimeout(input_box);
+ })
+ .blur(function () {
+ input_box.blur();
+
+ //return the object to this can be referenced in the callback functions.
+ return hiddenInput;
+ })
+ ;
+
+ // Keep a reference to the selected token and dropdown item
+ var selected_token = null;
+ var selected_token_index = 0;
+ var selected_dropdown_item = null;
+
+ // The list to store the token items in
+ var token_list = $("")
+ .addClass($(input).data("settings").classes.tokenList)
+ .click(function (event) {
+ var li = $(event.target).closest("li");
+ if(li && li.get(0) && $.data(li.get(0), "tokeninput")) {
+ toggle_select_token(li);
+ } else {
+ // Deselect selected token
+ if(selected_token) {
+ deselect_token($(selected_token), POSITION.END);
}
- break;
+ // Focus input box
+ focusWithTimeout(input_box);
+ }
+ })
+ .mouseover(function (event) {
+ var li = $(event.target).closest("li");
+ if(li && selected_token !== this) {
+ li.addClass($(input).data("settings").classes.highlightedToken);
+ }
+ })
+ .mouseout(function (event) {
+ var li = $(event.target).closest("li");
+ if(li && selected_token !== this) {
+ li.removeClass($(input).data("settings").classes.highlightedToken);
+ }
+ })
+ .insertBefore(hiddenInput);
+
+ // The token holding the input box
+ var input_token = $("")
+ .addClass($(input).data("settings").classes.inputToken)
+ .appendTo(token_list)
+ .append(input_box);
+
+ // The list to store the dropdown items in
+ var dropdown = $("")
+ .addClass($(input).data("settings").classes.dropdown)
+ .appendTo("body")
+ .hide();
+
+ // Magic element to help us resize the text input
+ var input_resizer = $("")
+ .insertAfter(input_box)
+ .css({
+ position: "absolute",
+ top: -9999,
+ left: -9999,
+ width: "auto",
+ fontSize: input_box.css("fontSize"),
+ fontFamily: input_box.css("fontFamily"),
+ fontWeight: input_box.css("fontWeight"),
+ letterSpacing: input_box.css("letterSpacing"),
+ whiteSpace: "nowrap"
+ });
+
+ // Pre-populate list if items exist
+ hiddenInput.val("");
+ var li_data = $(input).data("settings").prePopulate || hiddenInput.data("pre");
+
+ if ($(input).data("settings").processPrePopulate && $.isFunction($(input).data("settings").onResult)) {
+ li_data = $(input).data("settings").onResult.call(hiddenInput, li_data);
+ }
- case KEY.BACKSPACE:
- previous_token = input_token.prev();
+ if (li_data && li_data.length) {
+ $.each(li_data, function (index, value) {
+ insert_token(value);
+ checkTokenLimit();
+ input_box.attr("placeholder", null)
+ });
+ }
- if (this.value.length === 0) {
- if (selected_token) {
- delete_token($(selected_token));
- hiddenInput.change();
- } else if(previous_token.length) {
- select_token($(previous_token.get(0)));
- }
+ // Check if widget should initialize as disabled
+ if ($(input).data("settings").disabled) {
+ toggleDisabled(true);
+ }
- return false;
- } else if($(this).val().length === 1) {
- hide_dropdown();
- } else {
- // set a timeout just long enough to let this function finish.
- setTimeout(function(){ do_search(); }, 5);
- }
- break;
-
- case KEY.TAB:
- case KEY.ENTER:
- case KEY.NUMPAD_ENTER:
- case KEY.COMMA:
- if(selected_dropdown_item) {
- add_token($(selected_dropdown_item).data("tokeninput"));
- hiddenInput.change();
- } else {
- if ($(input).data("settings").allowFreeTagging) {
- if($(input).data("settings").allowTabOut && $(this).val() === "") {
- return true;
- } else {
- add_freetagging_tokens();
- }
- } else {
- $(this).val("");
- if($(input).data("settings").allowTabOut) {
- return true;
+ // Initialization is done
+ if (typeof($(input).data("settings").onReady) === "function") {
+ $(input).data("settings").onReady.call();
+ }
+
+ //
+ // Public functions
+ //
+
+ this.clear = function() {
+ token_list.children("li").each(function() {
+ if ($(this).children("input").length === 0) {
+ delete_token($(this));
+ }
+ });
+ };
+
+ this.add = function(item) {
+ add_token(item);
+ };
+
+ this.remove = function(item) {
+ token_list.children("li").each(function() {
+ if ($(this).children("input").length === 0) {
+ var currToken = $(this).data("tokeninput");
+ var match = true;
+ for (var prop in item) {
+ if (item[prop] !== currToken[prop]) {
+ match = false;
+ break;
}
- }
- event.stopPropagation();
- event.preventDefault();
}
- return false;
+ if (match) {
+ delete_token($(this));
+ }
+ }
+ });
+ };
- case KEY.ESCAPE:
- hide_dropdown();
- return true;
+ this.getTokens = function() {
+ return saved_tokens;
+ };
+
+ this.toggleDisabled = function(disable) {
+ toggleDisabled(disable);
+ };
+
+ // Resize input to maximum width so the placeholder can be seen
+ resize_input();
+
+ //
+ // Private functions
+ //
+
+ function escapeHTML(text) {
+ return $(input).data("settings").enableHTML ? text : _escapeHTML(text);
+ }
- default:
- if (String.fromCharCode(event.which)) {
- // set a timeout just long enough to let this function finish.
- setTimeout(function(){ do_search(); }, 5);
+ // Toggles the widget between enabled and disabled state, or according
+ // to the [disable] parameter.
+ function toggleDisabled(disable) {
+ if (typeof disable === 'boolean') {
+ $(input).data("settings").disabled = disable
+ } else {
+ $(input).data("settings").disabled = !$(input).data("settings").disabled;
+ }
+ input_box.attr('disabled', $(input).data("settings").disabled);
+ token_list.toggleClass($(input).data("settings").classes.disabled, $(input).data("settings").disabled);
+ // if there is any token selected we deselect it
+ if(selected_token) {
+ deselect_token($(selected_token), POSITION.END);
+ }
+ hiddenInput.attr('disabled', $(input).data("settings").disabled);
+ }
+
+ function checkTokenLimit() {
+ if($(input).data("settings").tokenLimit !== null && token_count >= $(input).data("settings").tokenLimit) {
+ input_box.hide();
+ hide_dropdown();
+ return;
+ }
+ }
+
+ function resize_input() {
+ if(input_val === (input_val = input_box.val())) {return;}
+
+ // Get width left on the current line
+ var width_left = token_list.width() - input_box.offset().left - token_list.offset().left;
+ // Enter new content into resizer and resize input accordingly
+ input_resizer.html(_escapeHTML(input_val) || _escapeHTML(settings.placeholder));
+ // Get maximum width, minimum the size of input and maximum the widget's width
+ input_box.width(Math.min(token_list.width(),
+ Math.max(width_left, input_resizer.width() + 30)));
+ }
+
+ function add_freetagging_tokens() {
+ var value = $.trim(input_box.val());
+ var tokens = value.split($(input).data("settings").tokenDelimiter);
+ $.each(tokens, function(i, token) {
+ if (!token) {
+ return;
+ }
+
+ if ($.isFunction($(input).data("settings").onFreeTaggingAdd)) {
+ token = $(input).data("settings").onFreeTaggingAdd.call(hiddenInput, token);
+ }
+ var object = {};
+ object[$(input).data("settings").tokenValue] = object[$(input).data("settings").propertyToSearch] = token;
+ add_token(object);
+ });
+ }
+
+ // Inner function to a token to the list
+ function insert_token(item) {
+ var $this_token = $($(input).data("settings").tokenFormatter(item));
+ var readonly = item.readonly === true;
+
+ if(readonly) $this_token.addClass($(input).data("settings").classes.tokenReadOnly);
+
+ $this_token.addClass($(input).data("settings").classes.token).insertBefore(input_token);
+
+ // The 'delete token' button
+ if(!readonly) {
+ $("" + $(input).data("settings").deleteText + "")
+ .addClass($(input).data("settings").classes.tokenDelete)
+ .appendTo($this_token)
+ .click(function () {
+ if (!$(input).data("settings").disabled) {
+ delete_token($(this).parent());
+ hiddenInput.change();
+ return false;
+ }
+ });
+ }
+
+ // Store data on the token
+ var token_data = item;
+ $.data($this_token.get(0), "tokeninput", item);
+
+ // Save this token for duplicate checking
+ saved_tokens = saved_tokens.slice(0,selected_token_index).concat([token_data]).concat(saved_tokens.slice(selected_token_index));
+ selected_token_index++;
+
+ // Update the hidden input
+ update_hiddenInput(saved_tokens, hiddenInput);
+
+ token_count += 1;
+
+ // Check the token limit
+ if($(input).data("settings").tokenLimit !== null && token_count >= $(input).data("settings").tokenLimit) {
+ input_box.hide();
+ hide_dropdown();
+ }
+
+ return $this_token;
+ }
+
+ // Add a token to the token list based on user input
+ function add_token (item) {
+ var callback = $(input).data("settings").onAdd;
+
+ // See if the token already exists and select it if we don't want duplicates
+ if(token_count > 0 && $(input).data("settings").preventDuplicates) {
+ var found_existing_token = null;
+ token_list.children().each(function () {
+ var existing_token = $(this);
+ var existing_data = $.data(existing_token.get(0), "tokeninput");
+ if(existing_data && existing_data[settings.tokenValue] === item[settings.tokenValue]) {
+ found_existing_token = existing_token;
+ return false;
}
- break;
- }
- });
-
- // Keep reference for placeholder
- if (settings.placeholder) {
- input_box.attr("placeholder", settings.placeholder);
- }
-
- // Keep a reference to the original input box
- var hiddenInput = $(input)
- .hide()
- .val("")
- .focus(function () {
- focusWithTimeout(input_box);
- })
- .blur(function () {
- input_box.blur();
-
- //return the object to this can be referenced in the callback functions.
- return hiddenInput;
- })
- ;
-
- // Keep a reference to the selected token and dropdown item
- var selected_token = null;
- var selected_token_index = 0;
- var selected_dropdown_item = null;
-
- // The list to store the token items in
- var token_list = $("")
- .addClass($(input).data("settings").classes.tokenList)
- .click(function (event) {
- var li = $(event.target).closest("li");
- if(li && li.get(0) && $.data(li.get(0), "tokeninput")) {
- toggle_select_token(li);
- } else {
- // Deselect selected token
- if(selected_token) {
- deselect_token($(selected_token), POSITION.END);
- }
-
- // Focus input box
- focusWithTimeout(input_box);
- }
- })
- .mouseover(function (event) {
- var li = $(event.target).closest("li");
- if(li && selected_token !== this) {
- li.addClass($(input).data("settings").classes.highlightedToken);
- }
- })
- .mouseout(function (event) {
- var li = $(event.target).closest("li");
- if(li && selected_token !== this) {
- li.removeClass($(input).data("settings").classes.highlightedToken);
- }
- })
- .insertBefore(hiddenInput);
-
- // The token holding the input box
- var input_token = $("")
- .addClass($(input).data("settings").classes.inputToken)
- .appendTo(token_list)
- .append(input_box);
-
- // The list to store the dropdown items in
- var dropdown = $("")
- .addClass($(input).data("settings").classes.dropdown)
- .appendTo("body")
- .hide();
-
- // Magic element to help us resize the text input
- var input_resizer = $("")
- .insertAfter(input_box)
- .css({
- position: "absolute",
- top: -9999,
- left: -9999,
- width: "auto",
- fontSize: input_box.css("fontSize"),
- fontFamily: input_box.css("fontFamily"),
- fontWeight: input_box.css("fontWeight"),
- letterSpacing: input_box.css("letterSpacing"),
- whiteSpace: "nowrap"
- });
-
- // Pre-populate list if items exist
- hiddenInput.val("");
- var li_data = $(input).data("settings").prePopulate || hiddenInput.data("pre");
-
- if ($(input).data("settings").processPrePopulate && $.isFunction($(input).data("settings").onResult)) {
- li_data = $(input).data("settings").onResult.call(hiddenInput, li_data);
- }
-
- if (li_data && li_data.length) {
- $.each(li_data, function (index, value) {
- insert_token(value);
- checkTokenLimit();
- input_box.attr("placeholder", null)
- });
- }
-
- // Check if widget should initialize as disabled
- if ($(input).data("settings").disabled) {
- toggleDisabled(true);
- }
-
- // Initialization is done
- if (typeof($(input).data("settings").onReady) === "function") {
- $(input).data("settings").onReady.call();
- }
-
- //
- // Public functions
- //
-
- this.clear = function() {
- token_list.children("li").each(function() {
- if ($(this).children("input").length === 0) {
- delete_token($(this));
- }
- });
- };
-
- this.add = function(item) {
- add_token(item);
- };
-
- this.remove = function(item) {
- token_list.children("li").each(function() {
- if ($(this).children("input").length === 0) {
- var currToken = $(this).data("tokeninput");
- var match = true;
- for (var prop in item) {
- if (item[prop] !== currToken[prop]) {
- match = false;
- break;
- }
- }
- if (match) {
- delete_token($(this));
- }
- }
- });
- };
-
- this.getTokens = function() {
- return saved_tokens;
- };
-
- this.toggleDisabled = function(disable) {
- toggleDisabled(disable);
- };
-
- // Resize input to maximum width so the placeholder can be seen
- resize_input();
-
- //
- // Private functions
- //
-
- function escapeHTML(text) {
- return $(input).data("settings").enableHTML ? text : _escapeHTML(text);
- }
-
- // Toggles the widget between enabled and disabled state, or according
- // to the [disable] parameter.
- function toggleDisabled(disable) {
- if (typeof disable === 'boolean') {
- $(input).data("settings").disabled = disable
- } else {
- $(input).data("settings").disabled = !$(input).data("settings").disabled;
- }
- input_box.attr('disabled', $(input).data("settings").disabled);
- token_list.toggleClass($(input).data("settings").classes.disabled, $(input).data("settings").disabled);
- // if there is any token selected we deselect it
- if(selected_token) {
- deselect_token($(selected_token), POSITION.END);
- }
- hiddenInput.attr('disabled', $(input).data("settings").disabled);
- }
-
- function checkTokenLimit() {
- if($(input).data("settings").tokenLimit !== null && token_count >= $(input).data("settings").tokenLimit) {
- input_box.hide();
- hide_dropdown();
- return;
- }
- }
-
- function resize_input() {
- if(input_val === (input_val = input_box.val())) {return;}
-
- // Get width left on the current line
- var width_left = token_list.width() - input_box.offset().left - token_list.offset().left;
- // Enter new content into resizer and resize input accordingly
- input_resizer.html(_escapeHTML(input_val) || _escapeHTML(settings.placeholder));
- // Get maximum width, minimum the size of input and maximum the widget's width
- input_box.width(Math.min(token_list.width(),
- Math.max(width_left, input_resizer.width() + 30)));
- }
-
- function add_freetagging_tokens() {
- var value = $.trim(input_box.val());
- var tokens = value.split($(input).data("settings").tokenDelimiter);
- $.each(tokens, function(i, token) {
- if (!token) {
- return;
+ });
+
+ if(found_existing_token) {
+ select_token(found_existing_token);
+ input_token.insertAfter(found_existing_token);
+ focusWithTimeout(input_box);
+ return;
+ }
}
- if ($.isFunction($(input).data("settings").onFreeTaggingAdd)) {
- token = $(input).data("settings").onFreeTaggingAdd.call(hiddenInput, token);
+ // Squeeze input_box so we force no unnecessary line break
+ input_box.width(1);
+ focusWithTimeout(input_box);
+
+ // Insert the new tokens
+ if($(input).data("settings").tokenLimit == null || token_count < $(input).data("settings").tokenLimit) {
+ insert_token(item);
+ // Remove the placeholder so it's not seen after you've added a token
+ input_box.attr("placeholder", null);
+ checkTokenLimit();
+ }
+
+ // Clear input box
+ input_box.val("");
+
+ // Don't show the help dropdown, they've got the idea
+ hide_dropdown();
+
+ // Execute the onAdd callback if defined
+ if($.isFunction(callback)) {
+ callback.call(hiddenInput,item);
+ }
+ }
+
+ // Select a token in the token list
+ function select_token (token) {
+ if (!$(input).data("settings").disabled) {
+ token.addClass($(input).data("settings").classes.selectedToken);
+ selected_token = token.get(0);
+
+ // Hide input box
+ input_box.val("");
+
+ // Hide dropdown if it is visible (eg if we clicked to select token)
+ hide_dropdown();
}
- var object = {};
- object[$(input).data("settings").tokenValue] = object[$(input).data("settings").propertyToSearch] = token;
- add_token(object);
- });
- }
-
- // Inner function to a token to the list
- function insert_token(item) {
- var $this_token = $($(input).data("settings").tokenFormatter(item));
- var readonly = item.readonly === true;
-
- if(readonly) $this_token.addClass($(input).data("settings").classes.tokenReadOnly);
-
- $this_token.addClass($(input).data("settings").classes.token).insertBefore(input_token);
-
- // The 'delete token' button
- if(!readonly) {
- $("" + $(input).data("settings").deleteText + "")
- .addClass($(input).data("settings").classes.tokenDelete)
- .appendTo($this_token)
- .click(function () {
- if (!$(input).data("settings").disabled) {
- delete_token($(this).parent());
+ }
+
+ // Deselect a token in the token list
+ function deselect_token (token, position) {
+ token.removeClass($(input).data("settings").classes.selectedToken);
+ selected_token = null;
+
+ if(position === POSITION.BEFORE) {
+ input_token.insertBefore(token);
+ selected_token_index--;
+ } else if(position === POSITION.AFTER) {
+ input_token.insertAfter(token);
+ selected_token_index++;
+ } else {
+ input_token.appendTo(token_list);
+ selected_token_index = token_count;
+ }
+
+ // Show the input box and give it focus again
+ focusWithTimeout(input_box);
+ }
+
+ // Toggle selection of a token in the token list
+ function toggle_select_token(token) {
+ var previous_selected_token = selected_token;
+
+ if(selected_token) {
+ deselect_token($(selected_token), POSITION.END);
+ }
+
+ if(previous_selected_token === token.get(0)) {
+ deselect_token(token, POSITION.END);
+ } else {
+ select_token(token);
+ }
+ }
+
+ // Delete a token from the token list
+ function delete_token (token) {
+ // Remove the id from the saved list
+ var token_data = $.data(token.get(0), "tokeninput");
+ var callback = $(input).data("settings").onDelete;
+
+ var index = token.prevAll().length;
+ if(index > selected_token_index) index--;
+
+ // Delete the token
+ token.remove();
+ selected_token = null;
+
+ // Show the input box and give it focus again
+ focusWithTimeout(input_box);
+
+ // Remove this token from the saved list
+ saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1));
+ if (saved_tokens.length == 0) {
+ input_box.attr("placeholder", settings.placeholder)
+ }
+ if(index < selected_token_index) selected_token_index--;
+
+ // Update the hidden input
+ update_hiddenInput(saved_tokens, hiddenInput);
+
+ token_count -= 1;
+
+ if($(input).data("settings").tokenLimit !== null) {
+ input_box
+ .show()
+ .val("");
+ focusWithTimeout(input_box);
+ }
+
+ // Execute the onDelete callback if defined
+ if($.isFunction(callback)) {
+ callback.call(hiddenInput,token_data);
+ }
+ }
+
+ // Update the hidden input box value
+ function update_hiddenInput(saved_tokens, hiddenInput) {
+ var token_values = $.map(saved_tokens, function (el) {
+ if(typeof $(input).data("settings").tokenValue == 'function')
+ return $(input).data("settings").tokenValue.call(this, el);
+
+ return el[$(input).data("settings").tokenValue];
+ });
+ hiddenInput.val(token_values.join($(input).data("settings").tokenDelimiter));
+
+ }
+
+ // Hide and clear the results dropdown
+ function hide_dropdown () {
+ dropdown.hide().empty();
+ selected_dropdown_item = null;
+ }
+
+ function show_dropdown() {
+ dropdown
+ .css({
+ position: "absolute",
+ top: token_list.offset().top + token_list.outerHeight(true),
+ left: token_list.offset().left,
+ width: token_list.width(),
+ 'z-index': $(input).data("settings").zindex
+ })
+ .show();
+ }
+
+ function show_dropdown_searching () {
+ if($(input).data("settings").searchingText) {
+ dropdown.html("" + escapeHTML($(input).data("settings").searchingText) + "
");
+ show_dropdown();
+ }
+ }
+
+ function show_dropdown_hint () {
+ if($(input).data("settings").hintText) {
+ dropdown.html("" + escapeHTML($(input).data("settings").hintText) + "
");
+ show_dropdown();
+ }
+ }
+
+ var regexp_special_chars = new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g');
+ function regexp_escape(term) {
+ return term.replace(regexp_special_chars, '\\$&');
+ }
+
+ // Highlight the query part of the search term
+ function highlight_term(value, term) {
+ return value.replace(
+ new RegExp(
+ "(?![^&;]+;)(?!<[^<>]*)(" + regexp_escape(term) + ")(?![^<>]*>)(?![^&;]+;)",
+ "gi"
+ ), function(match, p1) {
+ return "" + escapeHTML(p1) + "";
+ }
+ );
+ }
+
+ function find_value_and_highlight_term(template, value, term) {
+ return template.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + regexp_escape(value) + ")(?![^<>]*>)(?![^&;]+;)", "g"), highlight_term(value, term));
+ }
+
+ // exclude existing tokens from dropdown, so the list is clearer
+ function excludeCurrent(results) {
+ if ($(input).data("settings").excludeCurrent) {
+ var currentTokens = $(input).data("tokenInputObject").getTokens(),
+ trimmedList = [];
+ if (currentTokens.length) {
+ $.each(results, function(index, value) {
+ var notFound = true;
+ $.each(currentTokens, function(cIndex, cValue) {
+ if (value[$(input).data("settings").propertyToSearch] == cValue[$(input).data("settings").propertyToSearch]) {
+ notFound = false;
+ return false;
+ }
+ });
+
+ if (notFound) {
+ trimmedList.push(value);
+ }
+ });
+ results = trimmedList;
+ }
+ }
+
+ return results;
+ }
+
+ // Populate the results dropdown with some results
+ function populateDropdown (query, results) {
+ // exclude current tokens if configured
+ results = excludeCurrent(results);
+
+ if(results && results.length) {
+ dropdown.empty();
+ var dropdown_ul = $("")
+ .appendTo(dropdown)
+ .mouseover(function (event) {
+ select_dropdown_item($(event.target).closest("li"));
+ })
+ .mousedown(function (event) {
+ add_token($(event.target).closest("li").data("tokeninput"));
hiddenInput.change();
return false;
+ })
+ .hide();
+
+ if ($(input).data("settings").resultsLimit && results.length > $(input).data("settings").resultsLimit) {
+ results = results.slice(0, $(input).data("settings").resultsLimit);
+ }
+
+ $.each(results, function(index, value) {
+ var this_li = $(input).data("settings").resultsFormatter(value);
+
+ this_li = find_value_and_highlight_term(this_li ,value[$(input).data("settings").propertyToSearch], query);
+ this_li = $(this_li).appendTo(dropdown_ul);
+
+ if(index % 2) {
+ this_li.addClass($(input).data("settings").classes.dropdownItem);
+ } else {
+ this_li.addClass($(input).data("settings").classes.dropdownItem2);
}
+
+ if(index === 0 && $(input).data("settings").autoSelectFirstResult) {
+ select_dropdown_item(this_li);
+ }
+
+ $.data(this_li.get(0), "tokeninput", value);
});
- }
-
- // Store data on the token
- var token_data = item;
- $.data($this_token.get(0), "tokeninput", item);
-
- // Save this token for duplicate checking
- saved_tokens = saved_tokens.slice(0,selected_token_index).concat([token_data]).concat(saved_tokens.slice(selected_token_index));
- selected_token_index++;
-
- // Update the hidden input
- update_hiddenInput(saved_tokens, hiddenInput);
-
- token_count += 1;
-
- // Check the token limit
- if($(input).data("settings").tokenLimit !== null && token_count >= $(input).data("settings").tokenLimit) {
- input_box.hide();
- hide_dropdown();
- }
-
- return $this_token;
- }
-
- // Add a token to the token list based on user input
- function add_token (item) {
- var callback = $(input).data("settings").onAdd;
-
- // See if the token already exists and select it if we don't want duplicates
- if(token_count > 0 && $(input).data("settings").preventDuplicates) {
- var found_existing_token = null;
- token_list.children().each(function () {
- var existing_token = $(this);
- var existing_data = $.data(existing_token.get(0), "tokeninput");
- if(existing_data && existing_data[settings.tokenValue] === item[settings.tokenValue]) {
- found_existing_token = existing_token;
- return false;
- }
- });
-
- if(found_existing_token) {
- select_token(found_existing_token);
- input_token.insertAfter(found_existing_token);
- focusWithTimeout(input_box);
- return;
- }
- }
-
- // Squeeze input_box so we force no unnecessary line break
- input_box.width(1);
-
- // Insert the new tokens
- if($(input).data("settings").tokenLimit == null || token_count < $(input).data("settings").tokenLimit) {
- insert_token(item);
- // Remove the placeholder so it's not seen after you've added a token
- input_box.attr("placeholder", null);
- checkTokenLimit();
- }
-
- // Clear input box
- input_box.val("");
-
- // Don't show the help dropdown, they've got the idea
- hide_dropdown();
-
- // Execute the onAdd callback if defined
- if($.isFunction(callback)) {
- callback.call(hiddenInput,item);
- }
- }
-
- // Select a token in the token list
- function select_token (token) {
- if (!$(input).data("settings").disabled) {
- token.addClass($(input).data("settings").classes.selectedToken);
- selected_token = token.get(0);
-
- // Hide input box
- input_box.val("");
-
- // Hide dropdown if it is visible (eg if we clicked to select token)
- hide_dropdown();
- }
- }
-
- // Deselect a token in the token list
- function deselect_token (token, position) {
- token.removeClass($(input).data("settings").classes.selectedToken);
- selected_token = null;
-
- if(position === POSITION.BEFORE) {
- input_token.insertBefore(token);
- selected_token_index--;
- } else if(position === POSITION.AFTER) {
- input_token.insertAfter(token);
- selected_token_index++;
- } else {
- input_token.appendTo(token_list);
- selected_token_index = token_count;
- }
-
- // Show the input box and give it focus again
- focusWithTimeout(input_box);
- }
-
- // Toggle selection of a token in the token list
- function toggle_select_token(token) {
- var previous_selected_token = selected_token;
-
- if(selected_token) {
- deselect_token($(selected_token), POSITION.END);
- }
-
- if(previous_selected_token === token.get(0)) {
- deselect_token(token, POSITION.END);
- } else {
- select_token(token);
- }
- }
-
- // Delete a token from the token list
- function delete_token (token) {
- // Remove the id from the saved list
- var token_data = $.data(token.get(0), "tokeninput");
- var callback = $(input).data("settings").onDelete;
-
- var index = token.prevAll().length;
- if(index > selected_token_index) index--;
-
- // Delete the token
- token.remove();
- selected_token = null;
-
- // Show the input box and give it focus again
- focusWithTimeout(input_box);
-
- // Remove this token from the saved list
- saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1));
- if (saved_tokens.length == 0) {
- input_box.attr("placeholder", settings.placeholder)
- }
- if(index < selected_token_index) selected_token_index--;
-
- // Update the hidden input
- update_hiddenInput(saved_tokens, hiddenInput);
-
- token_count -= 1;
-
- if($(input).data("settings").tokenLimit !== null) {
- input_box
- .show()
- .val("");
- focusWithTimeout(input_box);
- }
-
- // Execute the onDelete callback if defined
- if($.isFunction(callback)) {
- callback.call(hiddenInput,token_data);
- }
- }
-
- // Update the hidden input box value
- function update_hiddenInput(saved_tokens, hiddenInput) {
- var token_values = $.map(saved_tokens, function (el) {
- if(typeof $(input).data("settings").tokenValue == 'function')
- return $(input).data("settings").tokenValue.call(this, el);
-
- return el[$(input).data("settings").tokenValue];
- });
- hiddenInput.val(token_values.join($(input).data("settings").tokenDelimiter));
-
- }
-
- // Hide and clear the results dropdown
- function hide_dropdown () {
- dropdown.hide().empty();
- selected_dropdown_item = null;
- }
-
- function show_dropdown() {
- dropdown
- .css({
- position: "absolute",
- top: token_list.offset().top + token_list.outerHeight(true),
- left: token_list.offset().left,
- width: token_list.width(),
- 'z-index': $(input).data("settings").zindex
- })
- .show();
- }
-
- function show_dropdown_searching () {
- if($(input).data("settings").searchingText) {
- dropdown.html("" + escapeHTML($(input).data("settings").searchingText) + "
");
- show_dropdown();
- }
- }
-
- function show_dropdown_hint () {
- if($(input).data("settings").hintText) {
- dropdown.html("" + escapeHTML($(input).data("settings").hintText) + "
");
- show_dropdown();
- }
- }
-
- var regexp_special_chars = new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g');
- function regexp_escape(term) {
- return term.replace(regexp_special_chars, '\\$&');
- }
-
- // Highlight the query part of the search term
- function highlight_term(value, term) {
- return value.replace(
- new RegExp(
- "(?![^&;]+;)(?!<[^<>]*)(" + regexp_escape(term) + ")(?![^<>]*>)(?![^&;]+;)",
- "gi"
- ), function(match, p1) {
- return "" + escapeHTML(p1) + "";
+
+ show_dropdown();
+
+ if($(input).data("settings").animateDropdown) {
+ dropdown_ul.slideDown("fast");
+ } else {
+ dropdown_ul.show();
+ }
+ } else {
+ if($(input).data("settings").noResultsText) {
+ dropdown.html("" + escapeHTML($(input).data("settings").noResultsText) + "
");
+ show_dropdown();
+ }
+ }
+ }
+
+ // Highlight an item in the results dropdown
+ function select_dropdown_item (item) {
+ if(item) {
+ if(selected_dropdown_item) {
+ deselect_dropdown_item($(selected_dropdown_item));
+ }
+
+ item.addClass($(input).data("settings").classes.selectedDropdownItem);
+ selected_dropdown_item = item.get(0);
+ }
+ }
+
+ // Remove highlighting from an item in the results dropdown
+ function deselect_dropdown_item (item) {
+ item.removeClass($(input).data("settings").classes.selectedDropdownItem);
+ selected_dropdown_item = null;
+ }
+
+ // Do a search and show the "searching" dropdown if the input is longer
+ // than $(input).data("settings").minChars
+ function do_search() {
+ var query = input_box.val();
+
+ if(query && query.length) {
+ if(selected_token) {
+ deselect_token($(selected_token), POSITION.AFTER);
+ }
+
+ if(query.length >= $(input).data("settings").minChars) {
+ show_dropdown_searching();
+ clearTimeout(timeout);
+
+ timeout = setTimeout(function(){
+ run_search(query);
+ }, $(input).data("settings").searchDelay);
+ } else {
+ hide_dropdown();
+ }
}
- );
- }
-
- function find_value_and_highlight_term(template, value, term) {
- return template.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + regexp_escape(value) + ")(?![^<>]*>)(?![^&;]+;)", "g"), highlight_term(value, term));
- }
-
- // exclude existing tokens from dropdown, so the list is clearer
- function excludeCurrent(results) {
- if ($(input).data("settings").excludeCurrent) {
- var currentTokens = $(input).data("tokenInputObject").getTokens(),
- trimmedList = [];
- if (currentTokens.length) {
- $.each(results, function(index, value) {
- var notFound = true;
- $.each(currentTokens, function(cIndex, cValue) {
- if (value[$(input).data("settings").propertyToSearch] == cValue[$(input).data("settings").propertyToSearch]) {
- notFound = false;
- return false;
- }
- });
-
- if (notFound) {
- trimmedList.push(value);
- }
- });
- results = trimmedList;
- }
- }
-
- return results;
- }
-
- // Populate the results dropdown with some results
- function populateDropdown (query, results) {
- // exclude current tokens if configured
- results = excludeCurrent(results);
-
- if(results && results.length) {
- dropdown.empty();
- var dropdown_ul = $("")
- .appendTo(dropdown)
- .mouseover(function (event) {
- select_dropdown_item($(event.target).closest("li"));
- })
- .mousedown(function (event) {
- add_token($(event.target).closest("li").data("tokeninput"));
- hiddenInput.change();
- return false;
- })
- .hide();
-
- if ($(input).data("settings").resultsLimit && results.length > $(input).data("settings").resultsLimit) {
- results = results.slice(0, $(input).data("settings").resultsLimit);
- }
-
- $.each(results, function(index, value) {
- var this_li = $(input).data("settings").resultsFormatter(value);
-
- this_li = find_value_and_highlight_term(this_li ,value[$(input).data("settings").propertyToSearch], query);
- this_li = $(this_li).appendTo(dropdown_ul);
-
- if(index % 2) {
- this_li.addClass($(input).data("settings").classes.dropdownItem);
- } else {
- this_li.addClass($(input).data("settings").classes.dropdownItem2);
- }
-
- if(index === 0 && $(input).data("settings").autoSelectFirstResult) {
- select_dropdown_item(this_li);
- }
-
- $.data(this_li.get(0), "tokeninput", value);
- });
-
- show_dropdown();
-
- if($(input).data("settings").animateDropdown) {
- dropdown_ul.slideDown("fast");
- } else {
- dropdown_ul.show();
- }
- } else {
- if($(input).data("settings").noResultsText) {
- dropdown.html("" + escapeHTML($(input).data("settings").noResultsText) + "
");
- show_dropdown();
- }
- }
- }
-
- // Highlight an item in the results dropdown
- function select_dropdown_item (item) {
- if(item) {
- if(selected_dropdown_item) {
- deselect_dropdown_item($(selected_dropdown_item));
- }
-
- item.addClass($(input).data("settings").classes.selectedDropdownItem);
- selected_dropdown_item = item.get(0);
- }
- }
-
- // Remove highlighting from an item in the results dropdown
- function deselect_dropdown_item (item) {
- item.removeClass($(input).data("settings").classes.selectedDropdownItem);
- selected_dropdown_item = null;
- }
-
- // Do a search and show the "searching" dropdown if the input is longer
- // than $(input).data("settings").minChars
- function do_search() {
- var query = input_box.val();
-
- if(query && query.length) {
- if(selected_token) {
- deselect_token($(selected_token), POSITION.AFTER);
- }
-
- if(query.length >= $(input).data("settings").minChars) {
- show_dropdown_searching();
- clearTimeout(timeout);
-
- timeout = setTimeout(function(){
- run_search(query);
- }, $(input).data("settings").searchDelay);
- } else {
- hide_dropdown();
- }
- }
- }
-
- // Do the actual search
- function run_search(query) {
- var cache_key = query + computeURL();
- var cached_results = cache.get(cache_key);
- if (settings.caching && cached_results) {
- if ($.isFunction($(input).data("settings").onCachedResult)) {
- cached_results = $(input).data("settings").onCachedResult.call(hiddenInput, cached_results);
- }
- populateDropdown(query, cached_results);
- } else {
- // Are we doing an ajax search or local data search?
- if($(input).data("settings").url) {
- var url = computeURL();
- // Extract existing get params
- var ajax_params = {};
- ajax_params.data = {};
- if(url.indexOf("?") > -1) {
- var parts = url.split("?");
- ajax_params.url = parts[0];
-
- var param_array = parts[1].split("&");
- $.each(param_array, function (index, value) {
- var kv = value.split("=");
- ajax_params.data[kv[0]] = kv[1];
- });
- } else {
- ajax_params.url = url;
- }
-
- // Prepare the request
- ajax_params.data[$(input).data("settings").queryParam] = query;
- ajax_params.type = $(input).data("settings").method;
- ajax_params.dataType = $(input).data("settings").contentType;
- if ($(input).data("settings").crossDomain) {
- ajax_params.dataType = "jsonp";
- }
-
- // exclude current tokens?
- // send exclude list to the server, so it can also exclude existing tokens
- if ($(input).data("settings").excludeCurrent) {
- var currentTokens = $(input).data("tokenInputObject").getTokens();
- var tokenList = $.map(currentTokens, function (el) {
- if(typeof $(input).data("settings").tokenValue == 'function')
- return $(input).data("settings").tokenValue.call(this, el);
-
- return el[$(input).data("settings").tokenValue];
- });
-
- ajax_params.data[$(input).data("settings").excludeCurrentParameter] = tokenList.join($(input).data("settings").tokenDelimiter);
- }
-
- // Attach the success callback
- ajax_params.success = function(results) {
- cache.add(cache_key, $(input).data("settings").jsonContainer ? results[$(input).data("settings").jsonContainer] : results);
+ }
+
+ // Do the actual search
+ function run_search(query) {
+ var cache_key = query + computeURL();
+ var cached_results = cache.get(cache_key);
+ if (settings.caching && cached_results) {
+ if ($.isFunction($(input).data("settings").onCachedResult)) {
+ cached_results = $(input).data("settings").onCachedResult.call(hiddenInput, cached_results);
+ }
+ populateDropdown(query, cached_results);
+ } else {
+ // Are we doing an ajax search or local data search?
+ if($(input).data("settings").url) {
+ var url = computeURL();
+ // Extract existing get params
+ var ajax_params = {};
+ ajax_params.data = {};
+ if(url.indexOf("?") > -1) {
+ var parts = url.split("?");
+ ajax_params.url = parts[0];
+
+ var param_array = parts[1].split("&");
+ $.each(param_array, function (index, value) {
+ var kv = value.split("=");
+ ajax_params.data[kv[0]] = decodeURIComponent(kv[1]);
+ });
+ } else {
+ ajax_params.url = url;
+ }
+
+ // Prepare the request
+ ajax_params.data[$(input).data("settings").queryParam] = query;
+ ajax_params.type = $(input).data("settings").method;
+ ajax_params.dataType = $(input).data("settings").contentType;
+ if ($(input).data("settings").crossDomain) {
+ ajax_params.dataType = "jsonp";
+ }
+
+ // exclude current tokens?
+ // send exclude list to the server, so it can also exclude existing tokens
+ if ($(input).data("settings").excludeCurrent && $(input).data("settings").excludeCurrentParameter!=null) {
+ var currentTokens = $(input).data("tokenInputObject").getTokens();
+ var tokenList = $.map(currentTokens, function (el) {
+ if(typeof $(input).data("settings").tokenValue == 'function')
+ return $(input).data("settings").tokenValue.call(this, el);
+
+ return el[$(input).data("settings").tokenValue];
+ });
+
+ ajax_params.data[$(input).data("settings").excludeCurrentParameter] = tokenList.join($(input).data("settings").tokenDelimiter);
+ }
+
+ // Attach the success callback
+ ajax_params.success = function(results) {
+ cache.add(cache_key, $(input).data("settings").jsonContainer ? results[$(input).data("settings").jsonContainer] : results);
+ if($.isFunction($(input).data("settings").onResult)) {
+ results = $(input).data("settings").onResult.call(hiddenInput, results);
+ }
+
+ // only populate the dropdown if the results are associated with the active search query
+ if(input_box.val() === query) {
+ populateDropdown(query, $(input).data("settings").jsonContainer ? results[$(input).data("settings").jsonContainer] : results);
+ }
+ };
+
+ // Provide a beforeSend callback
+ if (settings.onSend) {
+ settings.onSend(ajax_params);
+ }
+
+ // Make the request
+ $.ajax(ajax_params);
+ } else if($(input).data("settings").local_data) {
+ // Do the search through local data
+ var results = $.grep($(input).data("settings").local_data, function (row) {
+ return row[$(input).data("settings").propertyToSearch].toLowerCase().indexOf(query.toLowerCase()) > -1;
+ });
+
+ cache.add(cache_key, results);
if($.isFunction($(input).data("settings").onResult)) {
results = $(input).data("settings").onResult.call(hiddenInput, results);
}
+ populateDropdown(query, results);
+ }
+ }
+ }
- // only populate the dropdown if the results are associated with the active search query
- if(input_box.val() === query) {
- populateDropdown(query, $(input).data("settings").jsonContainer ? results[$(input).data("settings").jsonContainer] : results);
- }
- };
-
- // Provide a beforeSend callback
- if (settings.onSend) {
- settings.onSend(ajax_params);
- }
-
- // Make the request
- $.ajax(ajax_params);
- } else if($(input).data("settings").local_data) {
- // Do the search through local data
- var results = $.grep($(input).data("settings").local_data, function (row) {
- return row[$(input).data("settings").propertyToSearch].toLowerCase().indexOf(query.toLowerCase()) > -1;
- });
-
- cache.add(cache_key, results);
- if($.isFunction($(input).data("settings").onResult)) {
- results = $(input).data("settings").onResult.call(hiddenInput, results);
- }
- populateDropdown(query, results);
- }
- }
- }
-
- // compute the dynamic URL
- function computeURL() {
- var settings = $(input).data("settings");
- return typeof settings.url == 'function' ? settings.url.call(settings) : settings.url;
- }
-
- // Bring browser focus to the specified object.
- // Use of setTimeout is to get around an IE bug.
- // (See, e.g., http://stackoverflow.com/questions/2600186/focus-doesnt-work-in-ie)
- //
- // obj: a jQuery object to focus()
- function focusWithTimeout(object) {
- setTimeout(
- function() {
- object.focus();
- },
- 50
- );
- }
- };
-
- // Really basic cache for the results
- $.TokenList.Cache = function (options) {
- var settings, data = {}, size = 0, flush;
-
- settings = $.extend({ max_size: 500 }, options);
-
- flush = function () {
- data = {};
- size = 0;
+ // compute the dynamic URL
+ function computeURL() {
+ var settings = $(input).data("settings");
+ return typeof settings.url == 'function' ? settings.url.call(settings) : settings.url;
+ }
+
+ // Bring browser focus to the specified object.
+ // Use of setTimeout is to get around an IE bug.
+ // (See, e.g., http://stackoverflow.com/questions/2600186/focus-doesnt-work-in-ie)
+ //
+ // obj: a jQuery object to focus()
+ function focusWithTimeout(object) {
+ setTimeout(
+ function() {
+ object.focus();
+ },
+ 50
+ );
+ }
};
- this.add = function (query, results) {
- if (size > settings.max_size) {
- flush();
- }
+ // Really basic cache for the results
+ $.TokenList.Cache = function (options) {
+ var settings, data = {}, size = 0, flush;
- if (!data[query]) {
- size += 1;
- }
+ settings = $.extend({ max_size: 500 }, options);
- data[query] = results;
- };
+ flush = function () {
+ data = {};
+ size = 0;
+ };
+
+ this.add = function (query, results) {
+ if (size > settings.max_size) {
+ flush();
+ }
+
+ if (!data[query]) {
+ size += 1;
+ }
+
+ data[query] = results;
+ };
- this.get = function (query) {
- return data[query];
+ this.get = function (query) {
+ return data[query];
+ };
};
- };
}(jQuery));