From 62a277d724332cc6bad87d430780f4c6494e4f14 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Fri, 15 Apr 2011 17:17:45 -0300 Subject: [PATCH 01/63] when tokenLimit was set to 1, onAdd callback didn't work as expected. --- src/jquery.tokeninput.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 914d6d17..7cadbec4 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -408,16 +408,15 @@ $.TokenList = function (input, url_or_data, settings) { if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) { input_box.hide(); hide_dropdown(); - return; } else { input_box.focus(); - } - - // Clear input box - input_box.val(""); + + // Clear input box + input_box.val(""); - // Don't show the help dropdown, they've got the idea - hide_dropdown(); + // Don't show the help dropdown, they've got the idea + hide_dropdown(); + } // Execute the onAdd callback if defined if($.isFunction(callback)) { From 73671e6ad37742bb4167c12be34fd021308551f1 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Mon, 18 Apr 2011 10:06:01 -0300 Subject: [PATCH 02/63] tokenInput won't ignore anymore params that are different of 'id' or 'name' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit onAdd and onDelete functions can now make use of different params when handling the selected object. For instance: [ {id: 1, name: 'São Paulo', 'geolocation': '-23.55 -46.63'}, {id: 2, name: 'New York', 'geolocation': '40.698 -73.98'} ] --- src/jquery.tokeninput.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 7cadbec4..2f0dc0d4 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -313,10 +313,10 @@ $.TokenList = function (input, url_or_data, settings) { var li_data = settings.prePopulate || hidden_input.data("pre"); if(settings.processPrePopulate && $.isFunction(settings.onResult)) { li_data = settings.onResult.call(hidden_input, li_data); - } + } if(li_data && li_data.length) { $.each(li_data, function (index, value) { - insert_token(value.id, value.name); + insert_token(value); }); } @@ -343,8 +343,8 @@ $.TokenList = function (input, url_or_data, settings) { } // Inner function to a token to the list - function insert_token(id, value) { - var this_token = $("
  • "+ value +"

  • ") + function insert_token(object) { + var this_token = $("
  • "+ object.name +"

  • ") .addClass(settings.classes.token) .insertBefore(input_token); @@ -358,7 +358,7 @@ $.TokenList = function (input, url_or_data, settings) { }); // Store data on the token - var token_data = {"id": id, "name": value}; + var token_data = object; $.data(this_token.get(0), "tokeninput", token_data); // Save this token for duplicate checking @@ -402,7 +402,7 @@ $.TokenList = function (input, url_or_data, settings) { } // Insert the new tokens - insert_token(li_data.id, li_data.name); + insert_token(li_data); // Check the token limit if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) { @@ -577,7 +577,7 @@ $.TokenList = function (input, url_or_data, settings) { select_dropdown_item(this_li); } - $.data(this_li.get(0), "tokeninput", {"id": value.id, "name": value.name}); + $.data(this_li.get(0), "tokeninput", value); }); show_dropdown(); From 7204b97aa896a369d2d8171cefe8619b992b780b Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Tue, 19 Apr 2011 14:40:38 -0300 Subject: [PATCH 03/63] fix regex expression based on this issue[1] [1] https://github.com/loopj/jquery-tokeninput/issues/65 --- src/jquery.tokeninput.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 2f0dc0d4..d6072bb2 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -313,7 +313,7 @@ $.TokenList = function (input, url_or_data, settings) { var li_data = settings.prePopulate || hidden_input.data("pre"); if(settings.processPrePopulate && $.isFunction(settings.onResult)) { li_data = settings.onResult.call(hidden_input, li_data); - } + } if(li_data && li_data.length) { $.each(li_data, function (index, value) { insert_token(value); @@ -542,10 +542,14 @@ $.TokenList = function (input, url_or_data, settings) { show_dropdown(); } } + + RegExp.escape = function(text) { + return text.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&"); + }; // Highlight the query part of the search term function highlight_term(value, term) { - return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "$1"); + return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + RegExp.escape(term) + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "$1"); } // Populate the results dropdown with some results From 294f6a883eae984545b56676d0973be61cc587e8 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Wed, 20 Apr 2011 13:26:45 -0300 Subject: [PATCH 04/63] send string to server without using toLowerCase function first --- src/jquery.tokeninput.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index d6072bb2..55bbc758 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -620,7 +620,7 @@ $.TokenList = function (input, url_or_data, settings) { // Do a search and show the "searching" dropdown if the input is longer // than settings.minChars function do_search() { - var query = input_box.val().toLowerCase(); + var query = input_box.val(); if(query && query.length) { if(selected_token) { @@ -642,9 +642,9 @@ $.TokenList = function (input, url_or_data, settings) { // Do the actual search function run_search(query) { - var cached_results = cache.get(query); + var cached_results = cache.get(query.toLowerCase()); if(cached_results) { - populate_dropdown(query, cached_results); + populate_dropdown(query.toLowerCase(), cached_results); } else { // Are we doing an ajax search or local data search? if(settings.url) { @@ -680,7 +680,7 @@ $.TokenList = function (input, url_or_data, settings) { cache.add(query, settings.jsonContainer ? results[settings.jsonContainer] : results); // only populate the dropdown if the results are associated with the active search query - if(input_box.val().toLowerCase() === query) { + if(input_box.val().toLowerCase() === query.toLowerCase()) { populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results); } }; From 2851ff58061bfcd1c58250741ec7ad599c365be2 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Mon, 25 Apr 2011 11:42:34 -0300 Subject: [PATCH 05/63] added tokenListFocused class --- src/jquery.tokeninput.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 55bbc758..7cac1f22 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -35,6 +35,7 @@ var DEFAULT_SETTINGS = { // Default classes to use when theming var DEFAULT_CLASSES = { tokenList: "token-input-list", + tokenListFocused: "token-input-focus", token: "token-input-token", tokenDelete: "token-input-delete-token", selectedToken: "token-input-selected-token", @@ -144,9 +145,11 @@ $.TokenList = function (input, url_or_data, settings) { if (settings.tokenLimit === null || settings.tokenLimit !== token_count) { show_dropdown_hint(); } + token_list.addClass(settings.classes.tokenListFocused); }) .blur(function () { hide_dropdown(); + token_list.removeClass(settings.classes.tokenListFocused); }) .bind("keyup keydown blur update", resize_input) .keydown(function (event) { From 6995d0ed21e233ffa6c74f37512ca06c6960c70e Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Mon, 25 Apr 2011 16:20:00 -0300 Subject: [PATCH 06/63] added support to allow custom entry in token input by using allowCustomEntry property --- src/jquery.tokeninput.js | 75 +++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 7cac1f22..f85605d3 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -19,6 +19,7 @@ var DEFAULT_SETTINGS = { minChars: 1, tokenLimit: null, jsonContainer: null, + allowCustomEntry: false, method: "GET", contentType: "json", queryParam: "q", @@ -145,7 +146,9 @@ $.TokenList = function (input, url_or_data, settings) { if (settings.tokenLimit === null || settings.tokenLimit !== token_count) { show_dropdown_hint(); } - token_list.addClass(settings.classes.tokenListFocused); + if($(input_box).is(":visible")) { + token_list.addClass(settings.classes.tokenListFocused); + } }) .blur(function () { hide_dropdown(); @@ -181,16 +184,45 @@ $.TokenList = function (input, url_or_data, settings) { } } else { var dropdown_item = null; - - if(event.keyCode === KEY.DOWN || event.keyCode === KEY.RIGHT) { - dropdown_item = $(selected_dropdown_item).next(); + + if(settings.allowCustomEntry == true) { + + if(event.keyCode === KEY.DOWN || event.keyCode === KEY.RIGHT) { + if($(selected_dropdown_item).length) { + if($(selected_dropdown_item).next().length) { + dropdown_item = $(selected_dropdown_item).next(); + } else { + deselect_dropdown_item($(selected_dropdown_item)); + } + } else { + dropdown_item = $(dropdown).find('li:first-child'); + } + } else { + if($(selected_dropdown_item).length) { + if($(selected_dropdown_item).prev().length) { + dropdown_item = $(selected_dropdown_item).prev(); + } else { + deselect_dropdown_item($(selected_dropdown_item)); + } + } else { + dropdown_item = $(dropdown).find('li:last-child'); + } + } + } else { - dropdown_item = $(selected_dropdown_item).prev(); + + if(event.keyCode === KEY.DOWN || event.keyCode === KEY.RIGHT) { + dropdown_item = $(selected_dropdown_item).next(); + } else { + dropdown_item = $(selected_dropdown_item).prev(); + } + } - - if(dropdown_item.length) { + + if(dropdown_item != null) { select_dropdown_item(dropdown_item); } + return false; } break; @@ -218,11 +250,16 @@ $.TokenList = function (input, url_or_data, settings) { case KEY.ENTER: case KEY.NUMPAD_ENTER: case KEY.COMMA: - if(selected_dropdown_item) { - add_token($(selected_dropdown_item)); - return false; - } - break; + if(selected_dropdown_item) { + add_token($(selected_dropdown_item)); + return false; + } + + if(settings.allowCustomEntry == true) { + add_token($(input_box).val()); + return false; + } + break; case KEY.ESCAPE: hide_dropdown(); @@ -381,7 +418,11 @@ $.TokenList = function (input, url_or_data, settings) { // Add a token to the token list based on user input function add_token (item) { - var li_data = $.data(item.get(0), "tokeninput"); + if(typeof(item) === "string") { + var li_data = {id: item, name: item}; + } else { + var li_data = $.data(item.get(0), "tokeninput"); + } var callback = settings.onAdd; // See if the token already exists and select it if we don't want duplicates @@ -579,9 +620,11 @@ $.TokenList = function (input, url_or_data, settings) { } else { this_li.addClass(settings.classes.dropdownItem2); } - - if(index === 0) { - select_dropdown_item(this_li); + + if(settings.allowCustomEntry == false) { + if(index === 0) { + select_dropdown_item(this_li); + } } $.data(this_li.get(0), "tokeninput", value); From dafc3b1186d939efd0846c1bc433b07dd84cda12 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Mon, 25 Apr 2011 17:04:58 -0300 Subject: [PATCH 07/63] avoid adding empty items when using allowCustomEntry. --- src/jquery.tokeninput.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index f85605d3..72a0ff0c 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -255,7 +255,7 @@ $.TokenList = function (input, url_or_data, settings) { return false; } - if(settings.allowCustomEntry == true) { + if(settings.allowCustomEntry == true && $(input_box).val() != '') { add_token($(input_box).val()); return false; } From 82d507a1243a760673e44a4c4e3404a66d727de4 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Mon, 25 Apr 2011 17:27:48 -0300 Subject: [PATCH 08/63] fix keyboard navigation due to allowCustomEntry bug --- src/jquery.tokeninput.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 72a0ff0c..128736aa 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -209,6 +209,10 @@ $.TokenList = function (input, url_or_data, settings) { } } + if(dropdown_item != null) { + select_dropdown_item(dropdown_item); + } + } else { if(event.keyCode === KEY.DOWN || event.keyCode === KEY.RIGHT) { @@ -216,13 +220,13 @@ $.TokenList = function (input, url_or_data, settings) { } else { dropdown_item = $(selected_dropdown_item).prev(); } + + if(dropdown_item.length) { + select_dropdown_item(dropdown_item); + } } - if(dropdown_item != null) { - select_dropdown_item(dropdown_item); - } - return false; } break; From d649e71b00f8ddd815c8d66d6b0e361a9cf17286 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Tue, 26 Apr 2011 12:00:58 -0300 Subject: [PATCH 09/63] avoid inserting items when number of items already got to tokenLimit --- src/jquery.tokeninput.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 128736aa..1940bfe1 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -145,6 +145,8 @@ $.TokenList = function (input, url_or_data, settings) { .focus(function () { if (settings.tokenLimit === null || settings.tokenLimit !== token_count) { show_dropdown_hint(); + } else { + $(this).blur(); } if($(input_box).is(":visible")) { token_list.addClass(settings.classes.tokenListFocused); From ae393a3a060c1145040e6f15d4e818874784d147 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Tue, 26 Apr 2011 16:18:37 -0300 Subject: [PATCH 10/63] won't submit form if press enter in token input when allowCustomEntry is set to false and there is no selection in dropdown menu, pressing enter would submit the form. this fix this issue. --- src/jquery.tokeninput.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 1940bfe1..4ccee1dc 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -258,13 +258,12 @@ $.TokenList = function (input, url_or_data, settings) { case KEY.COMMA: if(selected_dropdown_item) { add_token($(selected_dropdown_item)); - return false; } if(settings.allowCustomEntry == true && $(input_box).val() != '') { add_token($(input_box).val()); - return false; } + return false; break; case KEY.ESCAPE: From 011aa4251f187c77b830d19a5fd5c0ebabe62b2d Mon Sep 17 00:00:00 2001 From: Sebastian Lehbert Date: Fri, 29 Apr 2011 19:09:21 +0200 Subject: [PATCH 11/63] Feature: Drag and Drop - Sortable functionality --- src/jquery.tokeninput.js | 106 +++++++++++++++++++++++++++++++- styles/token-input-facebook.css | 8 +++ styles/token-input-mac.css | 8 +++ styles/token-input.css | 8 +++ 4 files changed, 128 insertions(+), 2 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 914d6d17..cfbec317 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -43,7 +43,9 @@ var DEFAULT_CLASSES = { dropdownItem: "token-input-dropdown-item", dropdownItem2: "token-input-dropdown-item2", selectedDropdownItem: "token-input-selected-dropdown-item", - inputToken: "token-input-input-token" + inputToken: "token-input-input-token", + insertBefore: "token-input-insert-before", + insertAfter: "token-input-insert-after" }; // Input box position "enum" @@ -307,6 +309,15 @@ $.TokenList = function (input, url_or_data, settings) { letterSpacing: input_box.css("letterSpacing"), whiteSpace: "nowrap" }); + + // True during dragging process + var dragging = false; + + // the dragged Token + var dragToken; + + // the destination Token + var dragDestination; // Pre-populate list if items exist hidden_input.val(""); @@ -325,6 +336,7 @@ $.TokenList = function (input, url_or_data, settings) { // // Private functions // + function resize_input() { if(input_val === (input_val = input_box.val())) {return;} @@ -347,6 +359,8 @@ $.TokenList = function (input, url_or_data, settings) { var this_token = $("
  • "+ value +"

  • ") .addClass(settings.classes.token) .insertBefore(input_token); + + addDragFunctionality(this_token); // The 'delete token' button $("" + settings.deleteText + "") @@ -369,12 +383,16 @@ $.TokenList = function (input, url_or_data, settings) { var token_ids = $.map(saved_tokens, function (el) { return el.id; }); + + hidden_input.val(token_ids.join(settings.tokenDelimiter)); token_count += 1; return this_token; } + + // Add a token to the token list based on user input function add_token (item) { @@ -424,9 +442,93 @@ $.TokenList = function (input, url_or_data, settings) { callback.call(hidden_input,li_data); } } + + + // + // Drag and Drop Functionality + // + function addDragFunctionality(token) { + token.bind('mousedown',function(){ + var token = $(this) + dragToken = token; + token.addClass(settings.classes.selectedToken); + dragging= true; + $(document).one('mouseup',function(){ + token.removeClass(settings.classes.selectedToken); + dragging=false; + move_token(token, dragDestination); + reindex_results(); + }); + return false; + }) + .bind('mouseover',function(){ + if(!dragging) return; + dragDestination = $(this); + if(is_after(dragToken, dragDestination)) { + dragDestination.addClass(settings.classes.insertAfter); + } else { + dragDestination.addClass(settings.classes.insertBefore); + }; + }).bind('mouseout', function(){ + if(!dragging) return; + $(this).removeClass(settings.classes.insertBefore); + $(this).removeClass(settings.classes.insertAfter); + }).bind('mouseup', function(){ + $(this).removeClass(settings.classes.insertBefore); + $(this).removeClass(settings.classes.insertAfter); + }); + } + + + function move_token(token, destinationToken) { + if(token.get(0) == destinationToken.get(0)) return; + + if(is_after(token, destinationToken)) { + token.insertAfter(destinationToken); + } else { + token.insertBefore(destinationToken); + } + + + } + + function is_after(first, last) { + index_tokens(); + first = $.data(first.get(0), "tokeninput") + last = $.data(last.get(0), "tokeninput") + return last.index > first.index + } + + + function index_tokens() { + var i = 0; + token_list.find('li').each(function(){ + var data = $.data(this, "tokeninput"); + if(data){ data.index = i; } + i++; + }); + } + + function reindex_results() { + var ids = [], tokens = []; + token_list.find('li').each(function(){ + var data = $.data(this, "tokeninput"); + if(data){ + ids.push(data.id); + tokens.push(data); + }; + }); + saved_tokens = tokens; + hidden_input.val(ids.join(settings.tokenDelimiter)); + } + + + // end Drag and Drop Functionality + + // Select a token in the token list - function select_token (token) { + function select_token(token) { token.addClass(settings.classes.selectedToken); selected_token = token.get(0); diff --git a/styles/token-input-facebook.css b/styles/token-input-facebook.css index b4d8e922..48d80756 100644 --- a/styles/token-input-facebook.css +++ b/styles/token-input-facebook.css @@ -70,6 +70,14 @@ li.token-input-input-token-facebook { list-style-type: none; } +li.token-input-insert-before-facebook { + border-left: 1px solid red; +} + +li.token-input-insert-after-facebook { + border-right: 1px solid red; +} + div.token-input-dropdown-facebook { position: absolute; width: 400px; diff --git a/styles/token-input-mac.css b/styles/token-input-mac.css index 18522f05..d5eed33f 100644 --- a/styles/token-input-mac.css +++ b/styles/token-input-mac.css @@ -112,6 +112,14 @@ li.token-input-input-token-mac input { margin: 0; } +li.token-input-insert-before-mac { + border-left: 1px solid red; +} + +li.token-input-insert-after-mac { + border-right: 1px solid red; +} + div.token-input-dropdown-mac { position: absolute; border: 1px solid #A4BDEC; diff --git a/styles/token-input.css b/styles/token-input.css index 03bb01c4..a11f7811 100644 --- a/styles/token-input.css +++ b/styles/token-input.css @@ -62,6 +62,14 @@ li.token-input-selected-token span { color: #bbb; } +li.token-input-insert-before { + border-top: 1px solid red; +} + +li.token-input-insert-after { + border-bottom: 1px solid red; +} + div.token-input-dropdown { position: absolute; width: 400px; From 894ec9f0a8fc7917f87dbb9f0ea7f5e14ccb25e1 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Sat, 30 Apr 2011 00:04:05 -0300 Subject: [PATCH 12/63] avoid showing noResultsText if allowCustomEntry is set to true since it is always possible to add a custom entry if nothing was found --- src/jquery.tokeninput.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 4ccee1dc..f619840f 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -643,9 +643,11 @@ $.TokenList = function (input, url_or_data, settings) { dropdown_ul.show(); } } else { - if(settings.noResultsText) { + if(settings.noResultsText && !settings.allowCustomEntry) { dropdown.html("

    "+settings.noResultsText+"

    "); show_dropdown(); + } else { + hide_dropdown(); } } } From 4525dbcb234da2c711b5770bbabc8bae09545840 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Sat, 30 Apr 2011 00:04:31 -0300 Subject: [PATCH 13/63] added demo to allowCustomEntry property --- demo.html | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/demo.html b/demo.html index 47cb9341..f4f774f1 100644 --- a/demo.html +++ b/demo.html @@ -212,5 +212,18 @@

    Using onAdd and onDelete Callbacks

    }); + +

    Allowing Custom Entry

    +
    + + + +
    - \ No newline at end of file + From f9dd456309cdcc87ce7020bed76a2cab4a9f2de3 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Sat, 30 Apr 2011 00:26:11 -0300 Subject: [PATCH 14/63] proper action when using delete button with selected token --- src/jquery.tokeninput.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index f619840f..19e0a220 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -70,6 +70,7 @@ var KEY = { UP: 38, RIGHT: 39, DOWN: 40, + DELETE: 46, NUMPAD_ENTER: 108, COMMA: 188 }; @@ -252,6 +253,18 @@ $.TokenList = function (input, url_or_data, settings) { } break; + case KEY.DELETE: + next_token = input_token.next(); + if(!$(this).val().length) { + if(selected_token) { + delete_token($(selected_token)); + } else if(next_token.length) { + select_token($(next_token.get(0))); + } + } + + break; + case KEY.TAB: case KEY.ENTER: case KEY.NUMPAD_ENTER: From 51fe8e02fda92e53dac22459478efa52e6f7ba0a Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Sat, 30 Apr 2011 00:27:26 -0300 Subject: [PATCH 15/63] deselect token when input is looses focus and makes input focus when clicking in a token (to proper make keyboard work again) --- src/jquery.tokeninput.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 19e0a220..abc2da0a 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -155,6 +155,7 @@ $.TokenList = function (input, url_or_data, settings) { }) .blur(function () { hide_dropdown(); + deselect_token($(selected_token)); token_list.removeClass(settings.classes.tokenListFocused); }) .bind("keyup keydown blur update", resize_input) @@ -314,6 +315,7 @@ $.TokenList = function (input, url_or_data, settings) { .click(function (event) { var li = $(event.target).closest("li"); if(li && li.get(0) && $.data(li.get(0), "tokeninput")) { + input_box.focus(); toggle_select_token(li); } else { // Deselect selected token From 6ddca5e02d28c2036ad4e4088010957e6e94db1c Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Sat, 30 Apr 2011 00:47:06 -0300 Subject: [PATCH 16/63] make keyboard navigation fully work tab will now enable to jump between different objects, like any regular form element. --- src/jquery.tokeninput.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index abc2da0a..fb4f4bfc 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -154,9 +154,9 @@ $.TokenList = function (input, url_or_data, settings) { } }) .blur(function () { - hide_dropdown(); deselect_token($(selected_token)); token_list.removeClass(settings.classes.tokenListFocused); + hide_dropdown(); }) .bind("keyup keydown blur update", resize_input) .keydown(function (event) { @@ -270,6 +270,13 @@ $.TokenList = function (input, url_or_data, settings) { case KEY.ENTER: case KEY.NUMPAD_ENTER: case KEY.COMMA: + + if(event.keyCode == KEY.TAB && !$(input_box).val().length) { + hide_dropdown(); + $(this).blur(); + return true; + } + if(selected_dropdown_item) { add_token($(selected_dropdown_item)); } @@ -277,6 +284,7 @@ $.TokenList = function (input, url_or_data, settings) { if(settings.allowCustomEntry == true && $(input_box).val() != '') { add_token($(input_box).val()); } + return false; break; From e6e18c362774e30e7fb0b38b069dff238886df78 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Sat, 30 Apr 2011 00:55:53 -0300 Subject: [PATCH 17/63] allow moving caret position while typing --- src/jquery.tokeninput.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index fb4f4bfc..362f1bfd 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -231,7 +231,8 @@ $.TokenList = function (input, url_or_data, settings) { } - return false; + // we need to allow caret moving here + return true; } break; From eb613ab567e05e5b77a68020fa9f656ab7a4eeec Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Sat, 30 Apr 2011 01:37:47 -0300 Subject: [PATCH 18/63] deselect element if it was selected by click and then keyboard was used to move between tokens --- src/jquery.tokeninput.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 362f1bfd..a3a93c8b 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -154,7 +154,9 @@ $.TokenList = function (input, url_or_data, settings) { } }) .blur(function () { - deselect_token($(selected_token)); + if(selected_token) { + deselect_token($(selected_token)); + } token_list.removeClass(settings.classes.tokenListFocused); hide_dropdown(); }) @@ -180,9 +182,15 @@ $.TokenList = function (input, url_or_data, settings) { deselect_token($(selected_token), POSITION.AFTER); } } else if((event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) && previous_token.length) { + if(selected_token) { + deselect_token($(selected_token)); + } // 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) { + if(selected_token) { + deselect_token($(selected_token)); + } // We are moving right, select the next token if it exists select_token($(next_token.get(0))); } From 39fd58e2f977db57f86cb0252172fa0c52f969d9 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Sat, 30 Apr 2011 01:43:42 -0300 Subject: [PATCH 19/63] hide caret when a token is selected caret will never hide when using IE however --- src/jquery.tokeninput.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index a3a93c8b..a47a2d18 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -511,7 +511,7 @@ $.TokenList = function (input, url_or_data, settings) { selected_token = token.get(0); // Hide input box - input_box.val(""); + input_box.val("").css('color', 'transparent'); // Hide dropdown if it is visible (eg if we clicked to select token) hide_dropdown(); @@ -521,6 +521,8 @@ $.TokenList = function (input, url_or_data, settings) { function deselect_token (token, position) { token.removeClass(settings.classes.selectedToken); selected_token = null; + + input_box.css('color', ''); if(position === POSITION.BEFORE) { input_token.insertBefore(token); @@ -566,7 +568,7 @@ $.TokenList = function (input, url_or_data, settings) { selected_token = null; // Show the input box and give it focus again - input_box.focus(); + input_box.focus().css('color', ''); // Remove this token from the saved list saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1)); From 31e81aec9c0b69d65e0aa8a682e084e06b6b9f2a Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Sat, 30 Apr 2011 02:13:36 -0300 Subject: [PATCH 20/63] will only add a custom token after checking trimmed element --- src/jquery.tokeninput.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index a47a2d18..840d8ea8 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -290,7 +290,7 @@ $.TokenList = function (input, url_or_data, settings) { add_token($(selected_dropdown_item)); } - if(settings.allowCustomEntry == true && $(input_box).val() != '') { + if(settings.allowCustomEntry == true && $.trim($(input_box).val()) != '') { add_token($(input_box).val()); } @@ -455,6 +455,7 @@ $.TokenList = function (input, url_or_data, settings) { // Add a token to the token list based on user input function add_token (item) { + if(typeof(item) === "string") { var li_data = {id: item, name: item}; } else { From 8a91de49ad4f15221fddf58a37463e1cd1566a7c Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Sat, 30 Apr 2011 03:44:54 -0300 Subject: [PATCH 21/63] recreated the entire way tokens are saved. this fixes a problem that was letting deleted tokens still be sent to the server, and also provides a much more intelligent way to insert and delete tokens from the list. --- src/jquery.tokeninput.js | 55 +++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 840d8ea8..658d1486 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -126,7 +126,7 @@ $.TokenList = function (input, url_or_data, settings) { // Save the tokens - var saved_tokens = []; + var saved_tokens = {}; // Keep track of the number of tokens in the list var token_count = 0; @@ -309,6 +309,12 @@ $.TokenList = function (input, url_or_data, settings) { break; } }); + + var unique_counter = 0; + function get_unique_id() { + unique_counter++; + return 'u' + unique_counter; + } // Keep a reference to the original input box var hidden_input = $(input) @@ -421,9 +427,13 @@ $.TokenList = function (input, url_or_data, settings) { // Inner function to a token to the list function insert_token(object) { + + var uniqueid = get_unique_id(); + var this_token = $("
  • "+ object.name +"

  • ") .addClass(settings.classes.token) - .insertBefore(input_token); + .insertBefore(input_token) + .attr('data-uniqueid', uniqueid); // The 'delete token' button $("" + settings.deleteText + "") @@ -439,15 +449,12 @@ $.TokenList = function (input, url_or_data, settings) { $.data(this_token.get(0), "tokeninput", token_data); // 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)); + + saved_tokens[uniqueid] = token_data; + update_hidden_input(); + selected_token_index++; - // Update the hidden input - var token_ids = $.map(saved_tokens, function (el) { - return el.id; - }); - hidden_input.val(token_ids.join(settings.tokenDelimiter)); - token_count += 1; return this_token; @@ -457,7 +464,7 @@ $.TokenList = function (input, url_or_data, settings) { function add_token (item) { if(typeof(item) === "string") { - var li_data = {id: item, name: item}; + var li_data = {name: item}; } else { var li_data = $.data(item.get(0), "tokeninput"); } @@ -563,7 +570,9 @@ $.TokenList = function (input, url_or_data, settings) { var index = token.prevAll().length; if(index > selected_token_index) index--; - + + var uniqueid = $(token).attr('data-uniqueid'); + // Delete the token token.remove(); selected_token = null; @@ -572,15 +581,11 @@ $.TokenList = function (input, url_or_data, settings) { input_box.focus().css('color', ''); // Remove this token from the saved list - saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1)); + delete saved_tokens[uniqueid]; + update_hidden_input(); + if(index < selected_token_index) selected_token_index--; - // Update the hidden input - var token_ids = $.map(saved_tokens, function (el) { - return el.id; - }); - hidden_input.val(token_ids.join(settings.tokenDelimiter)); - token_count -= 1; if(settings.tokenLimit !== null) { @@ -595,6 +600,20 @@ $.TokenList = function (input, url_or_data, settings) { callback.call(hidden_input,token_data); } } + + // Update the hidden input value + function update_hidden_input() { + var token_ids = []; + $.each(saved_tokens, function (index, value) { + if(value.id) { + token_ids[token_ids.length] = value.id; + } else { + token_ids[token_ids.length] = "'" + value.name.replace(/\'/gi, "\\'") + "'"; + } + }); + + hidden_input.val(token_ids.join(settings.tokenDelimiter)); + } // Hide and clear the results dropdown function hide_dropdown () { From ffb38ceb4b794e45ffcc06530fde2988cd5be863 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Tue, 3 May 2011 10:09:41 -0300 Subject: [PATCH 22/63] only up and down keys are used by now to select dropdown item in order to allow moving caret position while typing. --- src/jquery.tokeninput.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 658d1486..3174a70e 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -199,7 +199,7 @@ $.TokenList = function (input, url_or_data, settings) { if(settings.allowCustomEntry == true) { - if(event.keyCode === KEY.DOWN || event.keyCode === KEY.RIGHT) { + if(event.keyCode === KEY.DOWN) { if($(selected_dropdown_item).length) { if($(selected_dropdown_item).next().length) { dropdown_item = $(selected_dropdown_item).next(); @@ -209,7 +209,7 @@ $.TokenList = function (input, url_or_data, settings) { } else { dropdown_item = $(dropdown).find('li:first-child'); } - } else { + } else if(event.keyCode === KEY.UP) { if($(selected_dropdown_item).length) { if($(selected_dropdown_item).prev().length) { dropdown_item = $(selected_dropdown_item).prev(); @@ -227,9 +227,9 @@ $.TokenList = function (input, url_or_data, settings) { } else { - if(event.keyCode === KEY.DOWN || event.keyCode === KEY.RIGHT) { + if(event.keyCode === KEY.DOWN) { dropdown_item = $(selected_dropdown_item).next(); - } else { + } else if(event.keyCode === KEY.UP) { dropdown_item = $(selected_dropdown_item).prev(); } @@ -239,8 +239,12 @@ $.TokenList = function (input, url_or_data, settings) { } - // we need to allow caret moving here - return true; + if(event.keyCode === KEY.LEFT || event.keyCode === KEY.RIGHT) { + // we need to allow caret moving here + return true; + } else { + return false; + } } break; From e8ba00d57b054cbb23f095c072b8c7fccc2dcf7d Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Tue, 3 May 2011 14:37:14 -0300 Subject: [PATCH 23/63] checking for existing data both using id and name (name when id is not available) --- src/jquery.tokeninput.js | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 3174a70e..2c7485ad 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -146,8 +146,6 @@ $.TokenList = function (input, url_or_data, settings) { .focus(function () { if (settings.tokenLimit === null || settings.tokenLimit !== token_count) { show_dropdown_hint(); - } else { - $(this).blur(); } if($(input_box).is(":visible")) { token_list.addClass(settings.classes.tokenListFocused); @@ -480,9 +478,15 @@ $.TokenList = function (input, url_or_data, settings) { token_list.children().each(function () { var existing_token = $(this); var existing_data = $.data(existing_token.get(0), "tokeninput"); - if(existing_data && existing_data.id === li_data.id) { - found_existing_token = existing_token; - return false; + if(existing_data) { + if(existing_data.id && existing_data.id === li_data.id) { + found_existing_token = existing_token; + return false; + } + if(!existing_data.id && existing_data.name === li_data.name) { + found_existing_token = existing_token; + return false; + } } }); @@ -496,6 +500,9 @@ $.TokenList = function (input, url_or_data, settings) { // Insert the new tokens insert_token(li_data); + + // Clear input box + input_box.val(""); // Check the token limit if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) { @@ -503,9 +510,6 @@ $.TokenList = function (input, url_or_data, settings) { hide_dropdown(); } else { input_box.focus(); - - // Clear input box - input_box.val(""); // Don't show the help dropdown, they've got the idea hide_dropdown(); @@ -732,6 +736,10 @@ $.TokenList = function (input, url_or_data, settings) { // than settings.minChars function do_search() { var query = input_box.val(); + + if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) { + return false; + } if(query && query.length) { if(selected_token) { From 0900eb64c89466f506a6b6d9fd8956a7388d478b Mon Sep 17 00:00:00 2001 From: Sebastian Lehbert Date: Fri, 6 May 2011 14:56:27 +0200 Subject: [PATCH 24/63] make sortable Feature optional --- src/jquery.tokeninput.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index cfbec317..d06eb456 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -26,6 +26,7 @@ var DEFAULT_SETTINGS = { preventDuplicates: false, prePopulate: null, processPrePopulate: false, + makeSortable: false, animateDropdown: true, onResult: null, onAdd: null, @@ -360,7 +361,9 @@ $.TokenList = function (input, url_or_data, settings) { .addClass(settings.classes.token) .insertBefore(input_token); - addDragFunctionality(this_token); + if(settings.makeSortable) { + addDragFunctionality(this_token); + }; // The 'delete token' button $("" + settings.deleteText + "") From 5681faf69b3abe382fb88efbd3069d22dc5c30af Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Tue, 10 May 2011 00:13:06 -0300 Subject: [PATCH 25/63] added initial support for parseName setting --- demo.html | 21 +++++++++++++++++++++ src/jquery.tokeninput.js | 20 ++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/demo.html b/demo.html index f4f774f1..5a1a6014 100644 --- a/demo.html +++ b/demo.html @@ -225,5 +225,26 @@

    Allowing Custom Entry

    }); + +

    Using local data with parseName function

    +
    + + + +
    diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 658d1486..f0fa5ee3 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -27,6 +27,7 @@ var DEFAULT_SETTINGS = { preventDuplicates: false, prePopulate: null, processPrePopulate: false, + parseName: null, animateDropdown: true, onResult: null, onAdd: null, @@ -430,7 +431,14 @@ $.TokenList = function (input, url_or_data, settings) { var uniqueid = get_unique_id(); - var this_token = $("
  • "+ object.name +"

  • ") + var token_name; + if(settings.parseName) { + token_name = settings.parseName(object); + } else { + token_name = object.name; + } + + var this_token = $("
  • "+ token_name +"

  • ") .addClass(settings.classes.token) .insertBefore(input_token) .attr('data-uniqueid', uniqueid); @@ -671,7 +679,15 @@ $.TokenList = function (input, url_or_data, settings) { .hide(); $.each(results, function(index, value) { - var this_li = $("
  • " + highlight_term(value.name, query) + "
  • ") + + var token_name; + if(settings.parseName) { + token_name = settings.parseName(value); + } else { + token_name = value.name; + } + + var this_li = $("
  • " + highlight_term(token_name, query) + "
  • ") .appendTo(dropdown_ul); if(index % 2) { From 236bfcfcc7c87457b6da93ebe809b37a9a7ec614 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Tue, 10 May 2011 00:51:39 -0300 Subject: [PATCH 26/63] improved local search ignoring diacritical marks --- src/jquery.tokeninput.js | 105 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 204afbf9..bf2bb8ea 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -817,7 +817,11 @@ $.TokenList = function (input, url_or_data, settings) { } else if(settings.local_data) { // Do the search through local data var results = $.grep(settings.local_data, function (row) { - return row.name.toLowerCase().indexOf(query.toLowerCase()) > -1; + if(row.name.toLowerCase().removeDiacritics().indexOf(query.toLowerCase().removeDiacritics()) > -1) { + return true; + } else { + return false; + } }); if($.isFunction(settings.onResult)) { @@ -830,6 +834,105 @@ $.TokenList = function (input, url_or_data, settings) { } }; +// Remove Diacritics for a clever local search +String.prototype.removeDiacritics = function () { + + var str = this; + + // diacriticts map from http://lehelk.com/2011/05/06/script-to-remove-diacritics/ + var defaultDiacriticsRemovalMap = [ + {o:'A', r:/[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g}, + {o:'AA',r:/[\uA732]/g}, + {o:'AE',r:/[\u00C6\u01FC\u01E2]/g}, + {o:'AO',r:/[\uA734]/g}, + {o:'AU',r:/[\uA736]/g}, + {o:'AV',r:/[\uA738\uA73A]/g}, + {o:'AY',r:/[\uA73C]/g}, + {o:'B', r:/[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g}, + {o:'C', r:/[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g}, + {o:'D', r:/[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g}, + {o:'DZ',r:/[\u01F1\u01C4]/g}, + {o:'Dz',r:/[\u01F2\u01C5]/g}, + {o:'E', r:/[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g}, + {o:'F', r:/[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g}, + {o:'G', r:/[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g}, + {o:'H', r:/[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g}, + {o:'I', r:/[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g}, + {o:'J', r:/[\u004A\u24BF\uFF2A\u0134\u0248]/g}, + {o:'K', r:/[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g}, + {o:'L', r:/[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g}, + {o:'LJ',r:/[\u01C7]/g}, + {o:'Lj',r:/[\u01C8]/g}, + {o:'M', r:/[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g}, + {o:'N', r:/[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g}, + {o:'NJ',r:/[\u01CA]/g}, + {o:'Nj',r:/[\u01CB]/g}, + {o:'O', r:/[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g}, + {o:'OI',r:/[\u01A2]/g}, + {o:'OO',r:/[\uA74E]/g}, + {o:'OU',r:/[\u0222]/g}, + {o:'P', r:/[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g}, + {o:'Q', r:/[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g}, + {o:'R', r:/[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g}, + {o:'S', r:/[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g}, + {o:'T', r:/[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g}, + {o:'TZ',r:/[\uA728]/g}, + {o:'U', r:/[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g}, + {o:'V', r:/[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g}, + {o:'VY',r:/[\uA760]/g}, + {o:'W', r:/[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g}, + {o:'X', r:/[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g}, + {o:'Y', r:/[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g}, + {o:'Z', r:/[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g}, + {o:'a', r:/[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g}, + {o:'aa',r:/[\uA733]/g}, + {o:'ae',r:/[\u00E6\u01FD\u01E3]/g}, + {o:'ao',r:/[\uA735]/g}, + {o:'au',r:/[\uA737]/g}, + {o:'av',r:/[\uA739\uA73B]/g}, + {o:'ay',r:/[\uA73D]/g}, + {o:'b', r:/[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g}, + {o:'c', r:/[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g}, + {o:'d', r:/[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g}, + {o:'dz',r:/[\u01F3\u01C6]/g}, + {o:'e', r:/[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g}, + {o:'f', r:/[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g}, + {o:'g', r:/[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g}, + {o:'h', r:/[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g}, + {o:'hv',r:/[\u0195]/g}, + {o:'i', r:/[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g}, + {o:'j', r:/[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g}, + {o:'k', r:/[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g}, + {o:'l', r:/[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g}, + {o:'lj',r:/[\u01C9]/g}, + {o:'m', r:/[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g}, + {o:'n', r:/[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g}, + {o:'nj',r:/[\u01CC]/g}, + {o:'o', r:/[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g}, + {o:'oi',r:/[\u01A3]/g}, + {o:'ou',r:/[\u0223]/g}, + {o:'oo',r:/[\uA74F]/g}, + {o:'p',r:/[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g}, + {o:'q',r:/[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g}, + {o:'r',r:/[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g}, + {o:'s',r:/[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g}, + {o:'t',r:/[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g}, + {o:'tz',r:/[\uA729]/g}, + {o:'u',r:/[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g}, + {o:'v',r:/[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g}, + {o:'vy',r:/[\uA761]/g}, + {o:'w',r:/[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g}, + {o:'x',r:/[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g}, + {o:'y',r:/[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g}, + {o:'z',r:/[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g} + ]; + + for(var i = 0; i < defaultDiacriticsRemovalMap.length; i++) { + str = str.replace(defaultDiacriticsRemovalMap[i].r, defaultDiacriticsRemovalMap[i].o); + } + return str; +} + // Really basic cache for the results $.TokenList.Cache = function (options) { var settings = $.extend({ From 69e180c5aa74831c7899da881fd6d711d7067768 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Tue, 10 May 2011 01:01:25 -0300 Subject: [PATCH 27/63] avoid giving errors when add_token is called without an object and when dropdown_item is null --- src/jquery.tokeninput.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index bf2bb8ea..576cc8ba 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -234,7 +234,7 @@ $.TokenList = function (input, url_or_data, settings) { dropdown_item = $(selected_dropdown_item).prev(); } - if(dropdown_item.length) { + if(dropdown_item && dropdown_item.length) { select_dropdown_item(dropdown_item); } @@ -480,6 +480,11 @@ $.TokenList = function (input, url_or_data, settings) { } else { var li_data = $.data(item.get(0), "tokeninput"); } + + if(!li_data) { + return false; + } + var callback = settings.onAdd; // See if the token already exists and select it if we don't want duplicates From 4ee6cdbce6306ca931de5b1eae98d4322c59f919 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Tue, 10 May 2011 01:11:02 -0300 Subject: [PATCH 28/63] changed example to search for terms that also contain accents --- demo.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/demo.html b/demo.html index 5a1a6014..71b963c4 100644 --- a/demo.html +++ b/demo.html @@ -1,5 +1,7 @@ + + @@ -236,7 +238,9 @@

    Using local data with parseName function

    {id: 1, name: 'New York', state: 'NY'}, {id: 2, name: 'Palo Alto', state: 'CA'}, {id: 3, name: 'San Francisco', state: 'CA'}, - {id: 4, name: 'Boston', state: 'MA'} + {id: 4, name: 'Boston', state: 'MA'}, + {id: 5, name: 'São Paulo', state: 'SP'}, + {id: 6, name: 'Vitória', state: 'ES'} ], { parseName: function(element) { From bdff27e1a77c37174b25a42db8c56717669d3d09 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Tue, 10 May 2011 01:33:58 -0300 Subject: [PATCH 29/63] added searchColumns option --- demo.html | 5 +++-- src/jquery.tokeninput.js | 13 ++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/demo.html b/demo.html index 71b963c4..8842ca07 100644 --- a/demo.html +++ b/demo.html @@ -228,7 +228,7 @@

    Allowing Custom Entry

    -

    Using local data with parseName function

    +

    Using local data with parseName function and searchColumns setting

    @@ -245,7 +245,8 @@

    Using local data with parseName function

    { parseName: function(element) { return element.name + ', '+ element.state; - } + }, + searchColumns: ['name', 'state'] }); }); diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 576cc8ba..93f9f5c7 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -28,6 +28,7 @@ var DEFAULT_SETTINGS = { prePopulate: null, processPrePopulate: false, parseName: null, + searchColumns: ['name'], animateDropdown: true, onResult: null, onAdd: null, @@ -822,11 +823,13 @@ $.TokenList = function (input, url_or_data, settings) { } else if(settings.local_data) { // Do the search through local data var results = $.grep(settings.local_data, function (row) { - if(row.name.toLowerCase().removeDiacritics().indexOf(query.toLowerCase().removeDiacritics()) > -1) { - return true; - } else { - return false; - } + var founded = false; + $(settings.searchColumns).each(function(i, item) { + if(row[item].toString().toLowerCase().removeDiacritics().indexOf(query.toString().toLowerCase().removeDiacritics()) > -1) { + founded = true; + } + }); + return founded; }); if($.isFunction(settings.onResult)) { From bbe0c18485901eab8584b6262ec4e039e94d61b2 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Tue, 10 May 2011 02:10:54 -0300 Subject: [PATCH 30/63] fixed highlight_term to also accept strings with/without accents this also involved using a different aproach for removeDiacritics function --- src/jquery.tokeninput.js | 178 +++++++++++++++++---------------------- 1 file changed, 77 insertions(+), 101 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 93f9f5c7..8ff7bafc 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -664,13 +664,39 @@ $.TokenList = function (input, url_or_data, settings) { } } - RegExp.escape = function(text) { - return text.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&"); - }; - // Highlight the query part of the search term - function highlight_term(value, term) { - return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + RegExp.escape(term) + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "$1"); + // from http://www.alistapart.com/articles/accent-folding-for-auto-complete/ + function highlight_term(str, q) { + var str_folded = str.toString().removeDiacritics().toLowerCase().replace(/[<>]+/g, ''); + var q_folded = q.toString().removeDiacritics().toLowerCase().replace(/[<>]+/g, ''); + + // create an intermediary string with hilite hints + // example: fulani pez + var re = new RegExp(q_folded, 'g'); + var hilite_hints = str_folded.replace(re, '<'+q_folded+'>'); + + var spos = 0; + var highlighted = ''; + + // walk down the original string and the hilite hint + // string in parallel. when you encounter a < or > hint, + // append the opening / closing tag in our final string. + // if the current char is not a hint, append the corresponding + // char from the *original* string to our final string and + // advance the original string's pointer. + for (var i=0; i') { + highlighted += ''; + } else { + spos += 1; + highlighted += c; + } + } + return highlighted; } // Populate the results dropdown with some results @@ -842,104 +868,54 @@ $.TokenList = function (input, url_or_data, settings) { } }; -// Remove Diacritics for a clever local search +// Remove Diacritics +// from http://www.alistapart.com/articles/accent-folding-for-auto-complete/ String.prototype.removeDiacritics = function () { - var str = this; - - // diacriticts map from http://lehelk.com/2011/05/06/script-to-remove-diacritics/ - var defaultDiacriticsRemovalMap = [ - {o:'A', r:/[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g}, - {o:'AA',r:/[\uA732]/g}, - {o:'AE',r:/[\u00C6\u01FC\u01E2]/g}, - {o:'AO',r:/[\uA734]/g}, - {o:'AU',r:/[\uA736]/g}, - {o:'AV',r:/[\uA738\uA73A]/g}, - {o:'AY',r:/[\uA73C]/g}, - {o:'B', r:/[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g}, - {o:'C', r:/[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g}, - {o:'D', r:/[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g}, - {o:'DZ',r:/[\u01F1\u01C4]/g}, - {o:'Dz',r:/[\u01F2\u01C5]/g}, - {o:'E', r:/[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g}, - {o:'F', r:/[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g}, - {o:'G', r:/[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g}, - {o:'H', r:/[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g}, - {o:'I', r:/[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g}, - {o:'J', r:/[\u004A\u24BF\uFF2A\u0134\u0248]/g}, - {o:'K', r:/[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g}, - {o:'L', r:/[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g}, - {o:'LJ',r:/[\u01C7]/g}, - {o:'Lj',r:/[\u01C8]/g}, - {o:'M', r:/[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g}, - {o:'N', r:/[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g}, - {o:'NJ',r:/[\u01CA]/g}, - {o:'Nj',r:/[\u01CB]/g}, - {o:'O', r:/[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g}, - {o:'OI',r:/[\u01A2]/g}, - {o:'OO',r:/[\uA74E]/g}, - {o:'OU',r:/[\u0222]/g}, - {o:'P', r:/[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g}, - {o:'Q', r:/[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g}, - {o:'R', r:/[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g}, - {o:'S', r:/[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g}, - {o:'T', r:/[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g}, - {o:'TZ',r:/[\uA728]/g}, - {o:'U', r:/[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g}, - {o:'V', r:/[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g}, - {o:'VY',r:/[\uA760]/g}, - {o:'W', r:/[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g}, - {o:'X', r:/[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g}, - {o:'Y', r:/[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g}, - {o:'Z', r:/[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g}, - {o:'a', r:/[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g}, - {o:'aa',r:/[\uA733]/g}, - {o:'ae',r:/[\u00E6\u01FD\u01E3]/g}, - {o:'ao',r:/[\uA735]/g}, - {o:'au',r:/[\uA737]/g}, - {o:'av',r:/[\uA739\uA73B]/g}, - {o:'ay',r:/[\uA73D]/g}, - {o:'b', r:/[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g}, - {o:'c', r:/[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g}, - {o:'d', r:/[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g}, - {o:'dz',r:/[\u01F3\u01C6]/g}, - {o:'e', r:/[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g}, - {o:'f', r:/[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g}, - {o:'g', r:/[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g}, - {o:'h', r:/[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g}, - {o:'hv',r:/[\u0195]/g}, - {o:'i', r:/[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g}, - {o:'j', r:/[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g}, - {o:'k', r:/[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g}, - {o:'l', r:/[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g}, - {o:'lj',r:/[\u01C9]/g}, - {o:'m', r:/[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g}, - {o:'n', r:/[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g}, - {o:'nj',r:/[\u01CC]/g}, - {o:'o', r:/[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g}, - {o:'oi',r:/[\u01A3]/g}, - {o:'ou',r:/[\u0223]/g}, - {o:'oo',r:/[\uA74F]/g}, - {o:'p',r:/[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g}, - {o:'q',r:/[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g}, - {o:'r',r:/[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g}, - {o:'s',r:/[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g}, - {o:'t',r:/[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g}, - {o:'tz',r:/[\uA729]/g}, - {o:'u',r:/[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g}, - {o:'v',r:/[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g}, - {o:'vy',r:/[\uA761]/g}, - {o:'w',r:/[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g}, - {o:'x',r:/[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g}, - {o:'y',r:/[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g}, - {o:'z',r:/[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g} - ]; - - for(var i = 0; i < defaultDiacriticsRemovalMap.length; i++) { - str = str.replace(defaultDiacriticsRemovalMap[i].r, defaultDiacriticsRemovalMap[i].o); + var str = this; + + if (!str) { + return ''; } - return str; -} + + var accent_map = { + 'ẚ':'a', 'Á':'a', 'á':'a', 'À':'a', 'à':'a', 'Ă':'a', 'ă':'a', 'Ắ':'a', 'ắ':'a', 'Ằ':'a', 'ằ':'a', 'Ẵ':'a', 'ẵ':'a', 'Ẳ':'a', 'ẳ':'a', 'Â':'a', 'â':'a', 'Ấ':'a', 'ấ':'a', 'Ầ':'a', 'ầ':'a', 'Ẫ':'a', 'ẫ':'a', 'Ẩ':'a', 'ẩ':'a', 'Ǎ':'a', 'ǎ':'a', 'Å':'a', 'å':'a', 'Ǻ':'a', 'ǻ':'a', 'Ä':'a', 'ä':'a', 'Ǟ':'a', 'ǟ':'a', 'Ã':'a', 'ã':'a', 'Ȧ':'a', 'ȧ':'a', 'Ǡ':'a', 'ǡ':'a', 'Ą':'a', 'ą':'a', 'Ā':'a', 'ā':'a', 'Ả':'a', 'ả':'a', 'Ȁ':'a', 'ȁ':'a', 'Ȃ':'a', 'ȃ':'a', 'Ạ':'a', 'ạ':'a', 'Ặ':'a', 'ặ':'a', 'Ậ':'a', 'ậ':'a', 'Ḁ':'a', 'ḁ':'a', 'Ⱥ':'a', 'ⱥ':'a', 'Ǽ':'a', 'ǽ':'a', 'Ǣ':'a', 'ǣ':'a', + 'Ḃ':'b', 'ḃ':'b', 'Ḅ':'b', 'ḅ':'b', 'Ḇ':'b', 'ḇ':'b', 'Ƀ':'b', 'ƀ':'b', 'ᵬ':'b', 'Ɓ':'b', 'ɓ':'b', 'Ƃ':'b', 'ƃ':'b', + 'Ć':'c', 'ć':'c', 'Ĉ':'c', 'ĉ':'c', 'Č':'c', 'č':'c', 'Ċ':'c', 'ċ':'c', 'Ç':'c', 'ç':'c', 'Ḉ':'c', 'ḉ':'c', 'Ȼ':'c', 'ȼ':'c', 'Ƈ':'c', 'ƈ':'c', 'ɕ':'c', + 'Ď':'d', 'ď':'d', 'Ḋ':'d', 'ḋ':'d', 'Ḑ':'d', 'ḑ':'d', 'Ḍ':'d', 'ḍ':'d', 'Ḓ':'d', 'ḓ':'d', 'Ḏ':'d', 'ḏ':'d', 'Đ':'d', 'đ':'d', 'ᵭ':'d', 'Ɖ':'d', 'ɖ':'d', 'Ɗ':'d', 'ɗ':'d', 'Ƌ':'d', 'ƌ':'d', 'ȡ':'d', 'ð':'d', + 'É':'e', 'Ə':'e', 'Ǝ':'e', 'ǝ':'e', 'é':'e', 'È':'e', 'è':'e', 'Ĕ':'e', 'ĕ':'e', 'Ê':'e', 'ê':'e', 'Ế':'e', 'ế':'e', 'Ề':'e', 'ề':'e', 'Ễ':'e', 'ễ':'e', 'Ể':'e', 'ể':'e', 'Ě':'e', 'ě':'e', 'Ë':'e', 'ë':'e', 'Ẽ':'e', 'ẽ':'e', 'Ė':'e', 'ė':'e', 'Ȩ':'e', 'ȩ':'e', 'Ḝ':'e', 'ḝ':'e', 'Ę':'e', 'ę':'e', 'Ē':'e', 'ē':'e', 'Ḗ':'e', 'ḗ':'e', 'Ḕ':'e', 'ḕ':'e', 'Ẻ':'e', 'ẻ':'e', 'Ȅ':'e', 'ȅ':'e', 'Ȇ':'e', 'ȇ':'e', 'Ẹ':'e', 'ẹ':'e', 'Ệ':'e', 'ệ':'e', 'Ḙ':'e', 'ḙ':'e', 'Ḛ':'e', 'ḛ':'e', 'Ɇ':'e', 'ɇ':'e', 'ɚ':'e', 'ɝ':'e', + 'Ḟ':'f', 'ḟ':'f', 'ᵮ':'f', 'Ƒ':'f', 'ƒ':'f', + 'Ǵ':'g', 'ǵ':'g', 'Ğ':'g', 'ğ':'g', 'Ĝ':'g', 'ĝ':'g', 'Ǧ':'g', 'ǧ':'g', 'Ġ':'g', 'ġ':'g', 'Ģ':'g', 'ģ':'g', 'Ḡ':'g', 'ḡ':'g', 'Ǥ':'g', 'ǥ':'g', 'Ɠ':'g', 'ɠ':'g', + 'Ĥ':'h', 'ĥ':'h', 'Ȟ':'h', 'ȟ':'h', 'Ḧ':'h', 'ḧ':'h', 'Ḣ':'h', 'ḣ':'h', 'Ḩ':'h', 'ḩ':'h', 'Ḥ':'h', 'ḥ':'h', 'Ḫ':'h', 'ḫ':'h', 'H':'h', '̱':'h', 'ẖ':'h', 'Ħ':'h', 'ħ':'h', 'Ⱨ':'h', 'ⱨ':'h', + 'Í':'i', 'í':'i', 'Ì':'i', 'ì':'i', 'Ĭ':'i', 'ĭ':'i', 'Î':'i', 'î':'i', 'Ǐ':'i', 'ǐ':'i', 'Ï':'i', 'ï':'i', 'Ḯ':'i', 'ḯ':'i', 'Ĩ':'i', 'ĩ':'i', 'İ':'i', 'i':'i', 'Į':'i', 'į':'i', 'Ī':'i', 'ī':'i', 'Ỉ':'i', 'ỉ':'i', 'Ȉ':'i', 'ȉ':'i', 'Ȋ':'i', 'ȋ':'i', 'Ị':'i', 'ị':'i', 'Ḭ':'i', 'ḭ':'i', 'I':'i', 'ı':'i', 'Ɨ':'i', 'ɨ':'i', + 'Ĵ':'j', 'ĵ':'j', 'J':'j', '̌':'j', 'ǰ':'j', 'ȷ':'j', 'Ɉ':'j', 'ɉ':'j', 'ʝ':'j', 'ɟ':'j', 'ʄ':'j', + 'Ḱ':'k', 'ḱ':'k', 'Ǩ':'k', 'ǩ':'k', 'Ķ':'k', 'ķ':'k', 'Ḳ':'k', 'ḳ':'k', 'Ḵ':'k', 'ḵ':'k', 'Ƙ':'k', 'ƙ':'k', 'Ⱪ':'k', 'ⱪ':'k', + 'Ĺ':'a', 'ĺ':'l', 'Ľ':'l', 'ľ':'l', 'Ļ':'l', 'ļ':'l', 'Ḷ':'l', 'ḷ':'l', 'Ḹ':'l', 'ḹ':'l', 'Ḽ':'l', 'ḽ':'l', 'Ḻ':'l', 'ḻ':'l', 'Ł':'l', 'ł':'l', 'Ł':'l', '̣':'l', 'ł':'l', '̣':'l', 'Ŀ':'l', 'ŀ':'l', 'Ƚ':'l', 'ƚ':'l', 'Ⱡ':'l', 'ⱡ':'l', 'Ɫ':'l', 'ɫ':'l', 'ɬ':'l', 'ɭ':'l', 'ȴ':'l', + 'Ḿ':'m', 'ḿ':'m', 'Ṁ':'m', 'ṁ':'m', 'Ṃ':'m', 'ṃ':'m', 'ɱ':'m', + 'Ń':'n', 'ń':'n', 'Ǹ':'n', 'ǹ':'n', 'Ň':'n', 'ň':'n', 'Ñ':'n', 'ñ':'n', 'Ṅ':'n', 'ṅ':'n', 'Ņ':'n', 'ņ':'n', 'Ṇ':'n', 'ṇ':'n', 'Ṋ':'n', 'ṋ':'n', 'Ṉ':'n', 'ṉ':'n', 'Ɲ':'n', 'ɲ':'n', 'Ƞ':'n', 'ƞ':'n', 'ɳ':'n', 'ȵ':'n', 'N':'n', '̈':'n', 'n':'n', '̈':'n', + 'Ó':'o', 'ó':'o', 'Ò':'o', 'ò':'o', 'Ŏ':'o', 'ŏ':'o', 'Ô':'o', 'ô':'o', 'Ố':'o', 'ố':'o', 'Ồ':'o', 'ồ':'o', 'Ỗ':'o', 'ỗ':'o', 'Ổ':'o', 'ổ':'o', 'Ǒ':'o', 'ǒ':'o', 'Ö':'o', 'ö':'o', 'Ȫ':'o', 'ȫ':'o', 'Ő':'o', 'ő':'o', 'Õ':'o', 'õ':'o', 'Ṍ':'o', 'ṍ':'o', 'Ṏ':'o', 'ṏ':'o', 'Ȭ':'o', 'ȭ':'o', 'Ȯ':'o', 'ȯ':'o', 'Ȱ':'o', 'ȱ':'o', 'Ø':'o', 'ø':'o', 'Ǿ':'o', 'ǿ':'o', 'Ǫ':'o', 'ǫ':'o', 'Ǭ':'o', 'ǭ':'o', 'Ō':'o', 'ō':'o', 'Ṓ':'o', 'ṓ':'o', 'Ṑ':'o', 'ṑ':'o', 'Ỏ':'o', 'ỏ':'o', 'Ȍ':'o', 'ȍ':'o', 'Ȏ':'o', 'ȏ':'o', 'Ơ':'o', 'ơ':'o', 'Ớ':'o', 'ớ':'o', 'Ờ':'o', 'ờ':'o', 'Ỡ':'o', 'ỡ':'o', 'Ở':'o', 'ở':'o', 'Ợ':'o', 'ợ':'o', 'Ọ':'o', 'ọ':'o', 'Ộ':'o', 'ộ':'o', 'Ɵ':'o', 'ɵ':'o', + 'Ṕ':'p', 'ṕ':'p', 'Ṗ':'p', 'ṗ':'p', 'Ᵽ':'p', 'Ƥ':'p', 'ƥ':'p', 'P':'p', '̃':'p', 'p':'p', '̃':'p', + 'ʠ':'q', 'Ɋ':'q', 'ɋ':'q', + 'Ŕ':'r', 'ŕ':'r', 'Ř':'r', 'ř':'r', 'Ṙ':'r', 'ṙ':'r', 'Ŗ':'r', 'ŗ':'r', 'Ȑ':'r', 'ȑ':'r', 'Ȓ':'r', 'ȓ':'r', 'Ṛ':'r', 'ṛ':'r', 'Ṝ':'r', 'ṝ':'r', 'Ṟ':'r', 'ṟ':'r', 'Ɍ':'r', 'ɍ':'r', 'ᵲ':'r', 'ɼ':'r', 'Ɽ':'r', 'ɽ':'r', 'ɾ':'r', 'ᵳ':'r', + 'ß':'s', 'Ś':'s', 'ś':'s', 'Ṥ':'s', 'ṥ':'s', 'Ŝ':'s', 'ŝ':'s', 'Š':'s', 'š':'s', 'Ṧ':'s', 'ṧ':'s', 'Ṡ':'s', 'ṡ':'s', 'ẛ':'s', 'Ş':'s', 'ş':'s', 'Ṣ':'s', 'ṣ':'s', 'Ṩ':'s', 'ṩ':'s', 'Ș':'s', 'ș':'s', 'ʂ':'s', 'S':'s', '̩':'s', 's':'s', '̩':'s', + 'Þ':'t', 'þ':'t', 'Ť':'t', 'ť':'t', 'T':'t', '̈':'t', 'ẗ':'t', 'Ṫ':'t', 'ṫ':'t', 'Ţ':'t', 'ţ':'t', 'Ṭ':'t', 'ṭ':'t', 'Ț':'t', 'ț':'t', 'Ṱ':'t', 'ṱ':'t', 'Ṯ':'t', 'ṯ':'t', 'Ŧ':'t', 'ŧ':'t', 'Ⱦ':'t', 'ⱦ':'t', 'ᵵ':'t', 'ƫ':'t', 'Ƭ':'t', 'ƭ':'t', 'Ʈ':'t', 'ʈ':'t', 'ȶ':'t', + 'Ú':'u', 'ú':'u', 'Ù':'u', 'ù':'u', 'Ŭ':'u', 'ŭ':'u', 'Û':'u', 'û':'u', 'Ǔ':'u', 'ǔ':'u', 'Ů':'u', 'ů':'u', 'Ü':'u', 'ü':'u', 'Ǘ':'u', 'ǘ':'u', 'Ǜ':'u', 'ǜ':'u', 'Ǚ':'u', 'ǚ':'u', 'Ǖ':'u', 'ǖ':'u', 'Ű':'u', 'ű':'u', 'Ũ':'u', 'ũ':'u', 'Ṹ':'u', 'ṹ':'u', 'Ų':'u', 'ų':'u', 'Ū':'u', 'ū':'u', 'Ṻ':'u', 'ṻ':'u', 'Ủ':'u', 'ủ':'u', 'Ȕ':'u', 'ȕ':'u', 'Ȗ':'u', 'ȗ':'u', 'Ư':'u', 'ư':'u', 'Ứ':'u', 'ứ':'u', 'Ừ':'u', 'ừ':'u', 'Ữ':'u', 'ữ':'u', 'Ử':'u', 'ử':'u', 'Ự':'u', 'ự':'u', 'Ụ':'u', 'ụ':'u', 'Ṳ':'u', 'ṳ':'u', 'Ṷ':'u', 'ṷ':'u', 'Ṵ':'u', 'ṵ':'u', 'Ʉ':'u', 'ʉ':'u', + 'Ṽ':'v', 'ṽ':'v', 'Ṿ':'v', 'ṿ':'v', 'Ʋ':'v', 'ʋ':'v', + 'Ẃ':'w', 'ẃ':'w', 'Ẁ':'w', 'ẁ':'w', 'Ŵ':'w', 'ŵ':'w', 'W':'w', '̊':'w', 'ẘ':'w', 'Ẅ':'w', 'ẅ':'w', 'Ẇ':'w', 'ẇ':'w', 'Ẉ':'w', 'ẉ':'w', + 'Ẍ':'x', 'ẍ':'x', 'Ẋ':'x', 'ẋ':'x', + 'Ý':'y', 'ý':'y', 'Ỳ':'y', 'ỳ':'y', 'Ŷ':'y', 'ŷ':'y', 'Y':'y', '̊':'y', 'ẙ':'y', 'Ÿ':'y', 'ÿ':'y', 'Ỹ':'y', 'ỹ':'y', 'Ẏ':'y', 'ẏ':'y', 'Ȳ':'y', 'ȳ':'y', 'Ỷ':'y', 'ỷ':'y', 'Ỵ':'y', 'ỵ':'y', 'ʏ':'y', 'Ɏ':'y', 'ɏ':'y', 'Ƴ':'y', 'ƴ':'y', + 'Ź':'z', 'ź':'z', 'Ẑ':'z', 'ẑ':'z', 'Ž':'z', 'ž':'z', 'Ż':'z', 'ż':'z', 'Ẓ':'z', 'ẓ':'z', 'Ẕ':'z', 'ẕ':'z', 'Ƶ':'z', 'ƶ':'z', 'Ȥ':'z', 'ȥ':'z', 'ʐ':'z', 'ʑ':'z', 'Ⱬ':'z', 'ⱬ':'z', 'Ǯ':'z', 'ǯ':'z', 'ƺ':'z', + // Roman fullwidth ascii equivalents: 0xff00 to 0xff5e + '2':'2', '6':'6', 'B':'B', 'F':'F', 'J':'J', 'N':'N', 'R':'R', 'V':'V', 'Z':'Z', 'b':'b', 'f':'f', 'j':'j', 'n':'n', 'r':'r', 'v':'v', 'z':'z', '1':'1', '5':'5', '9':'9', 'A':'A', 'E':'E', 'I':'I', 'M':'M', 'Q':'Q', 'U':'U', 'Y':'Y', 'a':'a', 'e':'e', 'i':'i', 'm':'m', 'q':'q', 'u':'u', 'y':'y', '0':'0', '4':'4', '8':'8', 'D':'D', 'H':'H', 'L':'L', 'P':'P', 'T':'T', 'X':'X', 'd':'d', 'h':'h', 'l':'l', 'p':'p', 't':'t', 'x':'x', '3':'3', '7':'7', 'C':'C', 'G':'G', 'K':'K', 'O':'O', 'S':'S', 'W':'W', 'c':'c', 'g':'g', 'k':'k', 'o':'o', 's':'s', 'w':'w' + }; + + var ret = ''; + for (var i = 0; i < str.length; i++) { + ret += accent_map[str.charAt(i)] || str.charAt(i); + } + return ret; + +}; // Really basic cache for the results $.TokenList.Cache = function (options) { From 5a05e7ef4892052d788d99006ed65b862bb17523 Mon Sep 17 00:00:00 2001 From: Sebastian Lehbert Date: Fri, 13 May 2011 13:34:24 +0200 Subject: [PATCH 31/63] Fix: XSS Protection --- src/jquery.tokeninput.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index d06eb456..3baa6369 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -27,6 +27,7 @@ var DEFAULT_SETTINGS = { prePopulate: null, processPrePopulate: false, makeSortable: false, + escapeHTML: true, animateDropdown: true, onResult: null, onAdd: null, @@ -357,7 +358,7 @@ $.TokenList = function (input, url_or_data, settings) { // Inner function to a token to the list function insert_token(id, value) { - var this_token = $("
  • "+ value +"

  • ") + var this_token = $("
  • "+ escapeHTML(value) +"

  • ") .addClass(settings.classes.token) .insertBefore(input_token); @@ -670,7 +671,7 @@ $.TokenList = function (input, url_or_data, settings) { .hide(); $.each(results, function(index, value) { - var this_li = $("
  • " + highlight_term(value.name, query) + "
  • ") + var this_li = $("
  • " + highlight_term(escapeHTML(value.name), query) + "
  • ") .appendTo(dropdown_ul); if(index % 2) { @@ -718,6 +719,13 @@ $.TokenList = function (input, url_or_data, settings) { item.removeClass(settings.classes.selectedDropdownItem); selected_dropdown_item = null; } + + + function escapeHTML(text) { + if(!settings.escapeHTML) return text; + return $("

    ").text(text).html(); + } + // Do a search and show the "searching" dropdown if the input is longer // than settings.minChars From f99ffe7bd93fb9bf3e7d09ad15425f32054783bb Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Thu, 19 May 2011 01:47:45 -0300 Subject: [PATCH 32/63] won't duplicate entry if hit tokenLimit with allowCustomEntry --- src/jquery.tokeninput.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 8ff7bafc..06444894 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -510,6 +510,9 @@ $.TokenList = function (input, url_or_data, settings) { // Insert the new tokens insert_token(li_data); + + // Clear input box + input_box.val(""); // Check the token limit if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) { @@ -517,9 +520,6 @@ $.TokenList = function (input, url_or_data, settings) { hide_dropdown(); } else { input_box.focus(); - - // Clear input box - input_box.val(""); // Don't show the help dropdown, they've got the idea hide_dropdown(); From ab52568bc0e846a11a65068782ae49f9cf80d0b9 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Fri, 20 May 2011 14:13:41 -0300 Subject: [PATCH 33/63] make tokenLimit and prePopulate work together --- src/jquery.tokeninput.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index ef5141be..a2818cc8 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -397,14 +397,24 @@ $.TokenList = function (input, url_or_data, settings) { // Pre-populate list if items exist hidden_input.val(""); + var li_data = settings.prePopulate || hidden_input.data("pre"); if(settings.processPrePopulate && $.isFunction(settings.onResult)) { li_data = settings.onResult.call(hidden_input, li_data); - } + } if(li_data && li_data.length) { $.each(li_data, function (index, value) { - insert_token(value); + if(settings.tokenLimit == null || settings.tokenLimit >= (index+1)) { + insert_token(value); + } else { + return false; + } }); + + if(li_data.length >= settings.tokenLimit && settings.tokenLimit != null) { + input_box.hide(); + hide_dropdown(); + } } From 9b6c4e30e4ab3c221ef2869788e9e441f5ac1d95 Mon Sep 17 00:00:00 2001 From: Alex Gorbatchev Date: Mon, 6 Jun 2011 11:25:07 -0700 Subject: [PATCH 34/63] Added support for custom tag results formatter. --- demo.html | 21 +++++++++++++++++++++ src/jquery.tokeninput.js | 31 ++++++++++++++++++++----------- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/demo.html b/demo.html index 8842ca07..0b5e9b74 100644 --- a/demo.html +++ b/demo.html @@ -228,6 +228,27 @@

    Allowing Custom Entry

    +

    Custom Result Formatter for JSON

    +
    + + + +
    +

    Using local data with parseName function and searchColumns setting

    diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index a2818cc8..9c2b6d83 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -24,6 +24,8 @@ var DEFAULT_SETTINGS = { contentType: "json", queryParam: "q", tokenDelimiter: ",", + tokenQuote: "'", + tokenQuoteEscaped: "\\'", preventDuplicates: false, prePopulate: null, processPrePopulate: false, @@ -32,7 +34,8 @@ var DEFAULT_SETTINGS = { animateDropdown: true, onResult: null, onAdd: null, - onDelete: null + onDelete: null, + tokensFormatter: null }; // Default classes to use when theming @@ -633,18 +636,24 @@ $.TokenList = function (input, url_or_data, settings) { } } - // Update the hidden input value - function update_hidden_input() { + function format_tokens(tokens) { var token_ids = []; - $.each(saved_tokens, function (index, value) { - if(value.id) { - token_ids[token_ids.length] = value.id; - } else { - token_ids[token_ids.length] = "'" + value.name.replace(/\'/gi, "\\'") + "'"; - } + var regex = new RegExp(settings.tokenQuote, "gi"); + + $.each(tokens, function (index, value) { + token_ids.push(value.id + ? value.id + : settings.tokenQuote + value.name.replace(regex, settings.tokenQuoteEscaped) + settings.tokenQuote + ); }); - - hidden_input.val(token_ids.join(settings.tokenDelimiter)); + + return token_ids.join(settings.tokenDelimiter); + } + + // Update the hidden input value + function update_hidden_input() { + var formatter = settings.tokensFormatter || format_tokens; + hidden_input.val(formatter(saved_tokens)); } // Hide and clear the results dropdown From 8da9413024857dcfefcb68a28b6f74a050318376 Mon Sep 17 00:00:00 2001 From: Alex Gorbatchev Date: Mon, 6 Jun 2011 11:43:03 -0700 Subject: [PATCH 35/63] Dropdown takes the list's width Cherry-picked from https://github.com/ignacioola/jquery-tokeninput/commit/7f309beae1d3f051b4b778a763741087c4832630 --- src/jquery.tokeninput.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 9c2b6d83..3964c41f 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -668,7 +668,8 @@ $.TokenList = function (input, url_or_data, settings) { position: "absolute", top: $(token_list).offset().top + $(token_list).outerHeight(), left: $(token_list).offset().left, - zindex: 999 + zindex: 999, + width: $(token_list).width() }) .show(); } From 9cd8bbf3bdd430a8d358cead519a6dbcd45418af Mon Sep 17 00:00:00 2001 From: Alex Gorbatchev Date: Mon, 6 Jun 2011 12:02:13 -0700 Subject: [PATCH 36/63] Updated to allow parseName callback to fully control the content of LI. --- src/jquery.tokeninput.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 3964c41f..15e911c2 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -451,10 +451,10 @@ $.TokenList = function (input, url_or_data, settings) { if(settings.parseName) { token_name = settings.parseName(object); } else { - token_name = object.name; + token_name = "

    "+ object.name +"

    "; } - var this_token = $("
  • "+ token_name +"

  • ") + var this_token = $("
  • "+ token_name +"
  • ") .addClass(settings.classes.token) .insertBefore(input_token) .attr('data-uniqueid', uniqueid); From 94f4bc2b323ae618519dcde6cbe4c781f34f5d86 Mon Sep 17 00:00:00 2001 From: Alex Gorbatchev Date: Tue, 7 Jun 2011 15:10:10 -0700 Subject: [PATCH 37/63] Cleaned up facebook theme a bit. Added hover to the close button. --- styles/token-input-facebook.css | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/styles/token-input-facebook.css b/styles/token-input-facebook.css index 48d80756..8da58f03 100644 --- a/styles/token-input-facebook.css +++ b/styles/token-input-facebook.css @@ -30,16 +30,16 @@ li.token-input-token-facebook { overflow: hidden; height: auto !important; height: 15px; - margin: 3px; - padding: 1px 3px; + margin: 3px 0px 0 3px; + padding: 1px 1px 1px 3px; background-color: #eff2f7; color: #000; cursor: default; border: 1px solid #ccd5e4; font-size: 11px; - border-radius: 5px; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; + border-radius: 2px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; float: left; white-space: nowrap; } @@ -52,9 +52,15 @@ li.token-input-token-facebook p { li.token-input-token-facebook span { color: #a6b3cf; - margin-left: 5px; + margin-left: 3px; font-weight: bold; cursor: pointer; + padding: 0 2px; +} + +li.token-input-token-facebook span:hover { + color: #fff; + background: #5670a6; } li.token-input-selected-token-facebook { @@ -127,4 +133,4 @@ div.token-input-dropdown-facebook ul li em { div.token-input-dropdown-facebook ul li.token-input-selected-dropdown-item-facebook { background-color: #3b5998; color: #fff; -} \ No newline at end of file +} From 63296be5862490281a39e9ee13b5fe0eef74e4aa Mon Sep 17 00:00:00 2001 From: Alex Gorbatchev Date: Tue, 7 Jun 2011 15:31:35 -0700 Subject: [PATCH 38/63] Added the ability to populate via a function, similar to jQuery UI Autocomplete. Instead of a string or object, specify a function as the source parameter with two parameters (the query string and callback function respectively). The function can do what it needs to, then call the callback function passing through the query string and data as a JSON object. --- src/jquery.tokeninput.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 30a998da..bfae3b1e 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -86,25 +86,25 @@ var KEY = { // Expose the .tokenInput function to jQuery as a plugin -$.fn.tokenInput = function (url_or_data, options) { +$.fn.tokenInput = function (url_or_data_or_function, options) { var settings = $.extend({}, DEFAULT_SETTINGS, options || {}); return this.each(function () { - new $.TokenList(this, url_or_data, settings); + new $.TokenList(this, url_or_data_or_function, settings); }); }; // TokenList class for each input -$.TokenList = function (input, url_or_data, settings) { +$.TokenList = function (input, url_or_data_or_function, settings) { // // Initialization // // Configure the data source - if(typeof(url_or_data) === "string") { + if(typeof(url_or_data_or_function) === "string") { // Set the url to query against - settings.url = url_or_data; + settings.url = url_or_data_or_function; // Make a smart guess about cross-domain if it wasn't explicitly specified if(settings.crossDomain === undefined) { @@ -114,9 +114,11 @@ $.TokenList = function (input, url_or_data, settings) { settings.crossDomain = (location.href.split(/\/+/g)[1] !== settings.url.split(/\/+/g)[1]); } } - } else if(typeof(url_or_data) === "object") { + } else if(typeof(url_or_data_or_function) === "function") { + settings.sourceFunction = url_or_data_or_function; + } else if(typeof(url_or_data_or_function) === "object") { // Set the local data to search through - settings.local_data = url_or_data; + settings.local_data = url_or_data_or_function; } // Build class names @@ -675,6 +677,7 @@ $.TokenList = function (input, url_or_data, settings) { // Deselect a token in the token list function deselect_token (token, position) { token.removeClass(settings.classes.selectedToken); + selected_token = null; input_box.css('color', ''); From 849eb019b733b20f5872f6a3012849a5dc728e4b Mon Sep 17 00:00:00 2001 From: Alex Gorbatchev Date: Wed, 8 Jun 2011 11:57:52 -0700 Subject: [PATCH 39/63] Fixed results coming up blank. --- src/jquery.tokeninput.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 30a998da..4b3b130b 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -502,8 +502,7 @@ $.TokenList = function (input, url_or_data, settings) { return el.id; }); - - hidden_input.val(token_ids.join(settings.tokenDelimiter)); + update_hidden_input(); token_count += 1; @@ -652,7 +651,7 @@ $.TokenList = function (input, url_or_data, settings) { }; }); saved_tokens = tokens; - hidden_input.val(ids.join(settings.tokenDelimiter)); + update_hidden_input(); } From 7bab7739b131b559fc383a10deeb6dc09e6c5288 Mon Sep 17 00:00:00 2001 From: Alex Gorbatchev Date: Wed, 8 Jun 2011 12:21:09 -0700 Subject: [PATCH 40/63] Removed duplicate call of update_hidden_input() --- src/jquery.tokeninput.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 42a32597..47c8bc4d 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -503,8 +503,6 @@ $.TokenList = function (input, url_or_data_or_function, settings) { var token_ids = $.map(saved_tokens, function (el) { return el.id; }); - - update_hidden_input(); token_count += 1; From 1b1db09665ab62166a42f49bad648660889f18f2 Mon Sep 17 00:00:00 2001 From: Alex Gorbatchev Date: Wed, 8 Jun 2011 12:28:21 -0700 Subject: [PATCH 41/63] Further clean up of the facebook theme with tweaks from vdepizzol. --- styles/token-input-facebook.css | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/styles/token-input-facebook.css b/styles/token-input-facebook.css index 8da58f03..7fcc826b 100644 --- a/styles/token-input-facebook.css +++ b/styles/token-input-facebook.css @@ -9,9 +9,10 @@ ul.token-input-list-facebook { font-size: 12px; font-family: Verdana; min-height: 1px; + line-height: 15px; z-index: 999; margin: 0; - padding: 0; + padding: 2px 0; background-color: #fff; list-style-type: none; clear: left; @@ -20,9 +21,10 @@ ul.token-input-list-facebook { ul.token-input-list-facebook li input { border: 0; width: 100px; - padding: 3px 8px; + padding: 2px 3px; background-color: white; margin: 2px 0; + font: inherit; -webkit-appearance: caret; } @@ -30,7 +32,7 @@ li.token-input-token-facebook { overflow: hidden; height: auto !important; height: 15px; - margin: 3px 0px 0 3px; + margin: 2px 0 2px 4px; padding: 1px 1px 1px 3px; background-color: #eff2f7; color: #000; @@ -52,10 +54,19 @@ li.token-input-token-facebook p { li.token-input-token-facebook span { color: #a6b3cf; - margin-left: 3px; + margin: 1px 0 0 2px; font-weight: bold; cursor: pointer; - padding: 0 2px; + padding: 2px; + width: 11px; + height: 11px; + line-height: 9px; + display: inline-block; + text-align: center; + vertical-align: top; + border-radius: 2px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; } li.token-input-token-facebook span:hover { @@ -72,7 +83,7 @@ li.token-input-selected-token-facebook { li.token-input-input-token-facebook { float: left; margin: 0; - padding: 0; + padding: 1px; list-style-type: none; } @@ -134,3 +145,4 @@ div.token-input-dropdown-facebook ul li.token-input-selected-dropdown-item-faceb background-color: #3b5998; color: #fff; } + From 043693507c3e7c2c6cef2b9864f56c8c561292d9 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Sun, 12 Jun 2011 02:14:19 -0300 Subject: [PATCH 42/63] fix drag and drop mode when doing single click, items were wrongly moving. Added timeout to discover when allow drag&drop mode. Also, made facebook style a bit smaller and add initial support for drag&drop visual feedback --- src/jquery.tokeninput.js | 165 +++++++++++++++++++++----------- styles/token-input-facebook.css | 24 +++-- styles/token-input.css | 14 ++- 3 files changed, 138 insertions(+), 65 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 47c8bc4d..7d5b7527 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -48,6 +48,7 @@ var DEFAULT_CLASSES = { tokenDelete: "token-input-delete-token", selectedToken: "token-input-selected-token", highlightedToken: "token-input-highlighted-token", + draggedToken: "token-input-dragged-token", dropdown: "token-input-dropdown", dropdownItem: "token-input-dropdown-item", dropdownItem2: "token-input-dropdown-item2", @@ -407,6 +408,8 @@ $.TokenList = function (input, url_or_data_or_function, settings) { // True during dragging process var dragging = false; + var dragTimeout; + // the dragged Token var dragToken; @@ -580,78 +583,126 @@ $.TokenList = function (input, url_or_data_or_function, settings) { // Drag and Drop Functionality // function addDragFunctionality(token) { - token.bind('mousedown',function(){ - var token = $(this) - dragToken = token; - token.addClass(settings.classes.selectedToken); - dragging= true; - $(document).one('mouseup',function(){ - token.removeClass(settings.classes.selectedToken); - dragging=false; - move_token(token, dragDestination); - reindex_results(); + token.bind('mousedown', function() { + var token = $(this); + + dragToken = token; + + dragTimeout = window.setTimeout(function() { + var previous_selected_token = selected_token; + + if(selected_token == token) { + return; + } + + if(selected_token) { + deselect_token($(selected_token), POSITION.END); + } + + select_token(token); + + $(token).clone().appendTo('body').addClass(settings.classes.draggedToken); + + dragging = true; + + }, 200); + + $(document).one('mouseup', function() { + + window.clearTimeout(dragTimeout); + + if(dragging != true) { + return; + } + + $('li.'+settings.classes.draggedToken).remove(); + + if(selected_token) { + deselect_token($(selected_token), POSITION.END); + } + + dragging = false; + + if(dragDestination) { + move_token(token, dragDestination); + reindex_results(); + } + }); + + return false; + }) + .bind('mouseover', function() { + + if(!dragging) return; + + dragDestination = $(this); + + if(is_after(dragToken, dragDestination)) { + dragDestination.addClass(settings.classes.insertAfter); + } else { + dragDestination.addClass(settings.classes.insertBefore); + } + }) + .bind('mouseout', function() { + + if(!dragging) return; + + $(this).removeClass(settings.classes.insertBefore); + $(this).removeClass(settings.classes.insertAfter); + }). + bind('mouseup', function(){ + $(this).removeClass(settings.classes.insertBefore); + $(this).removeClass(settings.classes.insertAfter); + }); + + $('body').mousemove(function(e) { + if(!dragging) return; + + $('li.'+settings.classes.draggedToken).css({'top': e.pageY, 'left': e.pageX}); }); - return false; - }) - .bind('mouseover',function(){ - if(!dragging) return; - dragDestination = $(this); - if(is_after(dragToken, dragDestination)) { - dragDestination.addClass(settings.classes.insertAfter); - } else { - dragDestination.addClass(settings.classes.insertBefore); - }; - }).bind('mouseout', function(){ - if(!dragging) return; - $(this).removeClass(settings.classes.insertBefore); - $(this).removeClass(settings.classes.insertAfter); - }).bind('mouseup', function(){ - $(this).removeClass(settings.classes.insertBefore); - $(this).removeClass(settings.classes.insertAfter); - }); } function move_token(token, destinationToken) { - if(token.get(0) == destinationToken.get(0)) return; - - if(is_after(token, destinationToken)) { - token.insertAfter(destinationToken); - } else { - token.insertBefore(destinationToken); - } - - + if(!destinationToken || token.get(0) == destinationToken.get(0)) return; + + if(is_after(token, destinationToken)) { + token.insertAfter(destinationToken); + } else { + token.insertBefore(destinationToken); + } } function is_after(first, last) { - index_tokens(); - first = $.data(first.get(0), "tokeninput") - last = $.data(last.get(0), "tokeninput") - return last.index > first.index + index_tokens(); + first = $.data(first.get(0), "tokeninput"); + last = $.data(last.get(0), "tokeninput"); + return last.index > first.index } function index_tokens() { - var i = 0; - token_list.find('li').each(function(){ - var data = $.data(this, "tokeninput"); - if(data){ data.index = i; } - i++; - }); + var i = 0; + token_list.find('li').each(function() { + var data = $.data(this, "tokeninput"); + if(data) { + data.index = i; + } + i++; + }); } function reindex_results() { - var ids = [], tokens = []; - token_list.find('li').each(function(){ - var data = $.data(this, "tokeninput"); - if(data){ - ids.push(data.id); - tokens.push(data); - }; - }); - saved_tokens = tokens; - update_hidden_input(); + var ids = [], tokens = []; + token_list.find('li').each(function() { + var data = $.data(this, "tokeninput"); + if(data) { + ids.push(data.id); + tokens.push(data); + }; + }); + saved_tokens = tokens; + update_hidden_input(); } diff --git a/styles/token-input-facebook.css b/styles/token-input-facebook.css index 7fcc826b..da29d283 100644 --- a/styles/token-input-facebook.css +++ b/styles/token-input-facebook.css @@ -12,7 +12,7 @@ ul.token-input-list-facebook { line-height: 15px; z-index: 999; margin: 0; - padding: 2px 0; + padding: 1px 0; background-color: #fff; list-style-type: none; clear: left; @@ -21,9 +21,9 @@ ul.token-input-list-facebook { ul.token-input-list-facebook li input { border: 0; width: 100px; - padding: 2px 3px; + padding: 1px 3px; background-color: white; - margin: 2px 0; + margin: 1px 0; font: inherit; -webkit-appearance: caret; } @@ -32,7 +32,7 @@ li.token-input-token-facebook { overflow: hidden; height: auto !important; height: 15px; - margin: 2px 0 2px 4px; + margin: 1px 0 1px 2px; padding: 1px 1px 1px 3px; background-color: #eff2f7; color: #000; @@ -86,13 +86,25 @@ li.token-input-input-token-facebook { padding: 1px; list-style-type: none; } +li.token-input-dragged-token-facebook { + opacity: 0.7; + position: absolute; + margin: 5px 0 0 5px; + padding: 2px 6px; + list-style: none; + font-size: 11px; + font-family: Verdana; +} +li.token-input-dragged-token-facebook span { + display: none; +} li.token-input-insert-before-facebook { - border-left: 1px solid red; + border-left: 1px solid red; } li.token-input-insert-after-facebook { - border-right: 1px solid red; + border-right: 1px solid red; } div.token-input-dropdown-facebook { diff --git a/styles/token-input.css b/styles/token-input.css index a11f7811..79090613 100644 --- a/styles/token-input.css +++ b/styles/token-input.css @@ -61,13 +61,23 @@ li.token-input-selected-token { li.token-input-selected-token span { color: #bbb; } +li.token-input-dragged-token { + opacity: 0.5; + position: absolute; + margin: 5px 0 0 5px; + font-size: 12px; + font-family: Verdana; +} +li.token-input-dragged-token span { + display: none; +} li.token-input-insert-before { - border-top: 1px solid red; + border-top: 1px solid red; } li.token-input-insert-after { - border-bottom: 1px solid red; + border-bottom: 1px solid red; } div.token-input-dropdown { From cf1bc7776353e52e22587e490ca7315316a68137 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Sun, 12 Jun 2011 02:50:39 -0300 Subject: [PATCH 43/63] improvements in drag&drop visual feedback not there yet. --- src/jquery.tokeninput.js | 19 ++++++++++++------- styles/token-input-facebook.css | 7 +++++-- styles/token-input.css | 5 ++++- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 7d5b7527..05e01d20 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -49,6 +49,7 @@ var DEFAULT_CLASSES = { selectedToken: "token-input-selected-token", highlightedToken: "token-input-highlighted-token", draggedToken: "token-input-dragged-token", + draggedClone: "token-input-dragged-clone", dropdown: "token-input-dropdown", dropdownItem: "token-input-dropdown-item", dropdownItem2: "token-input-dropdown-item2", @@ -588,8 +589,7 @@ $.TokenList = function (input, url_or_data_or_function, settings) { dragToken = token; - dragTimeout = window.setTimeout(function() { - var previous_selected_token = selected_token; + dragTimeout = window.setTimeout(function(e) { if(selected_token == token) { return; @@ -601,7 +601,10 @@ $.TokenList = function (input, url_or_data_or_function, settings) { select_token(token); - $(token).clone().appendTo('body').addClass(settings.classes.draggedToken); + var position = $(token).position(); + + $(token).clone().appendTo('body').addClass(settings.classes.draggedClone).css({'top': position.top, 'left': position.left}); + token.addClass(settings.classes.draggedToken); dragging = true; @@ -615,14 +618,15 @@ $.TokenList = function (input, url_or_data_or_function, settings) { return; } - $('li.'+settings.classes.draggedToken).remove(); + dragging = false; + + $('li.'+settings.classes.draggedClone).remove(); + $('li.'+settings.classes.draggedToken).removeClass(settings.classes.draggedToken); if(selected_token) { deselect_token($(selected_token), POSITION.END); } - dragging = false; - if(dragDestination) { move_token(token, dragDestination); reindex_results(); @@ -658,7 +662,7 @@ $.TokenList = function (input, url_or_data_or_function, settings) { $('body').mousemove(function(e) { if(!dragging) return; - $('li.'+settings.classes.draggedToken).css({'top': e.pageY, 'left': e.pageX}); + $('li.'+settings.classes.draggedClone).css({'top': e.pageY, 'left': e.pageX}); }); } @@ -677,6 +681,7 @@ $.TokenList = function (input, url_or_data_or_function, settings) { index_tokens(); first = $.data(first.get(0), "tokeninput"); last = $.data(last.get(0), "tokeninput"); + if(!first || !last) return; return last.index > first.index } diff --git a/styles/token-input-facebook.css b/styles/token-input-facebook.css index da29d283..3505ff7b 100644 --- a/styles/token-input-facebook.css +++ b/styles/token-input-facebook.css @@ -87,15 +87,18 @@ li.token-input-input-token-facebook { list-style-type: none; } li.token-input-dragged-token-facebook { - opacity: 0.7; + opacity: 0; +} +li.token-input-dragged-clone-facebook { position: absolute; margin: 5px 0 0 5px; padding: 2px 6px; list-style: none; font-size: 11px; font-family: Verdana; + cursor: move; } -li.token-input-dragged-token-facebook span { +li.token-input-dragged-clone-facebook span { display: none; } diff --git a/styles/token-input.css b/styles/token-input.css index 79090613..f8fdeb75 100644 --- a/styles/token-input.css +++ b/styles/token-input.css @@ -62,13 +62,16 @@ li.token-input-selected-token span { color: #bbb; } li.token-input-dragged-token { + opacity: 0; +} +li.token-input-dragged-clone { opacity: 0.5; position: absolute; margin: 5px 0 0 5px; font-size: 12px; font-family: Verdana; } -li.token-input-dragged-token span { +li.token-input-dragged-clone span { display: none; } From 325ed44f82b28958a743c3496cf0bd64495b8456 Mon Sep 17 00:00:00 2001 From: Alex Gorbatchev Date: Sun, 3 Jul 2011 10:33:26 -0700 Subject: [PATCH 44/63] Now respects tabindex correctly. --- demo_tabindex.html | 53 ++++++++++++++++++++++++++++++++++++++++ src/jquery.tokeninput.js | 6 +++-- 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 demo_tabindex.html diff --git a/demo_tabindex.html b/demo_tabindex.html new file mode 100644 index 00000000..6c8050cb --- /dev/null +++ b/demo_tabindex.html @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + +

    jQuery Tokeninput Demos

    + +

    Tab index demo

    +
    + + + + + + +
    + +

    Tab index demo in a form

    +
    +
    + + + + + +
    + +
    + + diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 05e01d20..c2cf7dbf 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -86,7 +86,6 @@ var KEY = { COMMA: 188 }; - // Expose the .tokenInput function to jQuery as a plugin $.fn.tokenInput = function (url_or_data_or_function, options) { var settings = $.extend({}, DEFAULT_SETTINGS, options || {}); @@ -297,7 +296,7 @@ $.TokenList = function (input, url_or_data_or_function, settings) { if(event.keyCode == KEY.TAB && !$(input_box).val().length) { hide_dropdown(); - $(this).blur(); + // let the browser handle the tab key properly if user is trying to tab through or out return true; } @@ -342,6 +341,9 @@ $.TokenList = function (input, url_or_data_or_function, settings) { input_box.blur(); }); + // Carry over the tab index if it's set + input_box.attr({ tabindex: hidden_input.attr('tabindex') }); + // Keep a reference to the selected token and dropdown item var selected_token = null; var selected_token_index = 0; From b5e770a6297a6363106c5b78e760e00d1376034d Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Sun, 3 Jul 2011 17:08:11 -0300 Subject: [PATCH 45/63] updated README with list of changes changes from loopj version --- README.md | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7642be82..a3d31b7d 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,46 @@ Overview -------- 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. You may have seen a similar type of text entry when filling in the recipients field sending messages on facebook. -Documentation, Features and Demos ---------------------------------- -Full details and documentation can be found on the project page here: +Changes from original jquery-tokeninput +--------------------------------------- - \ No newline at end of file +This is a forked version of jquery-tokeninput with a couple of different things. I grouped a small list of changes between original loopj's version from mine. + +=== Support for server-side diacritical marks and original case === + +In [author]'s version, only a parsed text of what the user types goes to the server-side destination, without accents and all in lowercase. This version will send to server-side the intact version without parsing any character. This particularly allows to the server-side to know how to handle new custom entries and special items. + +=== Highlight terms === + +Original jquery-tokeniput used a simple regex expression to highlight the typed characters in the dropdown list. This caused to be a huge problem with accents and other special chars. After doing a major clean-up[1] on this, this updated version of tokeninput will properly highlight terms like “são paulo” when “sao paulo” is provided. + +[1] Based on this great article http://www.alistapart.com/articles/accent-folding-for-auto-complete/ + +=== Custom objects as data source === + +Originally jquery-tokeninput would only accept objects with 'id' and 'name' parameters, and would even ignore any other element in the object. With several hooks on this, I made token accept any kind of object, with options to choose which columns to use when searching and what + +
    
    +[
    +    {id: 1, city: 'Vitória', state: 'ES'},
    +    {id: 5, name: 'São Paulo', state: 'SP'},
    +    {id: 2, city: 'Palo Alto', state: 'CA'},
    +    {id: 3, city: 'San Francisco', state: 'CA'},
    +]
    +
    + +=== Custom Entries === + +Besides choosing predefined items from a list + +TODO + +=== Refactored way for storing elements and send them to the server === + +Original jquery-tokeninput had some misterious bug that kept deleted elements when form was submited. Besides this, it had a hard-to-maintain way to handle actions to add and remove tokens. + +With this new approach, I cleaned up and simplified the code, and added options to choose how to send the output to the server. + +Originally output would be a list of numbers imploded with ,, like 16,742,58,254. With custom entries support, output becomes 16,42,'Subway',37,'McDonald\'s',734. + +Alternatively, a function can be used to parse the output using tokensFormatter. With this, you can per instance return a json as output. From 222835e165347f5a486d7e25abbf533a90cb6741 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Sun, 3 Jul 2011 17:11:11 -0300 Subject: [PATCH 46/63] fixed titles of README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a3d31b7d..e319c3dd 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Original jquery-tokeniput used a simple regex expression to highlight the typed [1] Based on this great article http://www.alistapart.com/articles/accent-folding-for-auto-complete/ -=== Custom objects as data source === +### Custom objects as data source Originally jquery-tokeninput would only accept objects with 'id' and 'name' parameters, and would even ignore any other element in the object. With several hooks on this, I made token accept any kind of object, with options to choose which columns to use when searching and what @@ -33,13 +33,13 @@ Originally jquery-tokeninput would only accept objects with 'id' and 'name' para ] -=== Custom Entries === +### Custom Entries Besides choosing predefined items from a list TODO -=== Refactored way for storing elements and send them to the server === +### Refactored way for storing elements and send them to the server Original jquery-tokeninput had some misterious bug that kept deleted elements when form was submited. Besides this, it had a hard-to-maintain way to handle actions to add and remove tokens. From 69d66be5562960829496a59c0226142a060f1ec6 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Sun, 3 Jul 2011 17:13:19 -0300 Subject: [PATCH 47/63] fixes in README --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e319c3dd..242eae60 100644 --- a/README.md +++ b/README.md @@ -22,16 +22,14 @@ Original jquery-tokeniput used a simple regex expression to highlight the typed ### Custom objects as data source -Originally jquery-tokeninput would only accept objects with 'id' and 'name' parameters, and would even ignore any other element in the object. With several hooks on this, I made token accept any kind of object, with options to choose which columns to use when searching and what +Originally jquery-tokeninput would only accept objects with 'id' and 'name' parameters, and would even ignore any other element in the object. With several hooks on this, I made token accept any kind of object, with options to choose which columns to use when searching and what TODO -
    
    -[
    +
    [
         {id: 1, city: 'Vitória', state: 'ES'},
         {id: 5, name: 'São Paulo', state: 'SP'},
         {id: 2, city: 'Palo Alto', state: 'CA'},
         {id: 3, city: 'San Francisco', state: 'CA'},
    -]
    -
    +]
    ### Custom Entries From a30cfbc153ab1eb70e4ba09de3058d1314a0964e Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Sun, 3 Jul 2011 17:16:34 -0300 Subject: [PATCH 48/63] fix title in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 242eae60..f285bf7b 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,11 @@ Changes from original jquery-tokeninput This is a forked version of jquery-tokeninput with a couple of different things. I grouped a small list of changes between original loopj's version from mine. -=== Support for server-side diacritical marks and original case === +### Support for server-side diacritical marks and original case In [author]'s version, only a parsed text of what the user types goes to the server-side destination, without accents and all in lowercase. This version will send to server-side the intact version without parsing any character. This particularly allows to the server-side to know how to handle new custom entries and special items. -=== Highlight terms === +### Highlight terms Original jquery-tokeniput used a simple regex expression to highlight the typed characters in the dropdown list. This caused to be a huge problem with accents and other special chars. After doing a major clean-up[1] on this, this updated version of tokeninput will properly highlight terms like “são paulo” when “sao paulo” is provided. From 6518a4a9d9032e8f8a81795058d19580ee845b33 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Mon, 4 Jul 2011 13:29:16 -0300 Subject: [PATCH 49/63] finish example of custom objects as data source --- README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f285bf7b..11714ae2 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This is a forked version of jquery-tokeninput with a couple of different things. ### Support for server-side diacritical marks and original case -In [author]'s version, only a parsed text of what the user types goes to the server-side destination, without accents and all in lowercase. This version will send to server-side the intact version without parsing any character. This particularly allows to the server-side to know how to handle new custom entries and special items. +In loopj's version, only a parsed text of what the user types goes to the server-side destination, without accents and all in lowercase. This version will send to server-side the intact version without parsing any character. This particularly allows to the server-side to know how to handle new custom entries and special items. ### Highlight terms @@ -22,14 +22,20 @@ Original jquery-tokeniput used a simple regex expression to highlight the typed ### Custom objects as data source -Originally jquery-tokeninput would only accept objects with 'id' and 'name' parameters, and would even ignore any other element in the object. With several hooks on this, I made token accept any kind of object, with options to choose which columns to use when searching and what TODO +Originally jquery-tokeninput would only accept objects with 'id' and 'name' parameters, and would even ignore any other element in the object. With several hooks on this, I made token accept any kind of object, with options to choose which columns to use when searching (by using `searchColumns`) and what to display as the name of the item (with `parseName` param). -
    [
    +
    $("#demo").tokenInput([
         {id: 1, city: 'Vitória', state: 'ES'},
         {id: 5, name: 'São Paulo', state: 'SP'},
         {id: 2, city: 'Palo Alto', state: 'CA'},
         {id: 3, city: 'San Francisco', state: 'CA'},
    -]
    +], +{ + parseName: function(element) { + return element.city + ', '+ element.state; + }, + searchColumns: ['city', 'state'] +});
    ### Custom Entries From f0ac25ac25d2e73d6dc0759482dd0c0ce1f76ddf Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Wed, 13 Jul 2011 16:29:32 -0300 Subject: [PATCH 50/63] makes base input hidden according to type and not to css --- src/jquery.tokeninput.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index c2cf7dbf..d25d41dd 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -329,10 +329,12 @@ $.TokenList = function (input, url_or_data_or_function, settings) { unique_counter++; return 'u' + unique_counter; } - + + // hides original input box + input.type = 'hidden'; + // Keep a reference to the original input box var hidden_input = $(input) - .hide() .val("") .focus(function () { input_box.focus(); From 6d5639d5347ae7fa3a8d0b70f072f2477a4a3ed3 Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Wed, 13 Jul 2011 16:30:00 -0300 Subject: [PATCH 51/63] also add to tokenlist the existing classes of original input box --- src/jquery.tokeninput.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index d25d41dd..1dab8db1 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -353,7 +353,7 @@ $.TokenList = function (input, url_or_data_or_function, settings) { // The list to store the token items in var token_list = $("
      ") - .addClass(settings.classes.tokenList) + .addClass(settings.classes.tokenList + ' ' + hidden_input.attr('class')) .click(function (event) { var li = $(event.target).closest("li"); if(li && li.get(0) && $.data(li.get(0), "tokeninput")) { From 1853d3b22ea6904d09bca02869bef3af1023f6ba Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Fri, 15 Jul 2011 12:39:00 -0300 Subject: [PATCH 52/63] separate default settings in categories --- src/jquery.tokeninput.js | 43 ++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 1dab8db1..a4344178 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -9,37 +9,54 @@ */ (function ($) { + // Default settings var DEFAULT_SETTINGS = { + + // Strings + hintText: "Type in a search term", noResultsText: "No results", searchingText: "Searching...", deleteText: "×", - searchDelay: 300, - minChars: 1, + + // Field exibition and behavior + tokenLimit: null, - jsonContainer: null, allowCustomEntry: false, + preventDuplicates: false, + searchColumns: ['name'], + parseName: null, + escapeHTML: true, + searchDelay: 300, + minChars: 1, + makeSortable: false, + animateDropdown: true, + + // Get local & external data + method: "GET", contentType: "json", queryParam: "q", + jsonContainer: null, + prePopulate: null, + processPrePopulate: false, + + // Submit input value + tokenDelimiter: ",", tokenQuote: "'", tokenQuoteEscaped: "\\'", - preventDuplicates: false, - prePopulate: null, - processPrePopulate: false, - parseName: null, - searchColumns: ['name'], - makeSortable: false, - escapeHTML: true, - animateDropdown: true, + tokensFormatter: null, + + // Callbacks + onResult: null, onAdd: null, - onDelete: null, - tokensFormatter: null + onDelete: null }; + // Default classes to use when theming var DEFAULT_CLASSES = { tokenList: "token-input-list", From f38cd5c0e1a3423274f3dae1043966cf923befdc Mon Sep 17 00:00:00 2001 From: Vinicius Depizzol Date: Fri, 15 Jul 2011 14:50:59 -0300 Subject: [PATCH 53/63] merged loopj's public methods support --- demo.html | 30 ++++++++++++++++ src/jquery.tokeninput.js | 74 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 97 insertions(+), 7 deletions(-) diff --git a/demo.html b/demo.html index d392c055..c7cd77c7 100644 --- a/demo.html +++ b/demo.html @@ -287,5 +287,35 @@

      Using local data with parseName function and searchColumns se });

    + +

    Using the add, remove and clear Methods

    +
    + Add Token | Remove Token | Clear Tokens
    + + + +
    diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index a4344178..09b182ec 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -103,13 +103,37 @@ var KEY = { COMMA: 188 }; -// Expose the .tokenInput function to jQuery as a plugin -$.fn.tokenInput = function (url_or_data_or_function, options) { - var settings = $.extend({}, DEFAULT_SETTINGS, options || {}); +// 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("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; + } +} - return this.each(function () { - new $.TokenList(this, url_or_data_or_function, settings); - }); +// 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); + } }; @@ -459,6 +483,39 @@ $.TokenList = function (input, url_or_data_or_function, settings) { hide_dropdown(); } } + + + // + // 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[data-uniqueid]").each(function() { + 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)); + } + }); + } @@ -538,11 +595,14 @@ $.TokenList = function (input, url_or_data_or_function, settings) { // Add a token to the token list based on user input function add_token (item) { + if(typeof(item) === "string") { var li_data = {name: item}; - } else { + } else if(item[0]) { var li_data = $.data(item.get(0), "tokeninput"); + } else { + var li_data = item; } if(!li_data) { From 9e8020eac16ee4a782b5b25785f4d08d11bc439c Mon Sep 17 00:00:00 2001 From: Josh Schramm Date: Wed, 20 Apr 2011 12:54:08 -0400 Subject: [PATCH 54/63] Changed plugin to clear out bad text when no token is selected --- .DS_Store | Bin 0 -> 6148 bytes src/jquery.tokeninput.js | 1 + 2 files changed, 1 insertion(+) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..9a874b5768f336915163bb88cd434575b859f936 GIT binary patch literal 6148 zcmeH~Jr2S!425ml0g0s}V-^m;4I%_5-~tF3k&vj^b9A16778<}(6eNJu~Vz<8=6`~ zboab&MFtUB!i}=AFfm2m$tVxGT*u4pe81nUlA49C} z?O@64YO)2RT{MRe%{!}2F))pG(Sih~)xkgosK7*lF7m<7{{#Hn{6A@7N(HFEpDCdI z{ Date: Thu, 21 Apr 2011 10:01:12 +0300 Subject: [PATCH 55/63] Adds IDs for the generated input fields. --- src/jquery.tokeninput.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index c94e930d..29cb6d86 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -53,7 +53,8 @@ var DEFAULT_SETTINGS = { onResult: null, onAdd: null, - onDelete: null + onDelete: null, + idPrefix: "token-input-" }; @@ -196,6 +197,7 @@ $.TokenList = function (input, url_or_data_or_function, settings) { .css({ outline: "none" }) + .attr("id", settings.idPrefix + input.id) .focus(function () { if (settings.tokenLimit === null || settings.tokenLimit !== token_count) { show_dropdown_hint(); From b70002b846f3ed9dee71fda9c5b8f8e96775b76a Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 28 Jul 2011 16:57:13 -0700 Subject: [PATCH 56/63] Hand merged commit 86ae6e3b from upstream master with some interpretation of what the intent should be in the context of the vdepizzol branch. --- src/jquery.tokeninput.js | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 29cb6d86..0af85102 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -480,11 +480,8 @@ $.TokenList = function (input, url_or_data_or_function, settings) { return false; } }); - - if(li_data.length >= settings.tokenLimit && settings.tokenLimit != null) { - input_box.hide(); - hide_dropdown(); - } + + checkTokenLimit(); } @@ -525,7 +522,16 @@ $.TokenList = function (input, url_or_data_or_function, settings) { // // Private functions // - + + function checkTokenLimit(){ + if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) { + input_box.hide(); + hide_dropdown(); + return; + } else { + input_box.focus(); + } + } function resize_input() { if(input_val === (input_val = input_box.val())) {return;} @@ -642,20 +648,13 @@ $.TokenList = function (input, url_or_data_or_function, settings) { // Insert the new tokens insert_token(li_data); - + checkTokenLimit(); + // Clear input box input_box.val(""); - // Check the token limit - if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) { - input_box.hide(); - hide_dropdown(); - } else { - input_box.focus(); - - // Don't show the help dropdown, they've got the idea - hide_dropdown(); - } + // Don't show the help dropdown, they've got the idea + hide_dropdown(); // Execute the onAdd callback if defined if($.isFunction(callback)) { From fb99f74ad9d2827782dff608bae0b2566441ee14 Mon Sep 17 00:00:00 2001 From: James Smith Date: Sun, 12 Jun 2011 01:52:19 -0700 Subject: [PATCH 57/63] Bump version number and update changelog --- ChangeLog.md | 7 +++++++ src/jquery.tokeninput.js | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 7c1027d7..7637fbdf 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,13 @@ jQuery Tokeninput ChangeLog =========================== +Version 1.5.0 (2011-06-12) +-------------------------- +- Added add/remove/clear methods to programatically modify tokens +- Support setting of ids on dynamic input (for html label targetting) +- Fix token limit bug when using prepopulate +- Various other bug fixes + Version 1.4.2 (2011-03-26) -------------------------- - Implemented basic local data search to search without an ajax request diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 0af85102..e863192d 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -1,6 +1,6 @@ /* * jQuery Plugin: Tokenizing Autocomplete Text Entry - * Version 1.4.2 + * Version 1.5.0 * * Copyright (c) 2009 James Smith (http://loopj.com) * Licensed jointly under the GPL and MIT licenses, From d90b91f90807cb720cc51d87bf4c64825af4afc7 Mon Sep 17 00:00:00 2001 From: James Smith Date: Sun, 12 Jun 2011 01:52:50 -0700 Subject: [PATCH 58/63] Remove DS_Store --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 9a874b5768f336915163bb88cd434575b859f936..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425ml0g0s}V-^m;4I%_5-~tF3k&vj^b9A16778<}(6eNJu~Vz<8=6`~ zboab&MFtUB!i}=AFfm2m$tVxGT*u4pe81nUlA49C} z?O@64YO)2RT{MRe%{!}2F))pG(Sih~)xkgosK7*lF7m<7{{#Hn{6A@7N(HFEpDCdI z{ Date: Fri, 8 Jul 2011 14:25:24 +0200 Subject: [PATCH 59/63] Manually merged 386c3efe729 to resolve conflicts. Don't add tokens if limit has been reached. --- src/jquery.tokeninput.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index e863192d..fc7edc27 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -647,8 +647,10 @@ $.TokenList = function (input, url_or_data_or_function, settings) { } // Insert the new tokens - insert_token(li_data); - checkTokenLimit(); + if(settings.tokenLimit == null || token_count < settings.tokenLimit) { + insert_token(li_data); + checkTokenLimit(); + } // Clear input box input_box.val(""); From 300ed0a133e71c1508f882f783652688aefc0722 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 28 Jul 2011 17:23:27 -0700 Subject: [PATCH 60/63] Add emacs files into gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 04c4fef1..1091cbcf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ _site _layouts -.DS_Store \ No newline at end of file +.DS_Store +*~ +\#*# From 133a8ad509a6a6c4b2437fe345288df56e36d60e Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 28 Jul 2011 17:39:31 -0700 Subject: [PATCH 61/63] Actually complete implementation of sourceFunction which seems to be incomplete. --- src/jquery.tokeninput.js | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index fc7edc27..0a6d20fd 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -157,8 +157,8 @@ $.TokenList = function (input, url_or_data_or_function, settings) { settings.crossDomain = (location.href.split(/\/+/g)[1] !== settings.url.split(/\/+/g)[1]); } } - } else if(typeof(url_or_data_or_function) === "function") { - settings.sourceFunction = url_or_data_or_function; + } else if(typeof(url_or_data_or_function) === "function") { + settings.sourceFunction = url_or_data_or_function; } else if(typeof(url_or_data_or_function) === "object") { // Set the local data to search through settings.local_data = url_or_data_or_function; @@ -1133,17 +1133,23 @@ $.TokenList = function (input, url_or_data_or_function, settings) { // Make the request $.ajax(ajax_params); - } else if(settings.local_data) { - // Do the search through local data - var results = $.grep(settings.local_data, function (row) { - var founded = false; - $(settings.searchColumns).each(function(i, item) { - if(row[item].toString().toLowerCase().removeDiacritics().indexOf(query.toString().toLowerCase().removeDiacritics()) > -1) { - founded = true; - } + } else if(settings.local_data || settings.sourceFunction) { + var results = []; + if (settings.sourceFunction) { + // Execute the sourceFunction to get results + results = settings.sourceFunction(query); + } else { + // Do the search through local data + results = $.grep(settings.local_data, function (row) { + var founded = false; + $(settings.searchColumns).each(function(i, item) { + if(row[item].toString().toLowerCase().removeDiacritics().indexOf(query.toString().toLowerCase().removeDiacritics()) > -1) { + founded = true; + } + }); + return founded; }); - return founded; - }); + } if($.isFunction(settings.onResult)) { results = settings.onResult.call(hidden_input, results); From 59ad1c290b2468506b08a0e3661b150ebad53f21 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 29 Jul 2011 16:01:24 -0700 Subject: [PATCH 62/63] Implemented sourceFunction with callbacks, mimicking the JQuery UI autocomplete callback more or less... --- src/jquery.tokeninput.js | 71 ++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/src/jquery.tokeninput.js b/src/jquery.tokeninput.js index 0a6d20fd..cdfff156 100644 --- a/src/jquery.tokeninput.js +++ b/src/jquery.tokeninput.js @@ -71,6 +71,7 @@ var DEFAULT_CLASSES = { dropdown: "token-input-dropdown", dropdownItem: "token-input-dropdown-item", dropdownItem2: "token-input-dropdown-item2", + dropdownHint: "token-input-hint", selectedDropdownItem: "token-input-selected-dropdown-item", inputToken: "token-input-input-token", insertBefore: "token-input-insert-before", @@ -936,7 +937,10 @@ $.TokenList = function (input, url_or_data_or_function, settings) { function show_dropdown_hint () { if(settings.hintText) { - dropdown.html("

    "+settings.hintText+"

    "); + dropdown.empty(); + var hint = $("

    "+settings.hintText+"

    ") + .addClass(settings.classes.dropdownHint) + .appendTo(dropdown); show_dropdown(); } } @@ -1092,6 +1096,20 @@ $.TokenList = function (input, url_or_data_or_function, settings) { if(cached_results) { populate_dropdown(query.toLowerCase(), cached_results); } else { + var results_callback = function(results) { + if($.isFunction(settings.onResult)) { + results = settings.onResult.call(hidden_input, results); + } + if (settings.jsonContainer && typeof(results) === "object" && settings.jsonContainer in results) { + results = results[settings.jsonContainer]; + } + cache.add(query, results); + // only populate the dropdown if the results are associated with the active search query + if(input_box.val().toLowerCase() === query.toLowerCase()) { + populate_dropdown(query, results); + } + }; + // Are we doing an ajax search or local data search? if(settings.url) { // Extract exisiting get params @@ -1117,45 +1135,28 @@ $.TokenList = function (input, url_or_data_or_function, settings) { if(settings.crossDomain) { ajax_params.dataType = "jsonp"; } - - // Attach the success callback - ajax_params.success = function(results) { - if($.isFunction(settings.onResult)) { - results = settings.onResult.call(hidden_input, results); - } - cache.add(query, settings.jsonContainer ? results[settings.jsonContainer] : results); - - // only populate the dropdown if the results are associated with the active search query - if(input_box.val().toLowerCase() === query.toLowerCase()) { - populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results); - } - }; + ajax_params.success = results_callback; // Make the request $.ajax(ajax_params); - } else if(settings.local_data || settings.sourceFunction) { - var results = []; - if (settings.sourceFunction) { - // Execute the sourceFunction to get results - results = settings.sourceFunction(query); - } else { - // Do the search through local data - results = $.grep(settings.local_data, function (row) { - var founded = false; - $(settings.searchColumns).each(function(i, item) { - if(row[item].toString().toLowerCase().removeDiacritics().indexOf(query.toString().toLowerCase().removeDiacritics()) > -1) { - founded = true; - } - }); - return founded; + } + else if(settings.sourceFunction) { + // Execute the sourceFunction to get results + settings.sourceFunction(query, results_callback); + } + else if(settings.local_data) { + // Do the search through local data + var results = $.grep(settings.local_data, function (row) { + var founded = false; + $(settings.searchColumns).each(function(i, item) { + if(row[item].toString().toLowerCase().removeDiacritics().indexOf(query.toString().toLowerCase().removeDiacritics()) > -1) { + founded = true; + } }); - } + return founded; + }); - if($.isFunction(settings.onResult)) { - results = settings.onResult.call(hidden_input, results); - } - cache.add(query, results); - populate_dropdown(query, results); + results_callback(results); } } } From aaa32ec6c6bb4b417d586ccf7ead1525da003a66 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 29 Jul 2011 16:10:35 -0700 Subject: [PATCH 63/63] Updated README.md to explain my changes --- README.md | 52 +++++++++++++--------------------------------------- 1 file changed, 13 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 11714ae2..963e1ecc 100644 --- a/README.md +++ b/README.md @@ -8,47 +8,21 @@ Tokeninput is a jQuery plugin which allows your users to select multiple items f Changes from original jquery-tokeninput --------------------------------------- -This is a forked version of jquery-tokeninput with a couple of different things. I grouped a small list of changes between original loopj's version from mine. +This is a forked version of jquery-tokeninput blending the latest versions of vdepizzol's and loopj's versions, plus updating the search-by-function behavior to actually work and to be similar to JQuery UI Autocomplete's behavior. -### Support for server-side diacritical marks and original case +In this version, in addition to having all of the latest features of both loopj's 1.5 version and vdepizzol's improvements, the main change is that in addition to providing a URL or inline data for the first parameter, you can provide a search function to handle the lookups for you. This function can construct whatever type of request you like, such as: -In loopj's version, only a parsed text of what the user types goes to the server-side destination, without accents and all in lowercase. This version will send to server-side the intact version without parsing any character. This particularly allows to the server-side to know how to handle new custom entries and special items. - -### Highlight terms - -Original jquery-tokeniput used a simple regex expression to highlight the typed characters in the dropdown list. This caused to be a huge problem with accents and other special chars. After doing a major clean-up[1] on this, this updated version of tokeninput will properly highlight terms like “são paulo” when “sao paulo” is provided. - -[1] Based on this great article http://www.alistapart.com/articles/accent-folding-for-auto-complete/ - -### Custom objects as data source - -Originally jquery-tokeninput would only accept objects with 'id' and 'name' parameters, and would even ignore any other element in the object. With several hooks on this, I made token accept any kind of object, with options to choose which columns to use when searching (by using `searchColumns`) and what to display as the name of the item (with `parseName` param). - -
    $("#demo").tokenInput([
    -    {id: 1, city: 'Vitória', state: 'ES'},
    -    {id: 5, name: 'São Paulo', state: 'SP'},
    -    {id: 2, city: 'Palo Alto', state: 'CA'},
    -    {id: 3, city: 'San Francisco', state: 'CA'},
    -],
    -{
    -    parseName: function(element) {
    -        return element.city + ', '+ element.state;
    +  $("#element").tokenInput(
    +    function(query, response) {
    +      $.ajax({ url: "your url here",
    +               data: { q: query, extra: "stuff" },
    +               dataType: "json",
    +               success: function(data) { response(data) }
    +             });
         },
    -    searchColumns: ['city', 'state']
    -});
    - -### Custom Entries - -Besides choosing predefined items from a list - -TODO - -### Refactored way for storing elements and send them to the server - -Original jquery-tokeninput had some misterious bug that kept deleted elements when form was submited. Besides this, it had a hard-to-maintain way to handle actions to add and remove tokens. - -With this new approach, I cleaned up and simplified the code, and added options to choose how to send the output to the server. + { + preventDuplicates: true, + onAdd: function(list,item) { alert("You added " + item.name) } + }); -Originally output would be a list of numbers imploded with ,, like 16,742,58,254. With custom entries support, output becomes 16,42,'Subway',37,'McDonald\'s',734. -Alternatively, a function can be used to parse the output using tokensFormatter. With this, you can per instance return a json as output.