Changeset 23275


Ignore:
Timestamp:
Jun 16, 2013, 10:53:20 PM (7 years ago)
Author:
mistic100
Message:

feature:2927 Update TokenInput to 1.6.1/mistic100

Location:
trunk
Files:
1 added
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/admin/themes/default/template/batch_manager_global.tpl

    r23055 r23275  
    5656{/literal}{/footer_script}
    5757
     58{combine_css path='themes/default/js/plugins/jquery.tokeninput.css'}
    5859{combine_script id='jquery.tokeninput' load='footer' require='jquery' path='themes/default/js/plugins/jquery.tokeninput.js'}
    5960{combine_script id='jquery.progressBar' load='footer' path='themes/default/js/plugins/jquery.progressbar.min.js'}
     
    7475      animateDropdown: false,
    7576      preventDuplicates: true,
    76       allowCreation: true
     77      allowFreeTagging: true
    7778    }
    7879  );
     
    8687      animateDropdown: false,
    8788      preventDuplicates: true,
    88       allowCreation: false
     89      allowFreeTagging: false
    8990    }
    9091  );
  • trunk/admin/themes/default/template/batch_manager_unit.tpl

    r18974 r23275  
    33{include file='include/colorbox.inc.tpl'}
    44
     5{combine_css path='themes/default/js/plugins/jquery.tokeninput.css'}
    56{combine_script id='jquery.tokeninput' load='async' require='jquery' path='themes/default/js/plugins/jquery.tokeninput.js'}
    67{footer_script require='jquery.tokeninput'}
     
    1516      animateDropdown: false,
    1617      preventDuplicates: true,
    17       allowCreation: true
     18      allowFreeTagging: true
    1819    }
    1920  );
  • trunk/admin/themes/default/template/picture_modify.tpl

    r20531 r23275  
    1212{/literal}{/footer_script}
    1313
     14{combine_css path='themes/default/js/plugins/jquery.tokeninput.css'}
    1415{combine_script id='jquery.tokeninput' load='async' require='jquery' path='themes/default/js/plugins/jquery.tokeninput.js'}
    1516{footer_script require='jquery.tokeninput'}
     
    2425      animateDropdown: false,
    2526      preventDuplicates: true,
    26       allowCreation: true
     27      allowFreeTagging: true
    2728    }
    2829  );
  • trunk/admin/themes/default/theme.css

    r23259 r23275  
    963963#batchManagerGlobal #filter_dimension .ui-slider-horizontal {width:650px;margin:5px 0 10px 0;}
    964964
    965 
    966 /* TokenInput (with Facebook style) */
    967 ul.token-input-list {overflow: hidden; height: auto !important; height: 1%;width: 400px;border: 1px solid #8496ba;cursor: text;font-size: 12px;font-family: Verdana;min-height: 1px;z-index: 999;margin: 0;padding: 0;background-color: #fff;list-style-type: none;clear: left;}
    968 ul.token-input-list li input {border: 0;width: 100px;padding: 3px 8px;background-color: white;margin: 2px 0;-webkit-appearance: caret;}
    969 li.token-input-token {overflow: hidden; height: auto !important; height: 15px;margin: 3px;padding: 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;float: left;white-space: nowrap;}
    970 li.token-input-token p {display: inline;padding: 0;margin: 0;}
    971 li.token-input-token span {color: #a6b3cf;margin-left: 5px;font-weight: bold;cursor: pointer;}
    972 li.token-input-selected-token {background-color: #5670a6;border: 1px solid #3b5998;color: #fff;}
    973 li.token-input-input-token {float: left;margin: 0;padding: 0;list-style-type: none;width:10px;}
    974 div.token-input-dropdown {position: absolute;width: 400px;background-color: #fff;overflow: hidden;border-left: 1px solid #ccc;border-right: 1px solid #ccc;border-bottom: 1px solid #ccc;cursor: default;font-size: 11px;font-family: Verdana;z-index: 1;}
    975 div.token-input-dropdown p {margin: 0;padding: 5px;font-weight: bold;color: #777;}
    976 div.token-input-dropdown ul {margin: 0;padding: 0;}
    977 div.token-input-dropdown ul li {background-color: #fff;padding: 3px;margin: 0;list-style-type: none;}
    978 div.token-input-dropdown ul li.token-input-dropdown-item {background-color: #fff;}
    979 div.token-input-dropdown ul li.token-input-dropdown-item2 {background-color: #fff;}
    980 div.token-input-dropdown ul li em {font-weight: bold;font-style: normal;}
    981 div.token-input-dropdown ul li.token-input-selected-dropdown-item {background-color: #3b5998;color: #fff;}
    982 
    983965#mainConf a.addFilter {font-weight:normal;margin-left:20px;}
    984966#mainConf a.removeFilter {font-weight:normal;}
  • trunk/admin/themes/roma/theme.css

    r23121 r23275  
    275275
    276276/* TokenInput (with Facebook style for ROMA) */
    277 ul.token-input-list {border-color:#666;background-color:#444;}
    278 ul.token-input-list li input {background-color:#444;}
    279 li.token-input-token span {color:#878787;}
    280 div.token-input-dropdown {background-color:#eee;border-color:#666;}
    281 div.token-input-dropdown ul li {background-color:#eee;}
    282 div.token-input-dropdown ul li.token-input-selected-dropdown-item {background-color:#FF7800;}
     277ul.token-input-list {border-color:#666 !important;background-color:#444 !important;}
     278ul.token-input-list li input {background-color:#444 !important;}
     279li.token-input-token span {color:#878787 !important;}
     280div.token-input-dropdown {background-color:#eee !important;border-color:#666 !important;}
     281div.token-input-dropdown ul li {background-color:#eee !important;}
     282div.token-input-dropdown ul li.token-input-selected-dropdown-item {background-color:#FF7800 !important;}
    283283
    284284#progressbar {border:1px solid #666; background-color:#666;}
  • trunk/themes/default/js/plugins/jquery.tokeninput.js

    r23231 r23275  
    1 /**
    2   DON'T MAKE AUTOMATIC UPGRADE
    3   This is a merged version of :
    4   + https://github.com/gr2m/jquery-tokeninput/
    5   + https://github.com/mistic100/jquery-tokeninput/
    6 */
    7 
    81/*
    92 * jQuery Plugin: Tokenizing Autocomplete Text Entry
    10  * Version 1.4.2
     3 * Version 1.6.1
    114 *
    125 * Copyright (c) 2009 James Smith (http://loopj.com)
     
    147 * choose which one suits your project best!
    158 *
     9 * https://github.com/mistic100/jquery-tokeninput
    1610 */
    1711
     
    1913// Default settings
    2014var DEFAULT_SETTINGS = {
     15    // Search settings
     16    method: "GET",
     17    queryParam: "q",
     18    searchDelay: 300,
     19    minChars: 1,
     20    propertyToSearch: "name",
     21    jsonContainer: null,
     22    contentType: "json",
     23
     24    // Prepopulation settings
     25    prePopulate: null,
     26    processPrePopulate: false,
     27
     28    // Display settings
    2129    hintText: "Type in a search term",
    2230    noResultsText: "No results",
    2331    searchingText: "Searching...",
    24     newText: "(new)",
    2532    deleteText: "×",
    26     searchDelay: 300,
    27     minChars: 1,
     33    newText: " (new)",
     34    animateDropdown: true,
     35    placeholder: null,
     36    theme: null,
     37    zindex: 999,
     38    resultsLimit: null,
     39
     40    enableHTML: false,
     41
     42    resultsFormatter: function(item) {
     43      var string = item[this.propertyToSearch];
     44      return "<li>" + (this.enableHTML ? string : _escapeHTML(string)) + "</li>";
     45    },
     46
     47    tokenFormatter: function(item) {
     48      var string = item[this.propertyToSearch];
     49      return "<li><p>" + (this.enableHTML ? string : _escapeHTML(string)) + "</p></li>";
     50    },
     51
     52    // Tokenization settings
    2853    tokenLimit: null,
    29     jsonContainer: null,
    30     method: "GET",
    31     contentType: "json",
    32     queryParam: "q",
    3354    tokenDelimiter: ",",
    3455    preventDuplicates: false,
    35     prePopulate: null,
    36     processPrePopulate: false,
    37     animateDropdown: true,
     56    tokenValue: "id",
     57
     58    // Behavioral settings
     59    allowFreeTagging: false,
     60    freeTaggingHint: true,
     61    allowTabOut: false,
     62
     63    // Callbacks
    3864    onResult: null,
     65    onCachedResult: null,
    3966    onAdd: null,
     67    onFreeTaggingAdd: null,
    4068    onDelete: null,
    41     allowCreation: false,
    42     caseSensitive: false
     69    onReady: null,
     70
     71    // Other settings
     72    idPrefix: "token-input-",
     73
     74    // Keep track if the input is currently in disabled mode
     75    disabled: false
    4376};
    4477
     
    4780    tokenList: "token-input-list",
    4881    token: "token-input-token",
     82    tokenReadOnly: "token-input-token-readonly",
    4983    tokenDelete: "token-input-delete-token",
    5084    selectedToken: "token-input-selected-token",
     
    5488    dropdownItem2: "token-input-dropdown-item2",
    5589    selectedDropdownItem: "token-input-selected-dropdown-item",
    56     inputToken: "token-input-input-token"
     90    inputToken: "token-input-input-token",
     91    focused: "token-input-focused",
     92    disabled: "token-input-disabled"
    5793};
    5894
     
    83119};
    84120
     121var HTML_ESCAPES = {
     122  '&': '&amp;',
     123  '<': '&lt;',
     124  '>': '&gt;',
     125  '"': '&quot;',
     126  "'": '&#x27;',
     127  '/': '&#x2F;'
     128};
     129
     130var HTML_ESCAPE_CHARS = /[&<>"'\/]/g;
     131
     132function coerceToString(val) {
     133  return String((val === null || val === undefined) ? '' : val);
     134}
     135
     136function _escapeHTML(text) {
     137  return coerceToString(text).replace(HTML_ESCAPE_CHARS, function(match) {
     138    return HTML_ESCAPES[match];
     139  });
     140}
     141
     142// Additional public (exposed) methods
     143var methods = {
     144    init: function(url_or_data_or_function, options) {
     145        var settings = $.extend({}, DEFAULT_SETTINGS, options || {});
     146
     147        return this.each(function () {
     148            $(this).data("settings", settings);
     149            $(this).data("tokenInputObject", new $.TokenList(this, url_or_data_or_function, settings));
     150        });
     151    },
     152    clear: function() {
     153        this.data("tokenInputObject").clear();
     154        return this;
     155    },
     156    add: function(item) {
     157        this.data("tokenInputObject").add(item);
     158        return this;
     159    },
     160    remove: function(item) {
     161        this.data("tokenInputObject").remove(item);
     162        return this;
     163    },
     164    get: function() {
     165        return this.data("tokenInputObject").getTokens();
     166    },
     167    toggleDisabled: function(disable) {
     168        this.data("tokenInputObject").toggleDisabled(disable);
     169        return this;
     170    },
     171    setOptions: function(options){
     172        $(this).data("settings", $.extend({}, $(this).data("settings"), options || {}));
     173        return this;
     174    },
     175    destroy: function () {
     176        if(this.data("tokenInputObject")){
     177            this.data("tokenInputObject").clear();
     178            var tmpInput = this;
     179            var closest = this.parent();
     180            closest.empty();
     181            tmpInput.show();
     182            closest.append(tmpInput);
     183            return tmpInput;
     184        }
     185    }
     186};
    85187
    86188// Expose the .tokenInput function to jQuery as a plugin
    87 $.fn.tokenInput = function (url_or_data, options) {
    88     var settings = $.extend({}, DEFAULT_SETTINGS, options || {});
    89 
    90     return this.each(function () {
    91         new $.TokenList(this, url_or_data, settings);
    92     });
     189$.fn.tokenInput = function (method) {
     190    // Method calling and initialization logic
     191    if(methods[method]) {
     192        return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
     193    } else {
     194        return methods.init.apply(this, arguments);
     195    }
    93196};
    94 
    95197
    96198// TokenList class for each input
     
    101203
    102204    // Configure the data source
    103     if(typeof(url_or_data) === "string") {
     205    if($.type(url_or_data) === "string" || $.type(url_or_data) === "function") {
    104206        // Set the url to query against
    105         settings.url = url_or_data;
     207        $(input).data("settings").url = url_or_data;
     208
     209        // If the URL is a function, evaluate it here to do our initalization work
     210        var url = computeURL();
    106211
    107212        // Make a smart guess about cross-domain if it wasn't explicitly specified
    108         if(settings.crossDomain === undefined) {
    109             if(settings.url.indexOf("://") === -1) {
    110                 settings.crossDomain = false;
     213        if($(input).data("settings").crossDomain === undefined && typeof url === "string") {
     214            if(url.indexOf("://") === -1) {
     215                $(input).data("settings").crossDomain = false;
    111216            } else {
    112                 settings.crossDomain = (location.href.split(/\/+/g)[1] !== settings.url.split(/\/+/g)[1]);
     217                $(input).data("settings").crossDomain = (location.href.split(/\/+/g)[1] !== url.split(/\/+/g)[1]);
    113218            }
    114219        }
    115220    } else if(typeof(url_or_data) === "object") {
    116221        // Set the local data to search through
    117         settings.local_data = url_or_data;
     222        $(input).data("settings").local_data = url_or_data;
    118223    }
    119224
    120225    // Build class names
    121     if(settings.classes) {
     226    if($(input).data("settings").classes) {
    122227        // Use custom class names
    123         settings.classes = $.extend({}, DEFAULT_CLASSES, settings.classes);
    124     } else if(settings.theme) {
     228        $(input).data("settings").classes = $.extend({}, DEFAULT_CLASSES, $(input).data("settings").classes);
     229    } else if($(input).data("settings").theme) {
    125230        // Use theme-suffixed default class names
    126         settings.classes = {};
     231        $(input).data("settings").classes = {};
    127232        $.each(DEFAULT_CLASSES, function(key, value) {
    128             settings.classes[key] = value + "-" + settings.theme;
     233            $(input).data("settings").classes[key] = value + "-" + $(input).data("settings").theme;
    129234        });
    130235    } else {
    131         settings.classes = DEFAULT_CLASSES;
     236        $(input).data("settings").classes = DEFAULT_CLASSES;
    132237    }
    133238
     
    147252
    148253    // Create a new text input an attach keyup events
    149     var input_box = $("<input type=\"text\"  autocomplete=\"off\">")
     254    var input_box = $("<input type=\"text\"  autocomplete=\"off\" autocapitalize=\"off\">")
    150255        .css({
    151256            outline: "none"
    152257        })
     258        .attr("id", $(input).data("settings").idPrefix + input.id)
    153259        .focus(function () {
    154             if (settings.tokenLimit === null || settings.tokenLimit !== token_count) {
     260            if ($(input).data("settings").disabled) {
     261                return false;
     262            } else
     263            if ($(input).data("settings").tokenLimit === null || $(input).data("settings").tokenLimit !== token_count) {
    155264                show_dropdown_hint();
    156265            }
     266            token_list.addClass($(input).data("settings").classes.focused);
    157267        })
    158268        .blur(function () {
    159269            hide_dropdown();
     270
     271            if ($(input).data("settings").allowFreeTagging) {
     272              add_freetagging_tokens();
     273            }
     274
     275            $(this).val("");
     276            token_list.removeClass($(input).data("settings").classes.focused);
    160277        })
    161278        .bind("keyup keydown blur update", resize_input)
     
    199316                            select_dropdown_item(dropdown_item);
    200317                        }
    201                         return false;
    202318                    }
     319                    return false;
    203320                    break;
    204321
     
    209326                        if(selected_token) {
    210327                            delete_token($(selected_token));
     328                            hidden_input.change();
    211329                        } else if(previous_token.length) {
    212330                            select_token($(previous_token.get(0)));
     
    227345                case KEY.COMMA:
    228346                  if(selected_dropdown_item) {
    229                     add_token($(selected_dropdown_item));
    230                     return false;
     347                    add_token($(selected_dropdown_item).data("tokeninput"));
     348                    hidden_input.change();
     349                  } else {
     350                    if ($(input).data("settings").allowFreeTagging) {
     351                      if($(input).data("settings").allowTabOut && $(this).val() === "") {
     352                        return true;
     353                      } else {
     354                        add_freetagging_tokens();
     355                      }
     356                    } else {
     357                      $(this).val("");
     358                      if($(input).data("settings").allowTabOut) {
     359                        return true;
     360                      }
     361                    }
     362                    event.stopPropagation();
     363                    event.preventDefault();
    231364                  }
    232                   break;
     365                  return false;
    233366
    234367                case KEY.ESCAPE:
     
    245378        });
    246379
     380    // Keep reference for placeholder
     381    if (settings.placeholder)
     382        input_box.attr("placeholder", settings.placeholder)
     383
    247384    if ($(input).get(0).tagName == 'SELECT') {
    248     // Create a new input to store selected tokens, original will be delete later
    249       var hidden_input = $("<input type=\"text\"  name=\"" + $(input).attr('name') + "\" autocomplete=\"off\">")
    250                              .hide()
    251                              .val("")
    252                              .focus(function () {
    253                                  input_box.focus();
    254                              })
    255                              .blur(function () {
    256                                  input_box.blur();
    257                              })
    258                              .insertBefore(input);
     385        // Create a new input to store selected tokens, original will be removed later
     386        var hidden_input = $("<input type=\"text\"  name=\"" + $(input).attr('name') + "\" autocomplete=\"off\">")
     387                               .hide()
     388                               .val("")
     389                               .focus(function () {
     390                                   focus_with_timeout(input_box);
     391                               })
     392                               .blur(function () {
     393                                   input_box.blur();
     394                                   return hidden_input;
     395                               })
     396                               .insertBefore(input);
     397
     398        // get prepopulate options and store them in hidden_input
     399        var select_data = [];
     400        $(input).children('option').each(function () {
     401            var item = {};
     402            item[$(input).data("settings").tokenValue] = $(this).attr('value');
     403            item[$(input).data("settings").propertyToSearch] = $(this).text();
     404            select_data[ select_data.length ] = item;
     405        });
     406        hidden_input.data("pre", select_data);
     407       
     408        // remove the SELECT object
     409        hidden_input.data("settings", $(input).data("settings"));
     410        $(input).remove();
     411        input = hidden_input[0];
     412
    259413    } else {
    260     // Keep a reference to the original input box
    261       var hidden_input = $(input)
    262                              .hide()
    263                              .val("")
    264                              .focus(function () {
    265                                  input_box.focus();
    266                              })
    267                              .blur(function () {
    268                                  input_box.blur();
    269                              });
     414        // Keep a reference to the original input box
     415        var hidden_input = $(input)
     416                               .hide()
     417                               .val("")
     418                               .focus(function () {
     419                                   focus_with_timeout(input_box);
     420                               })
     421                               .blur(function () {
     422                                   input_box.blur();
     423                                   //return the object to this can be referenced in the callback functions.
     424                                   return hidden_input;
     425                               });
    270426    }
    271427
     
    277433    // The list to store the token items in
    278434    var token_list = $("<ul />")
    279         .addClass(settings.classes.tokenList)
     435        .addClass($(input).data("settings").classes.tokenList)
    280436        .click(function (event) {
    281437            var li = $(event.target).closest("li");
     
    289445
    290446                // Focus input box
    291                 input_box.focus();
     447                focus_with_timeout(input_box);
    292448            }
    293449        })
     
    295451            var li = $(event.target).closest("li");
    296452            if(li && selected_token !== this) {
    297                 li.addClass(settings.classes.highlightedToken);
     453                li.addClass($(input).data("settings").classes.highlightedToken);
    298454            }
    299455        })
     
    301457            var li = $(event.target).closest("li");
    302458            if(li && selected_token !== this) {
    303                 li.removeClass(settings.classes.highlightedToken);
     459                li.removeClass($(input).data("settings").classes.highlightedToken);
    304460            }
    305461        })
     
    308464    // The token holding the input box
    309465    var input_token = $("<li />")
    310         .addClass(settings.classes.inputToken)
     466        .addClass($(input).data("settings").classes.inputToken)
    311467        .appendTo(token_list)
    312468        .append(input_box);
     
    314470    // The list to store the dropdown items in
    315471    var dropdown = $("<div>")
    316         .addClass(settings.classes.dropdown)
     472        .addClass($(input).data("settings").classes.dropdown)
    317473        .appendTo("body")
    318474        .hide();
     
    335491    // Pre-populate list if items exist
    336492    hidden_input.val("");
    337     var li_data = settings.prePopulate || hidden_input.data("pre");
    338     if(settings.processPrePopulate && $.isFunction(settings.onResult)) {
    339         li_data = settings.onResult.call(hidden_input, li_data);
    340     }   
     493    var li_data = $(input).data("settings").prePopulate || hidden_input.data("pre");
     494    if($(input).data("settings").processPrePopulate && $.isFunction($(input).data("settings").onResult)) {
     495        li_data = $(input).data("settings").onResult.call(hidden_input, li_data);
     496    }
    341497    if(li_data && li_data.length) {
    342498        $.each(li_data, function (index, value) {
    343             insert_token(value.id, value.name);
     499            insert_token(value);
     500            checkTokenLimit();
     501            input_box.attr("placeholder", null)
    344502        });
    345503    }
    346    
    347     // Pre-populate from SELECT options
    348     if ($(input).get(0).tagName == 'SELECT') {
    349       $(input).children('option').each(function () {
    350         insert_token($(this).attr('value'), $(this).text());
    351       });
    352     }
    353 
    354 
     504
     505    // Check if widget should initialize as disabled
     506    if ($(input).data("settings").disabled) {
     507        toggleDisabled(true);
     508    }
     509
     510    // Initialization is done
     511    if($.isFunction($(input).data("settings").onReady)) {
     512        $(input).data("settings").onReady.call();
     513    }
     514
     515    //
     516    // Public functions
     517    //
     518
     519    this.clear = function() {
     520        token_list.children("li").each(function() {
     521            if ($(this).children("input").length === 0) {
     522                delete_token($(this));
     523            }
     524        });
     525    };
     526
     527    this.add = function(item) {
     528        add_token(item);
     529    };
     530
     531    this.remove = function(item) {
     532        token_list.children("li").each(function() {
     533            if ($(this).children("input").length === 0) {
     534                var currToken = $(this).data("tokeninput");
     535                var match = true;
     536                for (var prop in item) {
     537                    if (item[prop] !== currToken[prop]) {
     538                        match = false;
     539                        break;
     540                    }
     541                }
     542                if (match) {
     543                    delete_token($(this));
     544                }
     545            }
     546        });
     547    };
     548
     549    this.getTokens = function() {
     550        return saved_tokens;
     551    };
     552
     553    this.toggleDisabled = function(disable) {
     554        toggleDisabled(disable);
     555    };
     556
     557    // Resize input to maximum width so the placeholder can be seen
     558    resize_input();
    355559
    356560    //
     
    358562    //
    359563
     564    function escapeHTML(text) {
     565      return $(input).data("settings").enableHTML ? text : _escapeHTML(text);
     566    }
     567
     568    // Toggles the widget between enabled and disabled state, or according
     569    // to the [disable] parameter.
     570    function toggleDisabled(disable) {
     571        if (typeof disable === 'boolean') {
     572            $(input).data("settings").disabled = disable
     573        } else {
     574            $(input).data("settings").disabled = !$(input).data("settings").disabled;
     575        }
     576        input_box.attr('disabled', $(input).data("settings").disabled);
     577        token_list.toggleClass($(input).data("settings").classes.disabled, $(input).data("settings").disabled);
     578        // if there is any token selected we deselect it
     579        if(selected_token) {
     580            deselect_token($(selected_token), POSITION.END);
     581        }
     582        hidden_input.attr('disabled', $(input).data("settings").disabled);
     583    }
     584
     585    function checkTokenLimit() {
     586        if($(input).data("settings").tokenLimit !== null && token_count >= $(input).data("settings").tokenLimit) {
     587            input_box.hide();
     588            hide_dropdown();
     589            return;
     590        }
     591    }
     592
    360593    function resize_input() {
    361594        if(input_val === (input_val = input_box.val())) {return;}
    362595
     596        // Get width left on the current line
     597        var width_left = token_list.width() - input_box.offset().left - token_list.offset().left;
    363598        // Enter new content into resizer and resize input accordingly
    364         var escaped = input_val.replace(/&/g, '&amp;').replace(/\s/g,' ').replace(/</g, '&lt;').replace(/>/g, '&gt;');
    365         input_resizer.html(escaped);
    366         input_box.width(input_resizer.width() + 30);
     599        input_resizer.html(_escapeHTML(input_val));
     600        // Get maximum width, minimum the size of input and maximum the widget's width
     601        input_box.width(Math.min(token_list.width(),
     602                                 Math.max(width_left, input_resizer.width() + 30)));
    367603    }
    368604
     
    374610    }
    375611
     612    function add_freetagging_tokens() {
     613        var value = $.trim(input_box.val());
     614        var tokens = value.split($(input).data("settings").tokenDelimiter);
     615        $.each(tokens, function(i, token) {
     616          if (!token) {
     617            return;
     618          }
     619
     620          if ($.isFunction($(input).data("settings").onFreeTaggingAdd)) {
     621            token = $(input).data("settings").onFreeTaggingAdd.call(hidden_input, token);
     622          }
     623          var object = {};
     624          object[$(input).data("settings").tokenValue] = object[$(input).data("settings").propertyToSearch] = token;
     625          add_token(object);
     626        });
     627    }
     628
    376629    // Inner function to a token to the list
    377     function insert_token(id, value) {
    378         var this_token = $("<li><p>"+ value +"</p></li>")
    379           .addClass(settings.classes.token)
    380           .insertBefore(input_token);
     630    function insert_token(item) {
     631        var $this_token = $($(input).data("settings").tokenFormatter(item));
     632        var readonly = item.readonly === true ? true : false;
     633
     634        if(readonly) $this_token.addClass($(input).data("settings").classes.tokenReadOnly);
     635
     636        $this_token.addClass($(input).data("settings").classes.token).insertBefore(input_token);
    381637
    382638        // The 'delete token' button
    383         $("<span>" + settings.deleteText + "</span>")
    384             .addClass(settings.classes.tokenDelete)
    385             .appendTo(this_token)
    386             .click(function () {
    387                 delete_token($(this).parent());
    388                 return false;
    389             });
     639        if(!readonly) {
     640          $("<span>" + $(input).data("settings").deleteText + "</span>")
     641              .addClass($(input).data("settings").classes.tokenDelete)
     642              .appendTo($this_token)
     643              .click(function () {
     644                  if (!$(input).data("settings").disabled) {
     645                      delete_token($(this).parent());
     646                      hidden_input.change();
     647                      return false;
     648                  }
     649              });
     650        }
    390651
    391652        // Store data on the token
    392         var token_data = {"id": id, "name": value};
    393         $.data(this_token.get(0), "tokeninput", token_data);
     653        var token_data = item;
     654        $.data($this_token.get(0), "tokeninput", item);
    394655
    395656        // Save this token for duplicate checking
     
    398659
    399660        // Update the hidden input
    400         var token_ids = $.map(saved_tokens, function (el) {
    401             return el.id;
    402         });
    403         hidden_input.val(token_ids.join(settings.tokenDelimiter));
     661        update_hidden_input(saved_tokens, hidden_input);
    404662
    405663        token_count += 1;
    406664
    407         return this_token;
     665        // Check the token limit
     666        if($(input).data("settings").tokenLimit !== null && token_count >= $(input).data("settings").tokenLimit) {
     667            input_box.hide();
     668            hide_dropdown();
     669        }
     670
     671        return $this_token;
    408672    }
    409673
    410674    // Add a token to the token list based on user input
    411675    function add_token (item) {
    412         var li_data = $.data(item.get(0), "tokeninput");
    413         var callback = settings.onAdd;
     676        var callback = $(input).data("settings").onAdd;
    414677
    415678        // See if the token already exists and select it if we don't want duplicates
    416         if(token_count > 0 && settings.preventDuplicates) {
     679        if(token_count > 0 && $(input).data("settings").preventDuplicates) {
    417680            var found_existing_token = null;
    418681            token_list.children().each(function () {
    419682                var existing_token = $(this);
    420683                var existing_data = $.data(existing_token.get(0), "tokeninput");
    421                 if(existing_data && existing_data.id === li_data.id) {
     684                if(existing_data && existing_data[settings.tokenValue] === item[settings.tokenValue]) {
    422685                    found_existing_token = existing_token;
    423686                    return false;
     
    428691                select_token(found_existing_token);
    429692                input_token.insertAfter(found_existing_token);
    430                 input_box.focus();
     693                focus_with_timeout(input_box);
    431694                return;
    432695            }
    433696        }
    434697
     698        // Squeeze input_box so we force no unnecessary line break
     699        input_box.width(0);
     700
    435701        // Insert the new tokens
    436         insert_token(li_data.id, li_data.name);
    437 
    438         // Check the token limit
    439         if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) {
    440             input_box.hide();
    441             hide_dropdown();
    442             return;
    443         } else {
    444             input_box.focus();
     702        if($(input).data("settings").tokenLimit == null || token_count < $(input).data("settings").tokenLimit) {
     703            insert_token(item);
     704            // Remove the placeholder so it's not seen after you've added a token
     705            input_box.attr("placeholder", null)
     706            checkTokenLimit();
    445707        }
    446708
     
    453715        // Execute the onAdd callback if defined
    454716        if($.isFunction(callback)) {
    455             callback.call(hidden_input,li_data);
     717            callback.call(hidden_input,item);
    456718        }
    457719    }
     
    459721    // Select a token in the token list
    460722    function select_token (token) {
    461         token.addClass(settings.classes.selectedToken);
    462         selected_token = token.get(0);
    463 
    464         // Hide input box
    465         input_box.val("");
    466 
    467         // Hide dropdown if it is visible (eg if we clicked to select token)
    468         hide_dropdown();
     723        if (!$(input).data("settings").disabled) {
     724            token.addClass($(input).data("settings").classes.selectedToken);
     725            selected_token = token.get(0);
     726
     727            // Hide input box
     728            input_box.val("");
     729
     730            // Hide dropdown if it is visible (eg if we clicked to select token)
     731            hide_dropdown();
     732        }
    469733    }
    470734
    471735    // Deselect a token in the token list
    472736    function deselect_token (token, position) {
    473         token.removeClass(settings.classes.selectedToken);
     737        token.removeClass($(input).data("settings").classes.selectedToken);
    474738        selected_token = null;
    475739
     
    486750
    487751        // Show the input box and give it focus again
    488         input_box.focus();
     752        focus_with_timeout(input_box);
    489753    }
    490754
     
    508772        // Remove the id from the saved list
    509773        var token_data = $.data(token.get(0), "tokeninput");
    510         var callback = settings.onDelete;
     774        var callback = $(input).data("settings").onDelete;
    511775
    512776        var index = token.prevAll().length;
     
    518782
    519783        // Show the input box and give it focus again
    520         input_box.focus();
     784        focus_with_timeout(input_box);
    521785
    522786        // Remove this token from the saved list
    523787        saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1));
     788        if (saved_tokens.length == 0) {
     789            input_box.attr("placeholder", settings.placeholder)
     790        }
    524791        if(index < selected_token_index) selected_token_index--;
    525792
    526793        // Update the hidden input
    527         var token_ids = $.map(saved_tokens, function (el) {
    528             return el.id;
    529         });
    530         hidden_input.val(token_ids.join(settings.tokenDelimiter));
     794        update_hidden_input(saved_tokens, hidden_input);
    531795
    532796        token_count -= 1;
    533797
    534         if(settings.tokenLimit !== null) {
     798        if($(input).data("settings").tokenLimit !== null) {
    535799            input_box
    536800                .show()
    537                 .val("")
    538                 .focus();
     801                .val("");
     802            focus_with_timeout(input_box);
    539803        }
    540804
     
    543807            callback.call(hidden_input,token_data);
    544808        }
     809    }
     810
     811    // Update the hidden input box value
     812    function update_hidden_input(saved_tokens, hidden_input) {
     813        var token_values = $.map(saved_tokens, function (el) {
     814            if(typeof $(input).data("settings").tokenValue == 'function')
     815              return $(input).data("settings").tokenValue.call(this, el);
     816
     817            return el[$(input).data("settings").tokenValue];
     818        });
     819        hidden_input.val(token_values.join($(input).data("settings").tokenDelimiter));
     820
    545821    }
    546822
     
    555831            .css({
    556832                position: "absolute",
    557                 top: $(token_list).offset().top + $(token_list).outerHeight(),
    558                 left: $(token_list).offset().left,
    559                 zindex: 999
     833                top: token_list.offset().top + token_list.outerHeight(),
     834                left: token_list.offset().left,
     835                width: token_list.width(),
     836                'z-index': $(input).data("settings").zindex
    560837            })
    561838            .show();
     
    563840
    564841    function show_dropdown_searching () {
    565         if(settings.searchingText) {
    566             dropdown.html("<p>"+settings.searchingText+"</p>");
     842        if($(input).data("settings").searchingText) {
     843            dropdown.html("<p>" + escapeHTML($(input).data("settings").searchingText) + "</p>");
    567844            show_dropdown();
    568845        }
     
    570847
    571848    function show_dropdown_hint () {
    572         if(settings.hintText) {
    573             dropdown.html("<p>"+settings.hintText+"</p>");
     849        if($(input).data("settings").hintText) {
     850            dropdown.html("<p>" + escapeHTML($(input).data("settings").hintText) + "</p>");
    574851            show_dropdown();
    575852        }
     853    }
     854
     855    var regexp_special_chars = new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g');
     856    function regexp_escape(term) {
     857        return term.replace(regexp_special_chars, '\\$&');
    576858    }
    577859
    578860    // Highlight the query part of the search term
    579861    function highlight_term(value, term) {
    580         var param = "g";
    581         if (!settings.caseSensitive) param+= "i";
    582         return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + escape_regexp_chars(term) + ")(?![^<>]*>)(?![^&;]+;)", param), "<b>$1</b>");
    583     }
    584    
    585     function escape_regexp_chars(string) {
    586         var specials = new RegExp("[.*+?|()\\[\\]{}\\\\]", "g"); // .*+?|()[]{}\
    587         return string.replace(specials, "\\$&");
     862        return value.replace(
     863          new RegExp(
     864            "(?![^&;]+;)(?!<[^<>]*)(" + regexp_escape(term) + ")(?![^<>]*>)(?![^&;]+;)",
     865            "gi"
     866          ), function(match, p1) {
     867            return "<b>" + escapeHTML(p1) + "</b>";
     868          }
     869        );
     870    }
     871
     872    function find_value_and_highlight_term(template, value, term) {
     873        return template.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + regexp_escape(value) + ")(?![^<>]*>)(?![^&;]+;)", "g"), highlight_term(value, term));
    588874    }
    589875
     
    598884                })
    599885                .mousedown(function (event) {
    600                     add_token($(event.target).closest("li"));
     886                    add_token($(event.target).closest("li").data("tokeninput"));
     887                    hidden_input.change();
    601888                    return false;
    602889                })
    603890                .hide();
    604891
     892            if ($(input).data("settings").resultsLimit && results.length > $(input).data("settings").resultsLimit) {
     893                results = results.slice(0, $(input).data("settings").resultsLimit);
     894            }
     895
    605896            $.each(results, function(index, value) {
    606                 var this_li = $("<li>" + highlight_term(value.name, query) + "</li>")
    607                                   .appendTo(dropdown_ul);
     897                var this_li = $(input).data("settings").resultsFormatter(value);
     898
     899                this_li = find_value_and_highlight_term(this_li ,value[$(input).data("settings").propertyToSearch], query);
     900
     901                this_li = $(this_li).appendTo(dropdown_ul);
    608902
    609903                if(index % 2) {
    610                     this_li.addClass(settings.classes.dropdownItem);
     904                    this_li.addClass($(input).data("settings").classes.dropdownItem);
    611905                } else {
    612                     this_li.addClass(settings.classes.dropdownItem2);
     906                    this_li.addClass($(input).data("settings").classes.dropdownItem2);
    613907                }
    614908
     
    617911                }
    618912
    619                 $.data(this_li.get(0), "tokeninput", {"id": value.id, "name": value.name});
     913                $.data(this_li.get(0), "tokeninput", value);
    620914            });
    621915
    622916            show_dropdown();
    623917
    624             if(settings.animateDropdown) {
     918            if($(input).data("settings").animateDropdown) {
    625919                dropdown_ul.slideDown("fast");
    626920            } else {
     
    628922            }
    629923        } else {
    630             if(settings.noResultsText) {
    631                 dropdown.html("<p>"+settings.noResultsText+"</p>");
     924            if($(input).data("settings").noResultsText) {
     925                dropdown.html("<p>" + escapeHTML($(input).data("settings").noResultsText) + "</p>");
    632926                show_dropdown();
    633927            }
     
    642936            }
    643937
    644             item.addClass(settings.classes.selectedDropdownItem);
     938            item.addClass($(input).data("settings").classes.selectedDropdownItem);
    645939            selected_dropdown_item = item.get(0);
    646940        }
     
    649943    // Remove highlighting from an item in the results dropdown
    650944    function deselect_dropdown_item (item) {
    651         item.removeClass(settings.classes.selectedDropdownItem);
     945        item.removeClass($(input).data("settings").classes.selectedDropdownItem);
    652946        selected_dropdown_item = null;
    653947    }
    654948
    655949    // Do a search and show the "searching" dropdown if the input is longer
    656     // than settings.minChars
     950    // than $(input).data("settings").minChars
    657951    function do_search() {
    658952        var query = input_box.val();
    659         if (!settings.caseSensitive) query = query.toLowerCase();
    660953
    661954        if(query && query.length) {
     
    664957            }
    665958
    666             if(query.length >= settings.minChars) {
     959            if(query.length >= $(input).data("settings").minChars) {
    667960                show_dropdown_searching();
    668961                clearTimeout(timeout);
     
    670963                timeout = setTimeout(function(){
    671964                    run_search(query);
    672                 }, settings.searchDelay);
     965                }, $(input).data("settings").searchDelay);
    673966            } else {
    674967                hide_dropdown();
     
    679972    // Do the actual search
    680973    function run_search(query) {
    681         var cached_results = cache.get(query);
     974        var cache_key = query + computeURL();
     975        var cached_results = cache.get(cache_key);
    682976        if(cached_results) {
     977            if ($.isFunction($(input).data("settings").onCachedResult)) {
     978              cached_results = $(input).data("settings").onCachedResult.call(hidden_input, cached_results);
     979            }
    683980            populate_dropdown(query, cached_results);
    684981        } else {
    685982            // Are we doing an ajax search or local data search?
    686             if(settings.url) {
     983            if($(input).data("settings").url) {
     984                var url = computeURL();
    687985                // Extract exisiting get params
    688986                var ajax_params = {};
    689987                ajax_params.data = {};
    690                 if(settings.url.indexOf("?") > -1) {
    691                     var parts = settings.url.split("?");
     988                if(url.indexOf("?") > -1) {
     989                    var parts = url.split("?");
    692990                    ajax_params.url = parts[0];
    693991
     
    698996                    });
    699997                } else {
    700                     ajax_params.url = settings.url;
     998                    ajax_params.url = url;
    701999                }
    7021000
    7031001                // Prepare the request
    704                 ajax_params.data[settings.queryParam] = query;
    705                 ajax_params.type = settings.method;
    706                 ajax_params.dataType = settings.contentType;
    707                 if(settings.crossDomain) {
     1002                ajax_params.data[$(input).data("settings").queryParam] = query;
     1003                ajax_params.type = $(input).data("settings").method;
     1004                ajax_params.dataType = $(input).data("settings").contentType;
     1005                if($(input).data("settings").crossDomain) {
    7081006                    ajax_params.dataType = "jsonp";
    7091007                }
     
    7111009                // Attach the success callback
    7121010                ajax_params.success = function(results) {
    713                   if($.isFunction(settings.onResult)) {
    714                       results = settings.onResult.call(hidden_input, results);
     1011                  cache.add(cache_key, $(input).data("settings").jsonContainer ? results[$(input).data("settings").jsonContainer] : results);
     1012                  if($.isFunction($(input).data("settings").onResult)) {
     1013                      results = $(input).data("settings").onResult.call(hidden_input, results);
    7151014                  }
    7161015                 
    717                   if(settings.allowCreation) {
    718                       results.push({name: input_box.val() + settings.newText, id: input_box.val()});
     1016                  if($(input).data("settings").allowFreeTagging && $(input).data("settings").freeTaggingHint) {
     1017                      results.push({name: input_box.val() + $(input).data("settings").newText, id: input_box.val()});
    7191018                  }
    720                   cache.add(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
    7211019
    7221020                  // only populate the dropdown if the results are associated with the active search query
    723                   var value = input_box.val();
    724                   if (!settings.caseSensitive) value = value.toLowerCase();
    725                   if(value === query) {
    726                       populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
     1021                  if(input_box.val() === query) {
     1022                      populate_dropdown(query, $(input).data("settings").jsonContainer ? results[$(input).data("settings").jsonContainer] : results);
    7271023                  }
    7281024                };
     
    7301026                // Make the request
    7311027                $.ajax(ajax_params);
    732             } else if(settings.local_data) {
     1028            } else if($(input).data("settings").local_data) {
    7331029                // Do the search through local data
    734                 var results = $.grep(settings.local_data, function (row) {
    735                     if (settings.caseSensitive) {
    736                         return row.name.indexOf(query) > -1;
    737                     }
    738                     else {
    739                         return row.name.toLowerCase().indexOf(query.toLowerCase()) > -1;
    740                     }
     1030                var results = $.grep($(input).data("settings").local_data, function (row) {
     1031                    return row[$(input).data("settings").propertyToSearch].toLowerCase().indexOf(query.toLowerCase()) > -1;
    7411032                });
    742 
    743                 if($.isFunction(settings.onResult)) {
    744                     results = settings.onResult.call(hidden_input, results);
     1033               
     1034                if($(input).data("settings").allowFreeTagging && $(input).data("settings").freeTaggingHint) {
     1035                    results.push({name: input_box.val() + $(input).data("settings").newText, id: input_box.val()});
    7451036                }
    746                
    747                 if(settings.allowCreation) {
    748                     results.push({name: input_box.val() + settings.newText, id: input_box.val()});
     1037
     1038                cache.add(cache_key, results);
     1039                if($.isFunction($(input).data("settings").onResult)) {
     1040                    results = $(input).data("settings").onResult.call(hidden_input, results);
    7491041                }
    750                
    751                 cache.add(query, results);               
    752                
    7531042                populate_dropdown(query, results);
    7541043            }
    7551044        }
    7561045    }
    757    
    758     if ($(input).get(0).tagName == 'SELECT') {
    759       $(input).remove();
    760     }
     1046
     1047    // compute the dynamic URL
     1048    function computeURL() {
     1049        var url = $(input).data("settings").url;
     1050        if(typeof $(input).data("settings").url == 'function') {
     1051            url = $(input).data("settings").url.call($(input).data("settings"));
     1052        }
     1053        return url;
     1054    }
     1055
     1056    // Bring browser focus to the specified object.
     1057    // Use of setTimeout is to get around an IE bug.
     1058    // (See, e.g., http://stackoverflow.com/questions/2600186/focus-doesnt-work-in-ie)
     1059    //
     1060    // obj: a jQuery object to focus()
     1061    function focus_with_timeout(obj) {
     1062        setTimeout(function() { obj.focus(); }, 50);
     1063    }
     1064
    7611065};
    7621066
     
    7921096};
    7931097}(jQuery));
     1098
Note: See TracChangeset for help on using the changeset viewer.