b2evolution PHP Cross Reference Blogging Systems

Source: /rsc/js/jquery/jquery.tokeninput.js - 870 lines - 28021 bytes - Summary - Text - Print

   1  /*
   2   * jQuery Plugin: Tokenizing Autocomplete Text Entry
   3   * Version 1.6.0
   4   *
   5   * Copyright (c) 2009 James Smith (http://loopj.com)
   6   * Licensed jointly under the GPL and MIT licenses,
   7   * choose which one suits your project best!
   8   *
   9   */
  10  
  11  (function ($) {
  12  // Default settings
  13  var DEFAULT_SETTINGS = {
  14      // Search settings
  15      method: "GET",
  16      contentType: "json",
  17      queryParam: "q",
  18      searchDelay: 300,
  19      minChars: 1,
  20      propertyToSearch: "name",
  21      jsonContainer: null,
  22  
  23      // Display settings
  24      hintText: "Type in a search term",
  25      noResultsText: "No results",
  26      searchingText: "Searching...",
  27      deleteText: "×",
  28      animateDropdown: true,
  29  
  30      // Tokenization settings
  31      tokenLimit: null,
  32      tokenDelimiter: ",",
  33      preventDuplicates: false,
  34  
  35      // Output settings
  36      tokenValue: "id",
  37  
  38      // Prepopulation settings
  39      prePopulate: null,
  40      processPrePopulate: false,
  41  
  42      // Manipulation settings
  43      idPrefix: "token-input-",
  44  
  45      // Formatters
  46      resultsFormatter: function(item){ return "<li>" + item[this.propertyToSearch]+ "</li>" },
  47      tokenFormatter: function(item) { return "<li><p>" + item[this.propertyToSearch] + "</p></li>" },
  48  
  49      // Callbacks
  50      onResult: null,
  51      onAdd: null,
  52      onDelete: null,
  53      onReady: null
  54  };
  55  
  56  // Default classes to use when theming
  57  var DEFAULT_CLASSES = {
  58      tokenList: "token-input-list",
  59      token: "token-input-token",
  60      tokenDelete: "token-input-delete-token",
  61      selectedToken: "token-input-selected-token",
  62      highlightedToken: "token-input-highlighted-token",
  63      dropdown: "token-input-dropdown",
  64      dropdownItem: "token-input-dropdown-item",
  65      dropdownItem2: "token-input-dropdown-item2",
  66      selectedDropdownItem: "token-input-selected-dropdown-item",
  67      inputToken: "token-input-input-token"
  68  };
  69  
  70  // Input box position "enum"
  71  var POSITION = {
  72      BEFORE: 0,
  73      AFTER: 1,
  74      END: 2
  75  };
  76  
  77  // Keys "enum"
  78  var KEY = {
  79      BACKSPACE: 8,
  80      TAB: 9,
  81      ENTER: 13,
  82      ESCAPE: 27,
  83      SPACE: 32,
  84      PAGE_UP: 33,
  85      PAGE_DOWN: 34,
  86      END: 35,
  87      HOME: 36,
  88      LEFT: 37,
  89      UP: 38,
  90      RIGHT: 39,
  91      DOWN: 40,
  92      NUMPAD_ENTER: 108,
  93      COMMA: 188
  94  };
  95  
  96  // Additional public (exposed) methods
  97  var methods = {
  98      init: function(url_or_data_or_function, options) {
  99          var settings = $.extend({}, DEFAULT_SETTINGS, options || {});
 100  
 101          return this.each(function () {
 102              $(this).data("tokenInputObject", new $.TokenList(this, url_or_data_or_function, settings));
 103          });
 104      },
 105      clear: function() {
 106          this.data("tokenInputObject").clear();
 107          return this;
 108      },
 109      add: function(item) {
 110          this.data("tokenInputObject").add(item);
 111          return this;
 112      },
 113      remove: function(item) {
 114          this.data("tokenInputObject").remove(item);
 115          return this;
 116      },
 117      get: function() {
 118          return this.data("tokenInputObject").getTokens();
 119         }
 120  }
 121  
 122  // Expose the .tokenInput function to jQuery as a plugin
 123  $.fn.tokenInput = function (method) {
 124      // Method calling and initialization logic
 125      if(methods[method]) {
 126          return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
 127      } else {
 128          return methods.init.apply(this, arguments);
 129      }
 130  };
 131  
 132  // TokenList class for each input
 133  $.TokenList = function (input, url_or_data, settings) {
 134      //
 135      // Initialization
 136      //
 137  
 138      // Configure the data source
 139      if($.type(url_or_data) === "string" || $.type(url_or_data) === "function") {
 140          // Set the url to query against
 141          settings.url = url_or_data;
 142  
 143          // If the URL is a function, evaluate it here to do our initalization work
 144          var url = computeURL();
 145  
 146          // Make a smart guess about cross-domain if it wasn't explicitly specified
 147          if(settings.crossDomain === undefined) {
 148              if(url.indexOf("://") === -1) {
 149                  settings.crossDomain = false;
 150              } else {
 151                  settings.crossDomain = (location.href.split(/\/+/g)[1] !== url.split(/\/+/g)[1]);
 152              }
 153          }
 154      } else if(typeof(url_or_data) === "object") {
 155          // Set the local data to search through
 156          settings.local_data = url_or_data;
 157      }
 158  
 159      // Build class names
 160      if(settings.classes) {
 161          // Use custom class names
 162          settings.classes = $.extend({}, DEFAULT_CLASSES, settings.classes);
 163      } else if(settings.theme) {
 164          // Use theme-suffixed default class names
 165          settings.classes = {};
 166          $.each(DEFAULT_CLASSES, function(key, value) {
 167              settings.classes[key] = value + "-" + settings.theme;
 168          });
 169      } else {
 170          settings.classes = DEFAULT_CLASSES;
 171      }
 172  
 173  
 174      // Save the tokens
 175      var saved_tokens = [];
 176  
 177      // Keep track of the number of tokens in the list
 178      var token_count = 0;
 179  
 180      // Basic cache to save on db hits
 181      var cache = new $.TokenList.Cache();
 182  
 183      // Keep track of the timeout, old vals
 184      var timeout;
 185      var input_val;
 186  
 187      // Create a new text input an attach keyup events
 188      var input_box = $("<input type=\"text\"  autocomplete=\"off\">")
 189          .css({
 190              outline: "0 none"
 191          })
 192          .attr("id", settings.idPrefix + input.id)
 193          .focus(function () {
 194              if (settings.tokenLimit === null || settings.tokenLimit !== token_count) {
 195                  show_dropdown_hint();
 196              }
 197          })
 198          .blur(function () {
 199              hide_dropdown();
 200              if( $(this).val() != "" )
 201              {
 202                  $( ".token-input-list-facebook" ).addClass( "warning" );
 203              }
 204              else
 205              {
 206                  $( ".token-input-list-facebook" ).removeClass( "warning" );
 207              }
 208              //$(this).val("");
 209          })
 210          .bind("keyup keydown blur update", resize_input)
 211          .keydown(function (event) {
 212              var previous_token;
 213              var next_token;
 214  
 215              switch(event.keyCode) {
 216                  case KEY.LEFT:
 217                  case KEY.RIGHT:
 218                  case KEY.UP:
 219                  case KEY.DOWN:
 220                      if(!$(this).val()) {
 221                          previous_token = input_token.prev();
 222                          next_token = input_token.next();
 223  
 224                          if((previous_token.length && previous_token.get(0) === selected_token) || (next_token.length && next_token.get(0) === selected_token)) {
 225                              // Check if there is a previous/next token and it is selected
 226                              if(event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) {
 227                                  deselect_token($(selected_token), POSITION.BEFORE);
 228                              } else {
 229                                  deselect_token($(selected_token), POSITION.AFTER);
 230                              }
 231                          } else if((event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) && previous_token.length) {
 232                              // We are moving left, select the previous token if it exists
 233                              select_token($(previous_token.get(0)));
 234                          } else if((event.keyCode === KEY.RIGHT || event.keyCode === KEY.DOWN) && next_token.length) {
 235                              // We are moving right, select the next token if it exists
 236                              select_token($(next_token.get(0)));
 237                          }
 238                      } else {
 239                          var dropdown_item = null;
 240  
 241                          if(event.keyCode === KEY.DOWN || event.keyCode === KEY.RIGHT) {
 242                              dropdown_item = $(selected_dropdown_item).next();
 243                          } else {
 244                              dropdown_item = $(selected_dropdown_item).prev();
 245                          }
 246  
 247                          if(dropdown_item.length) {
 248                              select_dropdown_item(dropdown_item);
 249                          }
 250                          return false;
 251                      }
 252                      break;
 253  
 254                  case KEY.BACKSPACE:
 255                      previous_token = input_token.prev();
 256  
 257                      if(!$(this).val().length) {
 258                          if(selected_token) {
 259                              delete_token($(selected_token));
 260                              hidden_input.change();
 261                          } else if(previous_token.length) {
 262                              select_token($(previous_token.get(0)));
 263                          }
 264  
 265                          return false;
 266                      } else if($(this).val().length === 1) {
 267                          hide_dropdown();
 268                      } else {
 269                          // set a timeout just long enough to let this function finish.
 270                          setTimeout(function(){do_search();}, 5);
 271                      }
 272                      break;
 273  
 274                  case KEY.TAB:
 275                  case KEY.ENTER:
 276                  case KEY.NUMPAD_ENTER:
 277                  case KEY.COMMA:
 278                    if(selected_dropdown_item) {
 279                      add_token($(selected_dropdown_item).data("tokeninput"));
 280                      hidden_input.change();
 281                      return false;
 282                    }
 283                    break;
 284  
 285                  case KEY.ESCAPE:
 286                    hide_dropdown();
 287                    return true;
 288  
 289                  default:
 290                      if(String.fromCharCode(event.which)) {
 291                          // set a timeout just long enough to let this function finish.
 292                          setTimeout(function(){do_search();}, 5);
 293                      }
 294                      break;
 295              }
 296          });
 297  
 298      // Keep a reference to the original input box
 299      var hidden_input = $(input)
 300                             .hide()
 301                             .val("")
 302                             .focus(function () {
 303                                 input_box.focus();
 304                             })
 305                             .blur(function () {
 306                                 input_box.blur();
 307                             });
 308  
 309      // Keep a reference to the selected token and dropdown item
 310      var selected_token = null;
 311      var selected_token_index = 0;
 312      var selected_dropdown_item = null;
 313  
 314      // The list to store the token items in
 315      var token_list = $("<ul />")
 316          .addClass(settings.classes.tokenList)
 317          .click(function (event) {
 318              var li = $(event.target).closest("li");
 319              if(li && li.get(0) && $.data(li.get(0), "tokeninput")) {
 320                  toggle_select_token(li);
 321              } else {
 322                  // Deselect selected token
 323                  if(selected_token) {
 324                      deselect_token($(selected_token), POSITION.END);
 325                  }
 326  
 327                  // Focus input box
 328                  input_box.focus();
 329              }
 330          })
 331          .mouseover(function (event) {
 332              var li = $(event.target).closest("li");
 333              if(li && selected_token !== this) {
 334                  li.addClass(settings.classes.highlightedToken);
 335              }
 336          })
 337          .mouseout(function (event) {
 338              var li = $(event.target).closest("li");
 339              if(li && selected_token !== this) {
 340                  li.removeClass(settings.classes.highlightedToken);
 341              }
 342          })
 343          .insertBefore(hidden_input);
 344  
 345      // The token holding the input box
 346      var input_token = $("<li />")
 347          .addClass(settings.classes.inputToken)
 348          .appendTo(token_list)
 349          .append(input_box);
 350  
 351      // The list to store the dropdown items in
 352      var dropdown = $("<div>")
 353          .addClass(settings.classes.dropdown)
 354          .appendTo("body")
 355          .hide();
 356  
 357      // Magic element to help us resize the text input
 358      var input_resizer = $("<tester/>")
 359          .insertAfter(input_box)
 360          .css({
 361              position: "absolute",
 362              top: -9999,
 363              left: -9999,
 364              width: "auto",
 365              fontSize: input_box.css("fontSize"),
 366              fontFamily: input_box.css("fontFamily"),
 367              fontWeight: input_box.css("fontWeight"),
 368              letterSpacing: input_box.css("letterSpacing"),
 369              whiteSpace: "nowrap"
 370          });
 371  
 372      // Pre-populate list if items exist
 373      hidden_input.val("");
 374      var li_data = settings.prePopulate || hidden_input.data("pre");
 375      if(settings.processPrePopulate && $.isFunction(settings.onResult)) {
 376          li_data = settings.onResult.call(hidden_input, li_data);
 377      }
 378      if(li_data && li_data.length) {
 379          $.each(li_data, function (index, value) {
 380              insert_token(value);
 381              checkTokenLimit();
 382          });
 383      }
 384  
 385      // Initialization is done
 386      if($.isFunction(settings.onReady)) {
 387          settings.onReady.call();
 388      }
 389  
 390      //
 391      // Public functions
 392      //
 393  
 394      this.clear = function() {
 395          token_list.children("li").each(function() {
 396              if ($(this).children("input").length === 0) {
 397                  delete_token($(this));
 398              }
 399          });
 400      }
 401  
 402      this.add = function(item) {
 403          add_token(item);
 404      }
 405  
 406      this.remove = function(item) {
 407          token_list.children("li").each(function() {
 408              if ($(this).children("input").length === 0) {
 409                  var currToken = $(this).data("tokeninput");
 410                  var match = true;
 411                  for (var prop in item) {
 412                      if (item[prop] !== currToken[prop]) {
 413                          match = false;
 414                          break;
 415                      }
 416                  }
 417                  if (match) {
 418                      delete_token($(this));
 419                  }
 420              }
 421          });
 422      }
 423      
 424      this.getTokens = function() {
 425             return saved_tokens;
 426         }
 427  
 428      //
 429      // Private functions
 430      //
 431  
 432      function checkTokenLimit() {
 433          if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) {
 434              input_box.hide();
 435              hide_dropdown();
 436              return;
 437          }
 438      }
 439  
 440      function resize_input() {
 441          if(input_val === (input_val = input_box.val())) {return;}
 442  
 443          // Enter new content into resizer and resize input accordingly
 444          var escaped = input_val.replace(/&/g, '&amp;').replace(/\s/g,' ').replace(/</g, '&lt;').replace(/>/g, '&gt;');
 445          input_resizer.html(escaped);
 446          input_box.width(input_resizer.width() + 30);
 447      }
 448  
 449      function is_printable_character(keycode) {
 450          return ((keycode >= 48 && keycode <= 90) ||     // 0-1a-z
 451                  (keycode >= 96 && keycode <= 111) ||    // numpad 0-9 + - / * .
 452                  (keycode >= 186 && keycode <= 192) ||   // ; = , - . / ^
 453                  (keycode >= 219 && keycode <= 222));    // ( \ ) '
 454      }
 455  
 456      // Inner function to a token to the list
 457      function insert_token(item) {
 458          var this_token = settings.tokenFormatter(item);
 459          this_token = $(this_token)
 460            .addClass(settings.classes.token)
 461            .insertBefore(input_token);
 462  
 463          // The 'delete token' button
 464          $("<span>" + settings.deleteText + "</span>")
 465              .addClass(settings.classes.tokenDelete)
 466              .appendTo(this_token)
 467              .click(function () {
 468                  delete_token($(this).parent());
 469                  hidden_input.change();
 470                  return false;
 471              });
 472  
 473          // Store data on the token
 474          var token_data = {"id": item.id};
 475          token_data[settings.propertyToSearch] = item[settings.propertyToSearch];
 476          $.data(this_token.get(0), "tokeninput", item);
 477  
 478          // Save this token for duplicate checking
 479          saved_tokens = saved_tokens.slice(0,selected_token_index).concat([token_data]).concat(saved_tokens.slice(selected_token_index));
 480          selected_token_index++;
 481  
 482          // Update the hidden input
 483          update_hidden_input(saved_tokens, hidden_input);
 484  
 485          token_count += 1;
 486  
 487          // Check the token limit
 488          if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) {
 489              input_box.hide();
 490              hide_dropdown();
 491          }
 492  
 493          return this_token;
 494      }
 495  
 496      // Add a token to the token list based on user input
 497      function add_token (item) {
 498          var callback = settings.onAdd;
 499  
 500          // See if the token already exists and select it if we don't want duplicates
 501          if(token_count > 0 && settings.preventDuplicates) {
 502              var found_existing_token = null;
 503              token_list.children().each(function () {
 504                  var existing_token = $(this);
 505                  var existing_data = $.data(existing_token.get(0), "tokeninput");
 506                  if(existing_data && existing_data.id === item.id) {
 507                      found_existing_token = existing_token;
 508                      return false;
 509                  }
 510              });
 511  
 512              if(found_existing_token) {
 513                  select_token(found_existing_token);
 514                  input_token.insertAfter(found_existing_token);
 515                  input_box.focus();
 516                  return;
 517              }
 518          }
 519  
 520          // Insert the new tokens
 521          if(settings.tokenLimit == null || token_count < settings.tokenLimit) {
 522              insert_token(item);
 523              checkTokenLimit();
 524          }
 525  
 526          // Clear input box
 527          input_box.val("");
 528          $(".token-input-list-facebook").removeClass("warning");
 529  
 530          // Don't show the help dropdown, they've got the idea
 531          hide_dropdown();
 532  
 533          // Execute the onAdd callback if defined
 534          if($.isFunction(callback)) {
 535              callback.call(hidden_input,item);
 536          }
 537      }
 538  
 539      // Select a token in the token list
 540      function select_token (token) {
 541          token.addClass(settings.classes.selectedToken);
 542          selected_token = token.get(0);
 543  
 544          // Hide input box
 545          input_box.val("");
 546  
 547          // Hide dropdown if it is visible (eg if we clicked to select token)
 548          hide_dropdown();
 549      }
 550  
 551      // Deselect a token in the token list
 552      function deselect_token (token, position) {
 553          token.removeClass(settings.classes.selectedToken);
 554          selected_token = null;
 555  
 556          if(position === POSITION.BEFORE) {
 557              input_token.insertBefore(token);
 558              selected_token_index--;
 559          } else if(position === POSITION.AFTER) {
 560              input_token.insertAfter(token);
 561              selected_token_index++;
 562          } else {
 563              input_token.appendTo(token_list);
 564              selected_token_index = token_count;
 565          }
 566  
 567          // Show the input box and give it focus again
 568          input_box.focus();
 569      }
 570  
 571      // Toggle selection of a token in the token list
 572      function toggle_select_token(token) {
 573          var previous_selected_token = selected_token;
 574  
 575          if(selected_token) {
 576              deselect_token($(selected_token), POSITION.END);
 577          }
 578  
 579          if(previous_selected_token === token.get(0)) {
 580              deselect_token(token, POSITION.END);
 581          } else {
 582              select_token(token);
 583          }
 584      }
 585  
 586      // Delete a token from the token list
 587      function delete_token (token) {
 588          // Remove the id from the saved list
 589          var token_data = $.data(token.get(0), "tokeninput");
 590          var callback = settings.onDelete;
 591  
 592          var index = token.prevAll().length;
 593          if(index > selected_token_index) index--;
 594  
 595          // Delete the token
 596          token.remove();
 597          selected_token = null;
 598  
 599          // Show the input box and give it focus again
 600          input_box.focus();
 601  
 602          // Remove this token from the saved list
 603          saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1));
 604          if(index < selected_token_index) selected_token_index--;
 605  
 606          // Update the hidden input
 607          update_hidden_input(saved_tokens, hidden_input);
 608  
 609          token_count -= 1;
 610  
 611          if(settings.tokenLimit !== null) {
 612              input_box
 613                  .show()
 614                  .val("")
 615                  .focus();
 616          }
 617  
 618          // Execute the onDelete callback if defined
 619          if($.isFunction(callback)) {
 620              callback.call(hidden_input,token_data);
 621          }
 622      }
 623  
 624      // Update the hidden input box value
 625      function update_hidden_input(saved_tokens, hidden_input) {
 626          var token_values = $.map(saved_tokens, function (el) {
 627              return el[settings.tokenValue];
 628          });
 629          hidden_input.val(token_values.join(settings.tokenDelimiter));
 630  
 631      }
 632  
 633      // Hide and clear the results dropdown
 634      function hide_dropdown () {
 635          dropdown.hide().empty();
 636          selected_dropdown_item = null;
 637      }
 638  
 639      function show_dropdown() {
 640          dropdown
 641              .css({
 642                  position: "absolute",
 643                  top: $(token_list).offset().top + $(token_list).outerHeight(),
 644                  left: $(token_list).offset().left,
 645                  width: $(token_list).outerWidth() - 2,
 646                  zindex: 999
 647              })
 648              .show();
 649      }
 650  
 651      function show_dropdown_searching () {
 652          if(settings.searchingText) {
 653              dropdown.html("<p>"+settings.searchingText+"</p>");
 654              show_dropdown();
 655          }
 656      }
 657  
 658      function show_dropdown_hint () {
 659          if(settings.hintText) {
 660              dropdown.html("<p>"+settings.hintText+"</p>");
 661              show_dropdown();
 662          }
 663      }
 664  
 665      // Highlight the query part of the search term
 666      function highlight_term(value, term) {
 667          return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<b>$1</b>");
 668      }
 669      
 670      function find_value_and_highlight_term(template, value, term) {
 671          return template.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + value + ")(?![^<>]*>)(?![^&;]+;)", "g"), highlight_term(value, term));
 672      }
 673  
 674      // Populate the results dropdown with some results
 675      function populate_dropdown (query, results) {
 676          if(results && results.length) {
 677              dropdown.empty();
 678              var dropdown_ul = $("<ul>")
 679                  .appendTo(dropdown)
 680                  .mouseover(function (event) {
 681                      select_dropdown_item($(event.target).closest("li"));
 682                  })
 683                  .mousedown(function (event) {
 684                      add_token($(event.target).closest("li").data("tokeninput"));
 685                      hidden_input.change();
 686                      return false;
 687                  })
 688                  .hide();
 689  
 690              $.each(results, function(index, value) {
 691                  var this_li = settings.resultsFormatter(value);
 692                  
 693                  this_li = find_value_and_highlight_term(this_li ,value[settings.propertyToSearch], query);            
 694                  
 695                  this_li = $(this_li).appendTo(dropdown_ul);
 696                  
 697                  if(index % 2) {
 698                      this_li.addClass(settings.classes.dropdownItem);
 699                  } else {
 700                      this_li.addClass(settings.classes.dropdownItem2);
 701                  }
 702  
 703                  if(index === 0) {
 704                      select_dropdown_item(this_li);
 705                  }
 706  
 707                  $.data(this_li.get(0), "tokeninput", value);
 708              });
 709  
 710              show_dropdown();
 711  
 712              if(settings.animateDropdown) {
 713                  dropdown_ul.slideDown("fast");
 714              } else {
 715                  dropdown_ul.show();
 716              }
 717          } else {
 718              if(settings.noResultsText) {
 719                  dropdown.html("<p>"+settings.noResultsText+"</p>");
 720                  show_dropdown();
 721              }
 722          }
 723      }
 724  
 725      // Highlight an item in the results dropdown
 726      function select_dropdown_item (item) {
 727          if(item) {
 728              if(selected_dropdown_item) {
 729                  deselect_dropdown_item($(selected_dropdown_item));
 730              }
 731  
 732              item.addClass(settings.classes.selectedDropdownItem);
 733              selected_dropdown_item = item.get(0);
 734          }
 735      }
 736  
 737      // Remove highlighting from an item in the results dropdown
 738      function deselect_dropdown_item (item) {
 739          item.removeClass(settings.classes.selectedDropdownItem);
 740          selected_dropdown_item = null;
 741      }
 742  
 743      // Do a search and show the "searching" dropdown if the input is longer
 744      // than settings.minChars
 745      function do_search() {
 746          var query = input_box.val().toLowerCase();
 747  
 748          if(query && query.length) {
 749              if(selected_token) {
 750                  deselect_token($(selected_token), POSITION.AFTER);
 751              }
 752  
 753              if(query.length >= settings.minChars) {
 754                  show_dropdown_searching();
 755                  clearTimeout(timeout);
 756  
 757                  timeout = setTimeout(function(){
 758                      run_search(query);
 759                  }, settings.searchDelay);
 760              } else {
 761                  hide_dropdown();
 762              }
 763          }
 764      }
 765  
 766      // Do the actual search
 767      function run_search(query) {
 768          var cache_key = query + computeURL();
 769          var cached_results = cache.get(cache_key);
 770          if(cached_results) {
 771              populate_dropdown(query, cached_results);
 772          } else {
 773              // Are we doing an ajax search or local data search?
 774              if(settings.url) {
 775                  var url = computeURL();
 776                  // Extract exisiting get params
 777                  var ajax_params = {};
 778                  ajax_params.data = {};
 779                  if(url.indexOf("?") > -1) {
 780                      var parts = url.split("?");
 781                      ajax_params.url = parts[0];
 782  
 783                      var param_array = parts[1].split("&");
 784                      $.each(param_array, function (index, value) {
 785                          var kv = value.split("=");
 786                          ajax_params.data[kv[0]] = kv[1];
 787                      });
 788                  } else {
 789                      ajax_params.url = url;
 790                  }
 791  
 792                  // Prepare the request
 793                  ajax_params.data[settings.queryParam] = query;
 794                  ajax_params.type = settings.method;
 795                  ajax_params.dataType = settings.contentType;
 796                  if(settings.crossDomain) {
 797                      ajax_params.dataType = "jsonp";
 798                  }
 799  
 800                  // Attach the success callback
 801                  ajax_params.success = function(results) {
 802                    if($.isFunction(settings.onResult)) {
 803                        results = settings.onResult.call(hidden_input, results);
 804                    }
 805                    cache.add(cache_key, settings.jsonContainer ? results[settings.jsonContainer] : results);
 806  
 807                    // only populate the dropdown if the results are associated with the active search query
 808                    if(input_box.val().toLowerCase() === query) {
 809                        populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
 810                    }
 811                  };
 812  
 813                  // Make the request
 814                  $.ajax(ajax_params);
 815              } else if(settings.local_data) {
 816                  // Do the search through local data
 817                  var results = $.grep(settings.local_data, function (row) {
 818                      return row[settings.propertyToSearch].toLowerCase().indexOf(query.toLowerCase()) > -1;
 819                  });
 820  
 821                  if($.isFunction(settings.onResult)) {
 822                      results = settings.onResult.call(hidden_input, results);
 823                  }
 824                  cache.add(cache_key, results);
 825                  populate_dropdown(query, results);
 826              }
 827          }
 828      }
 829  
 830      // compute the dynamic URL
 831      function computeURL() {
 832          var url = settings.url;
 833          if(typeof settings.url == 'function') {
 834              url = settings.url.call();
 835          }
 836          return url;
 837      }
 838  };
 839  
 840  // Really basic cache for the results
 841  $.TokenList.Cache = function (options) {
 842      var settings = $.extend({
 843          max_size: 500
 844      }, options);
 845  
 846      var data = {};
 847      var size = 0;
 848  
 849      var flush = function () {
 850          data = {};
 851          size = 0;
 852      };
 853  
 854      this.add = function (query, results) {
 855          if(size > settings.max_size) {
 856              flush();
 857          }
 858  
 859          if(!data[query]) {
 860              size += 1;
 861          }
 862  
 863          data[query] = results;
 864      };
 865  
 866      this.get = function (query) {
 867          return data[query];
 868      };
 869  };
 870  }(jQuery));

title

Description

title

Description

title

Description

title

title

Body