text: /*
  	jQuery tagEditor v1.0.20
      Copyright (c) 2014 Simon Steinberger / Pixabay
      GitHub: https://github.com/Pixabay/jQuery-tagEditor
  	License: http://www.opensource.org/licenses/mit-license.php
  */
  
  (function ($) {
      // auto grow input (stackoverflow.com/questions/931207)
      $.fn.tagEditorInput = function () { var t = " ", e = $(this), n = parseInt(e.css("fontSize")), i = $("<span/>").css({ position: "absolute", top: -9999, left: -9999, width: "auto", fontSize: e.css("fontSize"), fontFamily: e.css("fontFamily"), fontWeight: e.css("fontWeight"), letterSpacing: e.css("letterSpacing"), whiteSpace: "nowrap" }), s = function () { if (t !== (t = e.val())) { i.text(t); var s = i.width() + n; 20 > s && (s = 20), s != e.width() && e.width(s) } }; return i.insertAfter(e), e.bind("keyup keydown focus", s) };
  
      // plugin with val as parameter for public methods
      $.fn.tagEditor = function (options, val, blur) {
  
          // helper
          function escape(tag) {
              return tag.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
          }
  
          // build options dictionary with default values
          var blur_result, o = $.extend({}, $.fn.tagEditor.defaults, options), selector = this;
  
          // store regex and default delimiter in options for later use
          o.dregex = new RegExp('[' + o.delimiter.replace('-', '\-') + ']', 'g');
  
          // public methods
          if (typeof options == 'string') {
              // depending on selector, response may contain tag lists of multiple editor instances
              var response = [];
              selector.each(function () {
                  // the editor is the next sibling to the hidden, original field
                  var el = $(this), o = el.data('options'), ed = el.next('.tag-editor');
                  if (options == 'getTags')
                      response.push({ field: el[0], editor: ed, tags: ed.data('tags') });
                  else if (options == 'addTag') {
                      if (o.maxTags && ed.data('tags').length >= o.maxTags) return false;
                      // insert new tag
                      $('<li><div class="tag-editor-spacer">&nbsp;' + o.delimiter[0] + '</div><div class="tag-editor-tag"></div><div class="tag-editor-delete"><i></i></div></li>').appendTo(ed).find('.tag-editor-tag')
                          .html('<input type="text" maxlength="' + o.maxLength + '">').addClass('active').find('input').val(val).blur();
                      if (!blur) ed.click();
                      else $('.placeholder', ed).remove();
                  } else if (options == 'removeTag') {
                      // trigger delete on matching tag, then click editor to create a new tag
                      $('.tag-editor-tag', ed).filter(function () { return $(this).text() == val; }).closest('li').find('.tag-editor-delete').click();
                      if (!blur) ed.click();
                  } else if (options == 'destroy') {
                      el.removeClass('tag-editor-hidden-src').removeData('options').off('focus.tag-editor').next('.tag-editor').remove();
                  }
              });
              return options == 'getTags' ? response : this;
          }
  
          // delete selected tags on backspace, delete, ctrl+x
          if (window.getSelection) $(document).off('keydown.tag-editor').on('keydown.tag-editor', function (e) {
              if (e.which == 8 || e.which == 46 || e.ctrlKey && e.which == 88) {
                  try {
                      var sel = getSelection(), el = document.activeElement.tagName == 'BODY' ? $(sel.getRangeAt(0).startContainer.parentNode).closest('.tag-editor') : 0;
                  } catch (e) { el = 0; }
                  if (sel.rangeCount > 0 && el && el.length) {
                      var tags = [], splits = sel.toString().split(el.prev().data('options').dregex);
                      for (i = 0; i < splits.length; i++) { var tag = $.trim(splits[i]); if (tag) tags.push(tag); }
                      $('.tag-editor-tag', el).each(function () {
                          if (~$.inArray($(this).text(), tags)) $(this).closest('li').find('.tag-editor-delete').click();
                      });
                      return false;
                  }
              }
          });
  
          return selector.each(function () {
              var el = $(this), tag_list = []; // cache current tags
  
              // create editor (ed) instance
              var ed = $('<ul ' + (o.clickDelete ? 'oncontextmenu="return false;" ' : '') + 'class="tag-editor"></ul>').insertAfter(el);
              el.addClass('tag-editor-hidden-src') // hide original field
                  .data('options', o) // set data on hidden field
                  .on('focus.tag-editor', function () { ed.click(); }); // simulate tabindex
  
              // add dummy item for min-height on empty editor
              ed.append('<li style="width:1px">&nbsp;</li>');
  
              // markup for new tag
              var new_tag = '<li><div class="tag-editor-spacer">&nbsp;' + o.delimiter[0] + '</div><div class="tag-editor-tag"></div><div class="tag-editor-delete"><i></i></div></li>';
  
              // helper: update global data
              function set_placeholder() {
                  if (o.placeholder && !tag_list.length && !$('.deleted, .placeholder, input', ed).length)
                      ed.append('<li class="placeholder"><div>' + o.placeholder + '</div></li>');
              }
  
              // helper: update global data
              function update_globals(init) {
                  var old_tags = tag_list.toString();
                  tag_list = $('.tag-editor-tag:not(.deleted)', ed).map(function (i, e) {
                      var val = $.trim($(this).hasClass('active') ? $(this).find('input').val() : $(e).text());
                      if (val) return val;
                  }).get();
                  ed.data('tags', tag_list);
                  el.val(tag_list.join(o.delimiter[0]));
                  // change callback except for plugin init
                  if (!init) if (old_tags != tag_list.toString()) o.onChange(el, ed, tag_list);
                  set_placeholder();
              }
  
              ed.click(function (e, closest_tag) {
                  var d, dist = 99999, loc;
  
                  // do not create tag when user selects tags by text selection
                  if (window.getSelection && getSelection() != '') return;
  
                  if (o.maxTags && ed.data('tags').length >= o.maxTags) { ed.find('input').blur(); return false; }
  
                  blur_result = true
                  $('input:focus', ed).blur();
                  if (!blur_result) return false;
                  blur_result = true
  
                  // always remove placeholder on click
                  $('.placeholder', ed).remove();
                  if (closest_tag && closest_tag.length)
                      loc = 'before';
                  else {
                      // calculate tag closest to click position
                      $('.tag-editor-tag', ed).each(function(){
                          var tag = $(this), to = tag.offset(), tag_x = to.left, tag_y = to.top;
                          if (e.pageY >= tag_y && e.pageY <= tag_y+tag.height()) {
                              if (e.pageX < tag_x) loc = 'before', d = tag_x - e.pageX;
                              else loc = 'after', d = e.pageX - tag_x - tag.width();
                              if (d < dist) dist = d, closest_tag = tag;
                          }
                      });
                  }
  
                  if (loc == 'before') {
                      $(new_tag).insertBefore(closest_tag.closest('li')).find('.tag-editor-tag').click();
                  } else if (loc == 'after')
                      $(new_tag).insertAfter(closest_tag.closest('li')).find('.tag-editor-tag').click();
                  else // empty editor
                      $(new_tag).appendTo(ed).find('.tag-editor-tag').click();
                  return false;
              });
  
              ed.on('click', '.tag-editor-delete', function (e) {
                  // delete icon is hidden when input is visible; place cursor near invisible delete icon on click
                  if ($(this).prev().hasClass('active')) { $(this).closest('li').find('input').caret(-1); return false; }
  
                  var li = $(this).closest('li'), tag = li.find('.tag-editor-tag');
                  if (o.beforeTagDelete(el, ed, tag_list, tag.text()) === false) return false;
                  tag.addClass('deleted').animate({ width: 0 }, o.animateDelete, function () { li.remove(); set_placeholder(); });
                  update_globals();
                  return false;
              });
  
              // delete on right mouse click or ctrl+click
              if (o.clickDelete)
                  ed.on('mousedown', '.tag-editor-tag', function (e) {
                      if (e.ctrlKey || e.which > 1) {
                          var li = $(this).closest('li'), tag = li.find('.tag-editor-tag');
                          if (o.beforeTagDelete(el, ed, tag_list, tag.text()) === false) return false;
                          tag.addClass('deleted').animate({ width: 0 }, o.animateDelete, function () { li.remove(); set_placeholder(); });
                          update_globals();
                          return false;
                      }
                  });
  
              ed.on('click', '.tag-editor-tag', function (e) {
                  // delete on right click or ctrl+click -> exit
                  if (o.clickDelete && (e.ctrlKey || e.which > 1)) return false;
  
                  if (!$(this).hasClass('active')) {
                      var tag = $(this).text();
                      // guess cursor position in text input
                      var left_percent = Math.abs(($(this).offset().left - e.pageX) / $(this).width()), caret_pos = parseInt(tag.length * left_percent),
                          input = $(this).html('<input type="text" maxlength="' + o.maxLength + '" value="' + escape(tag) + '">').addClass('active').find('input');
                      input.data('old_tag', tag).tagEditorInput().focus().caret(caret_pos);
                      if (o.autocomplete) {
                          var aco = $.extend({}, o.autocomplete);
                          // extend user provided autocomplete select method
                          var ac_select = 'select' in aco ? o.autocomplete.select : '';
                          aco.select = function (e, ui) {
                              if (ac_select) ac_select(e, ui); setTimeout(function () {
                                  ed.trigger('click', [$('.active', ed).find('input').closest('li').next('li').find('.tag-editor-tag')]);
                              }, 20);
                          };
                          input.autocomplete(aco);
                      }
                  }
                  return false;
              });
  
              // helper: split into multiple tags, e.g. after paste
              function split_cleanup(input) {
                  var li = input.closest('li'), sub_tags = input.val().replace(/ +/, ' ').split(o.dregex),
                      old_tag = input.data('old_tag'), old_tags = tag_list.slice(0), exceeded = false, cb_val; // copy tag_list
                  for (var i = 0; i < sub_tags.length; i++) {
                      tag = $.trim(sub_tags[i]).slice(0, o.maxLength);
                      if (o.forceLowercase) tag = tag.toLowerCase();
                      cb_val = o.beforeTagSave(el, ed, old_tags, old_tag, tag);
                      tag = cb_val || tag;
                      if (cb_val === false || !tag) continue;
                      // remove duplicates
                      if (o.removeDuplicates && ~$.inArray(tag, old_tags))
                          $('.tag-editor-tag', ed).each(function () { if ($(this).text() == tag) $(this).closest('li').remove(); });
                      old_tags.push(tag);
                      li.before('<li><div class="tag-editor-spacer">&nbsp;' + o.delimiter[0] + '</div><div class="tag-editor-tag">' + escape(tag) + '</div><div class="tag-editor-delete"><i></i></div></li>');
                      if (o.maxTags && old_tags.length >= o.maxTags) { exceeded = true; break; }
                  }
                  input.attr('maxlength', o.maxLength).removeData('old_tag').val('')
                  if (exceeded) input.blur(); else input.focus();
                  update_globals();
              }
  
              ed.on('blur', 'input', function (e) {
                  e.stopPropagation();
                  var input = $(this), old_tag = input.data('old_tag'), tag = $.trim(input.val().replace(/ +/, ' ').replace(o.dregex, o.delimiter[0]));
                  if (!tag) {
                      if (old_tag && o.beforeTagDelete(el, ed, tag_list, old_tag) === false) {
                          input.val(old_tag).focus();
                          blur_result = false;
                          update_globals();
                          return;
                      }
                      try { input.closest('li').remove(); } catch (e) { }
                      if (old_tag) update_globals();
                  }
                  else if (tag.indexOf(o.delimiter[0]) >= 0) { split_cleanup(input); return; }
                  else if (tag != old_tag) {
                      if (o.forceLowercase) tag = tag.toLowerCase();
                      cb_val = o.beforeTagSave(el, ed, tag_list, old_tag, tag);
                      tag = cb_val || tag;
                      if (cb_val === false) {
                          if (old_tag) {
                              input.val(old_tag).focus();
                              blur_result = false;
                              update_globals();
                              return;
                          }
                          try { input.closest('li').remove(); } catch (e) { }
                          if (old_tag) update_globals();
                      }
                          // remove duplicates
                      else if (o.removeDuplicates)
                          $('.tag-editor-tag:not(.active)', ed).each(function () { if ($(this).text() == tag) $(this).closest('li').remove(); });
                  }
                  input.parent().html(escape(tag)).removeClass('active');
                  if (tag != old_tag) update_globals();
                  set_placeholder();
              });
  
              var pasted_content;
              ed.on('paste', 'input', function (e) {
                  $(this).removeAttr('maxlength');
                  pasted_content = $(this);
                  setTimeout(function () { split_cleanup(pasted_content); }, 30);
              });
  
              // keypress delimiter
              var inp;
              ed.on('keypress', 'input', function (e) {
                  if (o.delimiter.indexOf(String.fromCharCode(e.which)) >= 0) {
                      inp = $(this);
                      setTimeout(function () { split_cleanup(inp); }, 20);
                  }
              });
  
              ed.on('keydown', 'input', function (e) {
                  var $t = $(this);
  
                  // left/up key + backspace key on empty field
                  if ((e.which == 37 || !o.autocomplete && e.which == 38) && !$t.caret() || e.which == 8 && !$t.val()) {
                      var prev_tag = $t.closest('li').prev('li').find('.tag-editor-tag');
                      if (prev_tag.length) prev_tag.click().find('input').caret(-1);
                      else if ($t.val() && !(o.maxTags && ed.data('tags').length >= o.maxTags)) $(new_tag).insertBefore($t.closest('li')).find('.tag-editor-tag').click();
                      return false;
                  }
                      // right/down key
                  else if ((e.which == 39 || !o.autocomplete && e.which == 40) && ($t.caret() == $t.val().length)) {
                      var next_tag = $t.closest('li').next('li').find('.tag-editor-tag');
                      if (next_tag.length) next_tag.click().find('input').caret(0);
                      else if ($t.val()) ed.click();
                      return false;
                  }
                      // tab key
                  else if (e.which == 9) {
                      // shift+tab
                      if (e.shiftKey) {
                          var prev_tag = $t.closest('li').prev('li').find('.tag-editor-tag');
                          if (prev_tag.length) prev_tag.click().find('input').caret(0);
                          else if ($t.val() && !(o.maxTags && ed.data('tags').length >= o.maxTags)) $(new_tag).insertBefore($t.closest('li')).find('.tag-editor-tag').click();
                              // allow tabbing to previous element
                          else {
                              el.attr('disabled', 'disabled');
                              setTimeout(function () { el.removeAttr('disabled'); }, 30);
                              return;
                          }
                          return false;
                          // tab
                      } else {
                          var next_tag = $t.closest('li').next('li').find('.tag-editor-tag');
                          if (next_tag.length) next_tag.click().find('input').caret(0);
                          else if ($t.val()) ed.click();
                          else return; // allow tabbing to next element
                          return false;
                      }
                  }
                      // del key
                  else if (e.which == 46 && (!$.trim($t.val()) || ($t.caret() == $t.val().length))) {
                      var next_tag = $t.closest('li').next('li').find('.tag-editor-tag');
                      if (next_tag.length) next_tag.click().find('input').caret(0);
                      else if ($t.val()) ed.click();
                      return false;
                  }
                      // enter key
                  else if (e.which == 13) {
                      ed.trigger('click', [$t.closest('li').next('li').find('.tag-editor-tag')]);
  
                      // trigger blur if maxTags limit is reached
                      if (o.maxTags && ed.data('tags').length >= o.maxTags) ed.find('input').blur();
  
                      return false;
                  }
                      // pos1
                  else if (e.which == 36 && !$t.caret()) ed.find('.tag-editor-tag').first().click();
                      // end
                  else if (e.which == 35 && $t.caret() == $t.val().length) ed.find('.tag-editor-tag').last().click();
                      // esc
                  else if (e.which == 27) {
                      $t.val($t.data('old_tag') ? $t.data('old_tag') : '').blur();
                      return false;
                  }
              });
  
              // create initial tags
              var tags = o.initialTags.length ? o.initialTags : el.val().split(o.dregex);
              for (var i = 0; i < tags.length; i++) {
                  if (o.maxTags && i >= o.maxTags) break;
                  var tag = $.trim(tags[i].replace(/ +/, ' '));
                  if (tag) {
                      if (o.forceLowercase) tag = tag.toLowerCase();
                      tag_list.push(tag);
                      ed.append('<li><div class="tag-editor-spacer">&nbsp;' + o.delimiter[0] + '</div><div class="tag-editor-tag">' + escape(tag) + '</div><div class="tag-editor-delete"><i></i></div></li>');
                  }
              }
              update_globals(true); // true -> no onChange callback
  
              // init sortable
              if (o.sortable && $.fn.sortable) ed.sortable({
                  distance: 5, cancel: '.tag-editor-spacer, input', helper: 'clone',
                  update: function () { update_globals(); }
              });
          });
      };
  
      $.fn.tagEditor.defaults = {
          initialTags: [],
          maxTags: 0,
          maxLength: 50,
          delimiter: ',;',
          placeholder: '',
          forceLowercase: true,
          removeDuplicates: true,
          clickDelete: false,
          animateDelete: 175,
          sortable: true, // jQuery UI sortable
          autocomplete: null, // options dict for jQuery UI autocomplete
  
          // callbacks
          onChange: function () { },
          beforeTagSave: function () { },
          beforeTagDelete: function () { }
      };
  }(jQuery));
  