/*!
* bootstrap-tokenfield
* https://github.com/sliptree/bootstrap-tokenfield
* Copyright 2013-2014 Sliptree and other contributors; Licensed MIT
*/

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // For CommonJS and CommonJS-like environments where a window with jQuery
        // is present, execute the factory with the jQuery instance from the window object
        // For environments that do not inherently posses a window with a document
        // (such as Node.js), expose a Tokenfield-making factory as module.exports
        // This accentuates the need for the creation of a real window or passing in a jQuery instance
        // e.g. require("bootstrap-tokenfield")(window); or require("bootstrap-tokenfield")($);
        module.exports = global.window && global.window.$ ?
      factory(global.window.$) :
      function (input) {
          if (!input.$ && !input.fn) {
              throw new Error("Tokenfield requires a window object with jQuery or a jQuery instance");
          }
          return factory(input.$ || input);
      };
    } else {
        // Browser globals
        factory(jQuery, window);
    }
} (function ($, window) {

    "use strict"; // jshint ;_;

    /* TOKENFIELD PUBLIC CLASS DEFINITION
    * ============================== */

    var Tokenfield = function (element, options) {
        var _self = this

        this.$element = $(element)
        this.textDirection = this.$element.css('direction');

        // Extend options
        this.options = $.extend(true, {}, $.fn.tokenfield.defaults, { tokens: this.$element.val() }, this.$element.data(), options)

        // Setup delimiters and trigger keys
        this._delimiters = (typeof this.options.delimiter === 'string') ? [this.options.delimiter] : this.options.delimiter
        this._triggerKeys = $.map(this._delimiters, function (delimiter) {
            return delimiter.charCodeAt(0);
        });
        this._firstDelimiter = this._delimiters[0];

        // Check for whitespace, dash and special characters
        var whitespace = $.inArray(' ', this._delimiters)
      , dash = $.inArray('-', this._delimiters)

        if (whitespace >= 0)
            this._delimiters[whitespace] = '\\s'

        if (dash >= 0) {
            delete this._delimiters[dash]
            this._delimiters.unshift('-')
        }

        var specialCharacters = ['\\', '$', '[', '{', '^', '.', '|', '?', '*', '+', '(', ')']
        $.each(this._delimiters, function (index, character) {
            var pos = $.inArray(character, specialCharacters)
            if (pos >= 0) _self._delimiters[index] = '\\' + character;
        });

        // Store original input width
        var elRules = (window && typeof window.getMatchedCSSRules === 'function') ? window.getMatchedCSSRules(element) : null
      , elStyleWidth = element.style.width
      , elCSSWidth
      , elWidth = this.$element.width()

        if (elRules) {
            $.each(elRules, function (i, rule) {
                if (rule.style.width) {
                    elCSSWidth = rule.style.width;
                }
            });
        }

        // Move original input out of the way
        var hidingPosition = $('body').css('direction') === 'rtl' ? 'right' : 'left',
        originalStyles = { position: this.$element.css('position') };
        originalStyles[hidingPosition] = this.$element.css(hidingPosition);

        this.$element
      .data('original-styles', originalStyles)
      .data('original-tabindex', this.$element.prop('tabindex'))
      .css('position', 'absolute')
      .css(hidingPosition, '-10000px')
      .prop('tabindex', -1)

        // Create a wrapper
        this.$wrapper = $('<div class="tokenfield form-control" />')
        if (this.$element.hasClass('input-lg')) this.$wrapper.addClass('input-lg')
        if (this.$element.hasClass('input-sm')) this.$wrapper.addClass('input-sm')
        if (this.textDirection === 'rtl') this.$wrapper.addClass('rtl')

        // Create a new input
        var id = this.$element.prop('id') || new Date().getTime() + '' + Math.floor((1 + Math.random()) * 100)
        this.$input = $('<input type="' + this.options.inputType + '" class="token-input" autocomplete="off" />')
                    .appendTo(this.$wrapper)
                    .prop('placeholder', this.$element.prop('placeholder'))
                    .prop('id', id + '-tokenfield')
                    .prop('tabindex', this.$element.data('original-tabindex'))

        // Re-route original input label to new input
        var $label = $('label[for="' + this.$element.prop('id') + '"]')
        if ($label.length) {
            $label.prop('for', this.$input.prop('id'))
        }

        // Set up a copy helper to handle copy & paste
        this.$copyHelper = $('<input type="text" />').css('position', 'absolute').css(hidingPosition, '-10000px').prop('tabindex', -1).prependTo(this.$wrapper)

        // Set wrapper width
        if (elStyleWidth) {
            this.$wrapper.css('width', elStyleWidth);
        }
        else if (elCSSWidth) {
            this.$wrapper.css('width', elCSSWidth);
        }
        // If input is inside inline-form with no width set, set fixed width
        else if (this.$element.parents('.form-inline').length) {
            this.$wrapper.width(elWidth)
        }

        // Set tokenfield disabled, if original or fieldset input is disabled
        if (this.$element.prop('disabled') || this.$element.parents('fieldset[disabled]').length) {
            this.disable();
        }

        // Set tokenfield readonly, if original input is readonly
        if (this.$element.prop('readonly')) {
            this.readonly();
        }

        // Set up mirror for input auto-sizing
        this.$mirror = $('<span style="position:absolute; top:-999px; left:0; white-space:pre;"/>');
        this.$input.css('min-width', this.options.minWidth + 'px')
        $.each([
        'fontFamily',
        'fontSize',
        'fontWeight',
        'fontStyle',
        'letterSpacing',
        'textTransform',
        'wordSpacing',
        'textIndent'
    ], function (i, val) {
        _self.$mirror[0].style[val] = _self.$input.css(val);
    });
        this.$mirror.appendTo('body')

        // Insert tokenfield to HTML
        this.$wrapper.insertBefore(this.$element)
        this.$element.prependTo(this.$wrapper)

        // Calculate inner input width
        this.update()

        // Create initial tokens, if any
        this.setTokens(this.options.tokens, false, !this.$element.val() && this.options.tokens)

        // Start listening to events
        this.listen()

        // Initialize autocomplete, if necessary
        if (!$.isEmptyObject(this.options.autocomplete)) {
            var side = this.textDirection === 'rtl' ? 'right' : 'left'
       , autocompleteOptions = $.extend({
           minLength: this.options.showAutocompleteOnFocus ? 0 : null,
           position: { my: side + " top", at: side + " bottom", of: this.$wrapper }
       }, this.options.autocomplete)

            this.$input.autocomplete(autocompleteOptions)
        }

        // Initialize typeahead, if necessary
        if (!$.isEmptyObject(this.options.typeahead)) {

            var typeaheadOptions = this.options.typeahead
        , defaults = {
            minLength: this.options.showAutocompleteOnFocus ? 0 : null
        }
        , args = $.isArray(typeaheadOptions) ? typeaheadOptions : [typeaheadOptions, typeaheadOptions]

            args[0] = $.extend({}, defaults, args[0])

            this.$input.typeahead.apply(this.$input, args)
            this.typeahead = true
        }
    }

    Tokenfield.prototype = {

        constructor: Tokenfield

  , createToken: function (attrs, triggerChange) {
      var _self = this

      if (typeof attrs === 'string') {
          attrs = { value: attrs, label: attrs }
      } else {
          // Copy objects to prevent contamination of data sources.
          attrs = $.extend({}, attrs)
      }

      if (typeof triggerChange === 'undefined') {
          triggerChange = true
      }

      // Normalize label and value
      attrs.value = $.trim(attrs.value.toString());
      attrs.label = attrs.label && attrs.label.length ? $.trim(attrs.label) : attrs.value

      // Bail out if has no value or label, or label is too short
      if (!attrs.value.length || !attrs.label.length || attrs.label.length <= this.options.minLength) return

      // Bail out if maximum number of tokens is reached
      if (this.options.limit && this.getTokens().length >= this.options.limit) return

      // Allow changing token data before creating it
      var createEvent = $.Event('tokenfield:createtoken', { attrs: attrs })
      this.$element.trigger(createEvent)

      // Bail out if there if attributes are empty or event was defaultPrevented
      if (!createEvent.attrs || createEvent.isDefaultPrevented()) return

      var $token = $('<div class="token" />')
            .append('<span class="token-label" />')
            .append('<a href="#" class="close" tabindex="-1">&times;</a>')
            .data('attrs', attrs)

      // Insert token into HTML
      if (this.$input.hasClass('tt-input')) {
          // If the input has typeahead enabled, insert token before it's parent
          this.$input.parent().before($token)
      } else {
          this.$input.before($token)
      }

      // Temporarily set input width to minimum
      this.$input.css('width', this.options.minWidth + 'px')

      var $tokenLabel = $token.find('.token-label')
        , $closeButton = $token.find('.close')

      // Determine maximum possible token label width
      if (!this.maxTokenWidth) {
          this.maxTokenWidth =
          this.$wrapper.width() - $closeButton.outerWidth() -
          parseInt($closeButton.css('margin-left'), 10) -
          parseInt($closeButton.css('margin-right'), 10) -
          parseInt($token.css('border-left-width'), 10) -
          parseInt($token.css('border-right-width'), 10) -
          parseInt($token.css('padding-left'), 10) -
          parseInt($token.css('padding-right'), 10)
          parseInt($tokenLabel.css('border-left-width'), 10) -
          parseInt($tokenLabel.css('border-right-width'), 10) -
          parseInt($tokenLabel.css('padding-left'), 10) -
          parseInt($tokenLabel.css('padding-right'), 10)
          parseInt($tokenLabel.css('margin-left'), 10) -
          parseInt($tokenLabel.css('margin-right'), 10)
      }

      $tokenLabel.css('max-width', this.maxTokenWidth)
      if (this.options.html)
          $tokenLabel.html(attrs.label)
      else
          $tokenLabel.text(attrs.label)

      // Listen to events on token
      $token
        .on('mousedown', function (e) {
            if (_self._disabled || _self._readonly) return false
            _self.preventDeactivation = true
        })
        .on('click', function (e) {
            if (_self._disabled || _self._readonly) return false
            _self.preventDeactivation = false

            if (e.ctrlKey || e.metaKey) {
                e.preventDefault()
                return _self.toggle($token)
            }

            _self.activate($token, e.shiftKey, e.shiftKey)
        })
        .on('dblclick', function (e) {
            if (_self._disabled || _self._readonly || !_self.options.allowEditing) return false
            _self.edit($token)
        })

      $closeButton
          .on('click', $.proxy(this.remove, this))

      // Trigger createdtoken event on the original field
      // indicating that the token is now in the DOM
      this.$element.trigger($.Event('tokenfield:createdtoken', {
          attrs: attrs,
          relatedTarget: $token.get(0)
      }))

      // Trigger change event on the original field
      if (triggerChange) {
          this.$element.val(this.getTokensList()).trigger($.Event('change', { initiator: 'tokenfield' }))
      }

      // Update tokenfield dimensions
      var _self = this
      setTimeout(function () {
          _self.update()
      }, 0)

      // Return original element
      return this.$element.get(0)
  }

  , setTokens: function (tokens, add, triggerChange) {
      if (!add) this.$wrapper.find('.token').remove()

      if (!tokens) return

      if (typeof triggerChange === 'undefined') {
          triggerChange = true
      }

      if (typeof tokens === 'string') {
          if (this._delimiters.length) {
              // Split based on delimiters
              tokens = tokens.split(new RegExp('[' + this._delimiters.join('') + ']'))
          } else {
              tokens = [tokens];
          }
      }

      var _self = this
      $.each(tokens, function (i, attrs) {
          _self.createToken(attrs, triggerChange)
      })

      return this.$element.get(0)
  }

  , getTokenData: function ($token) {
      var data = $token.map(function () {
          var $token = $(this);
          return $token.data('attrs')
      }).get();

      if (data.length == 1) {
          data = data[0];
      }

      return data;
  }

  , getTokens: function (active) {
      var self = this
        , tokens = []
        , activeClass = active ? '.active' : '' // get active tokens only
      this.$wrapper.find('.token' + activeClass).each(function () {
          tokens.push(self.getTokenData($(this)))
      })
      return tokens
  }

  , getTokensList: function (delimiter, beautify, active) {
      delimiter = delimiter || this._firstDelimiter
      beautify = (typeof beautify !== 'undefined' && beautify !== null) ? beautify : this.options.beautify

      var separator = delimiter + (beautify && delimiter !== ' ' ? ' ' : '')
      return $.map(this.getTokens(active), function (token) {
          return token.value
      }).join(separator)
  }

  , getInput: function () {
      return this.$input.val()
  }

  , setInput: function (val) {
      if (this.$input.hasClass('tt-input')) {
          // Typeahead acts weird when simply setting input value to empty,
          // so we set the query to empty instead
          this.$input.typeahead('val', val)
      } else {
          this.$input.val(val)
      }
  }

  , listen: function () {
      var _self = this

      this.$element
        .on('change', $.proxy(this.change, this))

      this.$wrapper
        .on('mousedown', $.proxy(this.focusInput, this))

      this.$input
        .on('focus', $.proxy(this.focus, this))
        .on('blur', $.proxy(this.blur, this))
        .on('paste', $.proxy(this.paste, this))
        .on('keydown', $.proxy(this.keydown, this))
        .on('keypress', $.proxy(this.keypress, this))
        .on('keyup', $.proxy(this.keyup, this))

      this.$copyHelper
        .on('focus', $.proxy(this.focus, this))
        .on('blur', $.proxy(this.blur, this))
        .on('keydown', $.proxy(this.keydown, this))
        .on('keyup', $.proxy(this.keyup, this))

      // Secondary listeners for input width calculation
      this.$input
        .on('keypress', $.proxy(this.update, this))
        .on('keyup', $.proxy(this.update, this))

      this.$input
        .on('autocompletecreate', function () {
            // Set minimum autocomplete menu width
            var $_menuElement = $(this).data('ui-autocomplete').menu.element

            var minWidth = _self.$wrapper.outerWidth() -
              parseInt($_menuElement.css('border-left-width'), 10) -
              parseInt($_menuElement.css('border-right-width'), 10)

            $_menuElement.css('min-width', minWidth + 'px')
        })
        .on('autocompleteselect', function (e, ui) {
            if (_self.createToken(ui.item)) {
                _self.$input.val('')
                if (_self.$input.data('edit')) {
                    _self.unedit(true)
                }
            }
            return false
        })
        .on('typeahead:selected typeahead:autocompleted', function (e, datum, dataset) {
            // Create token
            if (_self.createToken(datum)) {
                _self.$input.typeahead('val', '')
                if (_self.$input.data('edit')) {
                    _self.unedit(true)
                }
            }
        })

      // Listen to window resize
      $(window).on('resize', $.proxy(this.update, this))

  }

  , keydown: function (e) {

      if (!this.focused) return

      var _self = this

      switch (e.keyCode) {
          case 8: // backspace
              if (!this.$input.is(document.activeElement)) break
              this.lastInputValue = this.$input.val()
              break

          case 37: // left arrow
              leftRight(this.textDirection === 'rtl' ? 'next' : 'prev')
              break

          case 38: // up arrow
              upDown('prev')
              break

          case 39: // right arrow
              leftRight(this.textDirection === 'rtl' ? 'prev' : 'next')
              break

          case 40: // down arrow
              upDown('next')
              break

          case 65: // a (to handle ctrl + a)
              if (this.$input.val().length > 0 || !(e.ctrlKey || e.metaKey)) break
              this.activateAll()
              e.preventDefault()
              break

          case 9: // tab
          case 13: // enter

              // We will handle creating tokens from autocomplete in autocomplete events
              if (this.$input.data('ui-autocomplete') && this.$input.data('ui-autocomplete').menu.element.find("li:has(a.ui-state-focus), li.ui-state-focus").length) break

              // We will handle creating tokens from typeahead in typeahead events
              if (this.$input.hasClass('tt-input') && this.$wrapper.find('.tt-cursor').length) break
              if (this.$input.hasClass('tt-input') && this.$wrapper.find('.tt-hint').val() && this.$wrapper.find('.tt-hint').val().length) break

              // Create token
              if (this.$input.is(document.activeElement) && this.$input.val().length || this.$input.data('edit')) {
                  return this.createTokensFromInput(e, this.$input.data('edit'));
              }

              // Edit token
              if (e.keyCode === 13) {
                  if (!this.$copyHelper.is(document.activeElement) || this.$wrapper.find('.token.active').length !== 1) break
                  if (!_self.options.allowEditing) break
                  this.edit(this.$wrapper.find('.token.active'))
              }
      }

      function leftRight(direction) {
          if (_self.$input.is(document.activeElement)) {
              if (_self.$input.val().length > 0) return

              direction += 'All'
              var $token = _self.$input.hasClass('tt-input') ? _self.$input.parent()[direction]('.token:first') : _self.$input[direction]('.token:first')
              if (!$token.length) return

              _self.preventInputFocus = true
              _self.preventDeactivation = true

              _self.activate($token)
              e.preventDefault()

          } else {
              _self[direction](e.shiftKey)
              e.preventDefault()
          }
      }

      function upDown(direction) {
          if (!e.shiftKey) return

          if (_self.$input.is(document.activeElement)) {
              if (_self.$input.val().length > 0) return

              var $token = _self.$input.hasClass('tt-input') ? _self.$input.parent()[direction + 'All']('.token:first') : _self.$input[direction + 'All']('.token:first')
              if (!$token.length) return

              _self.activate($token)
          }

          var opposite = direction === 'prev' ? 'next' : 'prev'
          , position = direction === 'prev' ? 'first' : 'last'

          _self.$firstActiveToken[opposite + 'All']('.token').each(function () {
              _self.deactivate($(this))
          })

          _self.activate(_self.$wrapper.find('.token:' + position), true, true)
          e.preventDefault()
      }

      this.lastKeyDown = e.keyCode
  }

  , keypress: function (e) {

      // Comma
      if ($.inArray(e.which, this._triggerKeys) !== -1 && this.$input.is(document.activeElement)) {
          if (this.$input.val()) {
              this.createTokensFromInput(e)
          }
          return false;
      }
  }

  , keyup: function (e) {
      this.preventInputFocus = false

      if (!this.focused) return

      switch (e.keyCode) {
          case 8: // backspace
              if (this.$input.is(document.activeElement)) {
                  if (this.$input.val().length || this.lastInputValue.length && this.lastKeyDown === 8) break

                  this.preventDeactivation = true
                  var $prevToken = this.$input.hasClass('tt-input') ? this.$input.parent().prevAll('.token:first') : this.$input.prevAll('.token:first')

                  if (!$prevToken.length) break

                  this.activate($prevToken)
              } else {
                  this.remove(e)
              }
              break

          case 46: // delete
              this.remove(e, 'next')
              break
      }
      this.lastKeyUp = e.keyCode
  }

  , focus: function (e) {
      this.focused = true
      this.$wrapper.addClass('focus')

      if (this.$input.is(document.activeElement)) {
          this.$wrapper.find('.active').removeClass('active')
          this.$firstActiveToken = null

          if (this.options.showAutocompleteOnFocus) {
              this.search()
          }
      }
  }

  , blur: function (e) {

      this.focused = false
      this.$wrapper.removeClass('focus')

      if (!this.preventDeactivation && !this.$element.is(document.activeElement)) {
          this.$wrapper.find('.active').removeClass('active')
          this.$firstActiveToken = null
      }

      if (!this.preventCreateTokens && (this.$input.data('edit') && !this.$input.is(document.activeElement) || this.options.createTokensOnBlur)) {
          this.createTokensFromInput(e)
      }

      this.preventDeactivation = false
      this.preventCreateTokens = false
  }

  , paste: function (e) {
      var _self = this

      // Add tokens to existing ones
      if (_self.options.allowPasting) {
          setTimeout(function () {
              _self.createTokensFromInput(e)
          }, 1)
      }
  }

  , change: function (e) {
      if (e.initiator === 'tokenfield') return // Prevent loops

      this.setTokens(this.$element.val())
  }

  , createTokensFromInput: function (e, focus) {
      if (this.$input.val().length < this.options.minLength)
          return // No input, simply return

      var tokensBefore = this.getTokensList()
      this.setTokens(this.$input.val(), true)

      if (tokensBefore == this.getTokensList() && this.$input.val().length)
          return false // No tokens were added, do nothing (prevent form submit)

      this.setInput('')

      if (this.$input.data('edit')) {
          this.unedit(focus)
      }

      return false // Prevent form being submitted
  }

  , next: function (add) {
      if (add) {
          var $firstActiveToken = this.$wrapper.find('.active:first')
          , deactivate = $firstActiveToken && this.$firstActiveToken ? $firstActiveToken.index() < this.$firstActiveToken.index() : false

          if (deactivate) return this.deactivate($firstActiveToken)
      }

      var $lastActiveToken = this.$wrapper.find('.active:last')
        , $nextToken = $lastActiveToken.nextAll('.token:first')

      if (!$nextToken.length) {
          this.$input.focus()
          return
      }

      this.activate($nextToken, add)
  }

  , prev: function (add) {

      if (add) {
          var $lastActiveToken = this.$wrapper.find('.active:last')
          , deactivate = $lastActiveToken && this.$firstActiveToken ? $lastActiveToken.index() > this.$firstActiveToken.index() : false

          if (deactivate) return this.deactivate($lastActiveToken)
      }

      var $firstActiveToken = this.$wrapper.find('.active:first')
        , $prevToken = $firstActiveToken.prevAll('.token:first')

      if (!$prevToken.length) {
          $prevToken = this.$wrapper.find('.token:first')
      }

      if (!$prevToken.length && !add) {
          this.$input.focus()
          return
      }

      this.activate($prevToken, add)
  }

  , activate: function ($token, add, multi, remember) {

      if (!$token) return

      if (typeof remember === 'undefined') var remember = true

      if (multi) var add = true

      this.$copyHelper.focus()

      if (!add) {
          this.$wrapper.find('.active').removeClass('active')
          if (remember) {
              this.$firstActiveToken = $token
          } else {
              delete this.$firstActiveToken
          }
      }

      if (multi && this.$firstActiveToken) {
          // Determine first active token and the current tokens indicies
          // Account for the 1 hidden textarea by subtracting 1 from both
          var i = this.$firstActiveToken.index() - 2
          , a = $token.index() - 2
          , _self = this

          this.$wrapper.find('.token').slice(Math.min(i, a) + 1, Math.max(i, a)).each(function () {
              _self.activate($(this), true)
          })
      }

      $token.addClass('active')
      this.$copyHelper.val(this.getTokensList(null, null, true)).select()
  }

  , activateAll: function () {
      var _self = this

      this.$wrapper.find('.token').each(function (i) {
          _self.activate($(this), i !== 0, false, false)
      })
  }

  , deactivate: function ($token) {
      if (!$token) return

      $token.removeClass('active')
      this.$copyHelper.val(this.getTokensList(null, null, true)).select()
  }

  , toggle: function ($token) {
      if (!$token) return

      $token.toggleClass('active')
      this.$copyHelper.val(this.getTokensList(null, null, true)).select()
  }

  , edit: function ($token) {
      if (!$token) return

      var attrs = $token.data('attrs')

      // Allow changing input value before editing
      var options = { attrs: attrs, relatedTarget: $token.get(0) }
      var editEvent = $.Event('tokenfield:edittoken', options)
      this.$element.trigger(editEvent)

      // Edit event can be cancelled if default is prevented
      if (editEvent.isDefaultPrevented()) return

      $token.find('.token-label').text(attrs.value)
      var tokenWidth = $token.outerWidth()

      var $_input = this.$input.hasClass('tt-input') ? this.$input.parent() : this.$input

      $token.replaceWith($_input)

      this.preventCreateTokens = true

      this.$input.val(attrs.value)
                .select()
                .data('edit', true)
                .width(tokenWidth)

      this.update();

      // Indicate that token is now being edited, and is replaced with an input field in the DOM
      this.$element.trigger($.Event('tokenfield:editedtoken', options))
  }

  , unedit: function (focus) {
      var $_input = this.$input.hasClass('tt-input') ? this.$input.parent() : this.$input
      $_input.appendTo(this.$wrapper)

      this.$input.data('edit', false)
      this.$mirror.text('')

      this.update()

      // Because moving the input element around in DOM
      // will cause it to lose focus, we provide an option
      // to re-focus the input after appending it to the wrapper
      if (focus) {
          var _self = this
          setTimeout(function () {
              _self.$input.focus()
          }, 1)
      }
  }

  , remove: function (e, direction) {
      if (this.$input.is(document.activeElement) || this._disabled || this._readonly) return

      var $token = (e.type === 'click') ? $(e.target).closest('.token') : this.$wrapper.find('.token.active')

      if (e.type !== 'click') {
          if (!direction) var direction = 'prev'
          this[direction]()

          // Was it the first token?
          if (direction === 'prev') var firstToken = $token.first().prevAll('.token:first').length === 0
      }

      // Prepare events and their options
      var options = { attrs: this.getTokenData($token), relatedTarget: $token.get(0) }
        , removeEvent = $.Event('tokenfield:removetoken', options)

      this.$element.trigger(removeEvent);

      // Remove event can be intercepted and cancelled
      if (removeEvent.isDefaultPrevented()) return

      var removedEvent = $.Event('tokenfield:removedtoken', options)
        , changeEvent = $.Event('change', { initiator: 'tokenfield' })

      // Remove token from DOM
      $token.remove()

      // Trigger events
      this.$element.val(this.getTokensList()).trigger(removedEvent).trigger(changeEvent)

      // Focus, when necessary:
      // When there are no more tokens, or if this was the first token
      // and it was removed with backspace or it was clicked on
      if (!this.$wrapper.find('.token').length || e.type === 'click' || firstToken) this.$input.focus()

      // Adjust input width
      this.$input.css('width', this.options.minWidth + 'px')
      this.update()

      // Cancel original event handlers
      e.preventDefault()
      e.stopPropagation()
  }

        /**
        * Update tokenfield dimensions
        */
  , update: function (e) {
      var value = this.$input.val()
        , inputPaddingLeft = parseInt(this.$input.css('padding-left'), 10)
        , inputPaddingRight = parseInt(this.$input.css('padding-right'), 10)
        , inputPadding = inputPaddingLeft + inputPaddingRight

      if (this.$input.data('edit')) {

          if (!value) {
              value = this.$input.prop("placeholder")
          }
          if (value === this.$mirror.text()) return

          this.$mirror.text(value)

          var mirrorWidth = this.$mirror.width() + 10;
          if (mirrorWidth > this.$wrapper.width()) {
              return this.$input.width(this.$wrapper.width())
          }

          this.$input.width(mirrorWidth)
      }
      else {
          //temporary reset width to minimal value to get proper results
          this.$input.width(this.options.minWidth);

          var w = (this.textDirection === 'rtl')
              ? this.$input.offset().left + this.$input.outerWidth() - this.$wrapper.offset().left - parseInt(this.$wrapper.css('padding-left'), 10) - inputPadding - 1
              : this.$wrapper.offset().left + this.$wrapper.width() + parseInt(this.$wrapper.css('padding-left'), 10) - this.$input.offset().left - inputPadding;
          //
          // some usecases pre-render widget before attaching to DOM,
          // dimensions returned by jquery will be NaN -> we default to 100%
          // so placeholder won't be cut off.
          isNaN(w) ? this.$input.width('100%') : this.$input.width(w);
      }
  }

  , focusInput: function (e) {
      if ($(e.target).closest('.token').length || $(e.target).closest('.token-input').length || $(e.target).closest('.tt-dropdown-menu').length) return
      // Focus only after the current call stack has cleared,
      // otherwise has no effect.
      // Reason: mousedown is too early - input will lose focus
      // after mousedown. However, since the input may be moved
      // in DOM, there may be no click or mouseup event triggered.
      var _self = this
      setTimeout(function () {
          _self.$input.focus()
      }, 0)
  }

  , search: function () {
      if (this.$input.data('ui-autocomplete')) {
          this.$input.autocomplete('search')
      }
  }

  , disable: function () {
      this.setProperty('disabled', true);
  }

  , enable: function () {
      this.setProperty('disabled', false);
  }

  , readonly: function () {
      this.setProperty('readonly', true);
  }

  , writeable: function () {
      this.setProperty('readonly', false);
  }

  , setProperty: function (property, value) {
      this['_' + property] = value;
      this.$input.prop(property, value);
      this.$element.prop(property, value);
      this.$wrapper[value ? 'addClass' : 'removeClass'](property);
  }

  , destroy: function () {
      // Set field value
      this.$element.val(this.getTokensList());
      // Restore styles and properties
      this.$element.css(this.$element.data('original-styles'));
      this.$element.prop('tabindex', this.$element.data('original-tabindex'));

      // Re-route tokenfield label to original input
      var $label = $('label[for="' + this.$input.prop('id') + '"]')
      if ($label.length) {
          $label.prop('for', this.$element.prop('id'))
      }

      // Move original element outside of tokenfield wrapper
      this.$element.insertBefore(this.$wrapper);

      // Remove tokenfield-related data
      this.$element.removeData('original-styles')
                   .removeData('original-tabindex')
                   .removeData('bs.tokenfield');

      // Remove tokenfield from DOM
      this.$wrapper.remove();
      this.$mirror.remove();

      var $_element = this.$element;

      return $_element;
  }

    }


    /* TOKENFIELD PLUGIN DEFINITION
    * ======================== */

    var old = $.fn.tokenfield

    $.fn.tokenfield = function (option, param) {
        var value
      , args = []

        Array.prototype.push.apply(args, arguments);

        var elements = this.each(function () {
            var $this = $(this)
        , data = $this.data('bs.tokenfield')
        , options = typeof option == 'object' && option

            if (typeof option === 'string' && data && data[option]) {
                args.shift()
                value = data[option].apply(data, args)
            } else {
                if (!data && typeof option !== 'string' && !param) {
                    $this.data('bs.tokenfield', (data = new Tokenfield(this, options)))
                    $this.trigger('tokenfield:initialize')
                }
            }
        })

        return typeof value !== 'undefined' ? value : elements;
    }

    $.fn.tokenfield.defaults = {
        minWidth: 60,
        minLength: 0,
        html: true,
        allowEditing: true,
        allowPasting: true,
        limit: 0,
        autocomplete: {},
        typeahead: {},
        showAutocompleteOnFocus: false,
        createTokensOnBlur: false,
        delimiter: ',',
        beautify: true,
        inputType: 'text'
    }

    $.fn.tokenfield.Constructor = Tokenfield


    /* TOKENFIELD NO CONFLICT
    * ================== */

    $.fn.tokenfield.noConflict = function () {
        $.fn.tokenfield = old
        return this
    }

    return Tokenfield;

}));