| define([
  'jquery'
], function ($) {
  function Tokenizer (decorated, $element, options) {
    var tokenizer = options.get('tokenizer');
    if (tokenizer !== undefined) {
      this.tokenizer = tokenizer;
    }
    decorated.call(this, $element, options);
  }
  Tokenizer.prototype.bind = function (decorated, container, $container) {
    decorated.call(this, container, $container);
    this.$search =  container.dropdown.$search || container.selection.$search ||
      $container.find('.select2-search__field');
  };
  Tokenizer.prototype.query = function (decorated, params, callback) {
    var self = this;
    function createAndSelect (data) {
      // Normalize the data object so we can use it for checks
      var item = self._normalizeItem(data);
      // Check if the data object already exists as a tag
      // Select it if it doesn't
      var $existingOptions = self.$element.find('option').filter(function () {
        return $(this).val() === item.id;
      });
      // If an existing option wasn't found for it, create the option
      if (!$existingOptions.length) {
        var $option = self.option(item);
        $option.attr('data-select2-tag', true);
        self._removeOldTags();
        self.addOptions([$option]);
      }
      // Select the item, now that we know there is an option for it
      select(item);
    }
    function select (data) {
      self.trigger('select', {
        data: data
      });
    }
    params.term = params.term || '';
    var tokenData = this.tokenizer(params, this.options, createAndSelect);
    if (tokenData.term !== params.term) {
      // Replace the search term if we have the search box
      if (this.$search.length) {
        this.$search.val(tokenData.term);
        this.$search.trigger('focus');
      }
      params.term = tokenData.term;
    }
    decorated.call(this, params, callback);
  };
  Tokenizer.prototype.tokenizer = function (_, params, options, callback) {
    var separators = options.get('tokenSeparators') || [];
    var term = params.term;
    var i = 0;
    var createTag = this.createTag || function (params) {
      return {
        id: params.term,
        text: params.term
      };
    };
    while (i < term.length) {
      var termChar = term[i];
      if ($.inArray(termChar, separators) === -1) {
        i++;
        continue;
      }
      var part = term.substr(0, i);
      var partParams = $.extend({}, params, {
        term: part
      });
      var data = createTag(partParams);
      if (data == null) {
        i++;
        continue;
      }
      callback(data);
      // Reset the term to not include the tokenized portion
      term = term.substr(i + 1) || '';
      i = 0;
    }
    return {
      term: term
    };
  };
  return Tokenizer;
});
 |