UNPKG

tokenfield

Version:

Input field with tagging/token/chip capabilities written in raw JavaScript

1,458 lines (1,290 loc) 76.1 kB
var Tokenfield = /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports, __webpack_require__) { module.exports = __webpack_require__(1).default; /***/ }), /* 1 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _events = __webpack_require__(2); var _events2 = _interopRequireDefault(_events); var _ajax = __webpack_require__(3); var _ajax2 = _interopRequireDefault(_ajax); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** * Input field with tagging/token/chip capabilities written in raw JavaScript * tokenfield 1.5.2 <https://github.com/KaneCohen/tokenfield> * Copyright 2022 Kane Cohen <https://github.com/KaneCohen> * Available under BSD-3-Clause license */ var _tokenfields = {}; var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; var reHasRegExpChar = RegExp(reRegExpChar.source); var _factory = document.createElement('div'); var _templates = { containerTokenfield: '<div class="tokenfield tokenfield-mode-tokens">\n <input class="tokenfield-copy-helper"\n style="display:none;position:fixed;top:-1000px;right:1000px;"\n tabindex="-1"\n type="text"\n />\n <div class="tokenfield-set">\n <ul></ul>\n </div>\n <input class="tokenfield-input" />\n <div class="tokenfield-suggest">\n <ul class="tokenfield-suggest-list"></ul>\n </div>\n </div>', containerList: '<div class="tokenfield tokenfield-mode-list">\n <input class="tokenfield-input" />\n <div class="tokenfield-suggest">\n <ul class="tokenfield-suggest-list"></ul>\n </div>\n <div class="tokenfield-set">\n <ul></ul>\n </div>\n </div>', suggestItem: '<li class="tokenfield-suggest-item"></li>', setItem: '<li class="tokenfield-set-item">\n <span class="item-label"></span>\n <a href="#" class="item-remove" tabindex="-1">\xD7</a>\n <input class="item-input" type="hidden" />\n </li>' }; function guid() { return ((1 + Math.random()) * 0x10000 | 0).toString(16) + ((1 + Math.random()) * 0x10000 | 0).toString(16); } function includes(arr, item) { return arr.indexOf(item) >= 0; } function getPath(node) { var nodes = [node]; while (node.parentNode) { node = node.parentNode; nodes.push(node); } return nodes; } function findElement(input) { if (input.nodeName) { return input; } else if (typeof input === 'string') { return document.querySelector(input); } return null; } function build(html, all) { if (html.nodeName) return html; html = html.replace(/(\t|\n$)/g, ''); _factory.innerHTML = ''; _factory.innerHTML = html; if (all === true) { return _factory.childNodes; } else { return _factory.childNodes[0]; } } function toString(value) { if (typeof value == 'string') { return value; } if (value === null) { return ''; } var result = value + ''; return result === '0' && 1 / value === -Infinity ? '-0' : result; } function keyToChar(e) { if (e.key || e.keyIdentifier) { return e.key || String.fromCharCode(parseInt(e.keyIdentifier.substr(2), 16)); } return null; } function escapeRegex(string) { string = toString(string); return string && reHasRegExpChar.test(string) ? string.replace(reRegExpChar, '\\$&') : string; } function makeDefaultsAndOptions() { var _defaults = { focusedItem: null, cache: {}, timer: null, xhr: null, suggested: false, suggestedItems: [], setItems: [], events: {}, delimiters: {} }; var _options = { el: null, form: true, // Listens to reset event on the specifiedform. If set to true listens to // immediate parent form. Also accepts selectors or elements. mode: 'tokenfield', // Display mode: tokenfield or list. addItemOnBlur: false, // Add token if input field loses focus. addItemsOnPaste: false, // Add tokens using `delimiters` option below to tokenize given string. keepItemsOrder: true, // Items and New Items values will be set in input with a specific position // in the list so that you can retreive correct position on the backend. setItems: [], // List of set items. items: [], // List of available items to work with. // Example: [{id: 143, value: 'Hello World'}, {id: 144, value: 'Foo Bar'}]. newItems: true, // Allow input (on delimiter key) of new items. multiple: true, // Accept multiple items. maxItems: 0, // Set maximum allowed number of items. minLength: 0, // Minimum length of the string to be converted into token. keys: { // Various action keys. 17: 'ctrl', 16: 'shift', 91: 'meta', 8: 'delete', // Backspace 27: 'esc', 37: 'left', 38: 'up', 39: 'right', 40: 'down', 46: 'delete', 65: 'select', // A 67: 'copy', // C 88: 'cut', // X 9: 'delimiter', // Tab 13: 'delimiter', // Enter 108: 'delimiter' // Numpad Enter }, matchRegex: '{value}', // Match regex where {value} would be replaced by ecaped user input. matchFlags: 'i', // Flags used in regex matching. matchStart: false, // Set match regex to test from the beginning of the string. matchEnd: false, // Set match regex to test from the end of the string. delimiters: [], // Array of strings which act as delimiters during tokenization. copyProperty: 'name', // Property of the token used for copy event. copyDelimiter: ', ', // Delimiter used to populate clipboard with selected tokens. remote: { type: 'GET', // Ajax request type. url: null, // Full server url. queryParam: 'q', // What param to use when asking server for data. delay: 300, // Dealy between last keydown event and ajax request for data. timestampParam: 't', params: {}, headers: {} }, placeholder: null, // Hardcoded placeholder text. If not set, will use placeholder from the element itself. inputType: 'text', // HTML attribute for the input element which lets mobile browsers use various input modes. // Accepts text, email, url, and others. minChars: 2, // Number of characters before we start to look for similar items. maxSuggest: 10, // Max items in the suggest box. maxSuggestWindow: 10, // Limit height of the suggest box after given number of items. filterSetItems: true, // Filters already set items from the suggestions list. filterMatchCase: false, // Sets case-sensitivity for checking whether new item is already set in the list. singleInput: false, // Pushes all token values into a single. Accepts: true, 'selector', or an element. // When set to true - would use tokenfield target element as an input to fill. singleInputValue: 'id', // Which property of the item to use when using fillInput. singleInputDelimiter: ', ', itemLabel: 'name', // Property to use in order to render item label. itemName: 'items', // If set, for each tag/token there will be added // input field with array property name: // name="items[]". newItemName: 'items_new', // Suffix that will be added to the new tag in // case it was not available from the server: // name="items_new[]". itemValue: 'id', // Value that will be taken out of the results and inserted into itemAttr. newItemValue: 'name', // Value that will be taken out of the results and inserted into itemAttr. itemData: 'name', // Which property to search for. validateNewItem: null // Run a function to test if new item is valid and can be added. }; return { _defaults: _defaults, _options: _options }; } var Tokenfield = function (_EventEmitter) { _inherits(Tokenfield, _EventEmitter); function Tokenfield() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, Tokenfield); var _this = _possibleConstructorReturn(this, (Tokenfield.__proto__ || Object.getPrototypeOf(Tokenfield)).call(this)); var _makeDefaultsAndOptio = makeDefaultsAndOptions(), _defaults = _makeDefaultsAndOptio._defaults, _options = _makeDefaultsAndOptio._options; _this.id = guid(); _this.key = 'key_' + _this.id; _this._vars = _extends({}, _defaults); _this._options = _extends({}, _options, options); _this._options.keys = _extends({}, _options.keys, options.keys); _this._options.remote = _extends({}, _options.remote, options.remote); _this._templates = _extends({}, _templates, options.templates); _this._vars.setItems = _this._prepareData(_this.remapData(_this._options.setItems || [])); _this._focused = false; _this._input = null; _this._form = false; _this._html = {}; var o = _this._options; // Make a hash map to simplify filtering later. o.delimiters.forEach(function (delimiter) { _this._vars.delimiters[delimiter] = true; }); var el = findElement(o.el); if (el) { _this.el = el; } else { throw new Error('Selector: DOM Element ' + o.el + ' not found.'); } if (o.singleInput) { var _el = findElement(o.singleInput); if (_el) { _this._input = _el; } else { _this._input = _this.el; } } _this.el.tokenfield = _this; if (o.placeholder === null) { o.placeholder = o.el.placeholder || ''; } if (o.form) { var form = false; if (o.form.nodeName) { form = o.form; } else if (o.form === true) { var node = _this.el; while (node.parentNode) { if (node.nodeName === 'FORM') { form = node; break; } node = node.parentNode; } } else if (typeof form == 'string') { form = document.querySelector(form); if (!form) { throw new Error('Selector: DOM Element ' + o.form + ' not found.'); } } _this._form = form; } else { throw new Error('Cannot create tokenfield without DOM Element.'); } _tokenfields[_this.id] = _this; _this._render(); return _this; } _createClass(Tokenfield, [{ key: '_render', value: function _render() { var o = this._options; var html = this._html; if (o.mode === 'tokenfield') { html.container = build(this._templates.containerTokenfield); } else { html.container = build(this._templates.containerList); } html.suggest = html.container.querySelector('.tokenfield-suggest'); html.suggestList = html.container.querySelector('.tokenfield-suggest-list'); html.items = html.container.querySelector('.tokenfield-set > ul'); html.input = html.container.querySelector('.tokenfield-input'); html.input.setAttribute('type', o.inputType); if (o.mode === 'tokenfield') { html.input.placeholder = this._vars.setItems.length ? '' : o.placeholder; } else { html.input.placeholder = o.placeholder; } html.copyHelper = html.container.querySelector('.tokenfield-copy-helper'); o.el.style.display = 'none'; html.suggest.style.display = 'none'; this._renderSizer(); // Set tokenfield in DOM. html.container.tokenfield = this; o.el.parentElement.insertBefore(html.container, o.el); html.container.insertBefore(o.el, html.container.firstChild); this._setEvents(); this._renderItems(); if (o.mode === 'tokenfield') { this._minimizeInput()._resizeInput(); } return this; } }, { key: '_renderSizer', value: function _renderSizer() { var html = this._html; var b = this._getBounds(); var style = window.getComputedStyle(html.container); var compensate = parseInt(style.paddingLeft, 10) + parseInt(style.paddingRight, 10); var styles = { width: 'auto', height: 'auto', overflow: 'hidden', whiteSpace: 'pre', maxWidth: b.width - compensate + 'px', position: 'fixed', top: -10000 + 'px', left: 10000 + 'px', fontSize: style.fontSize, paddingLeft: style.paddingLeft, paddingRight: style.paddingRight }; html.sizer = document.createElement('div'); html.sizer.id = 'tokenfield-sizer-' + this.id; for (var key in styles) { html.sizer.style[key] = styles[key]; } html.container.appendChild(html.sizer); } }, { key: '_minimizeInput', value: function _minimizeInput() { if (this._options.mode === 'tokenfield') { this._html.input.style.width = '20px'; } return this; } }, { key: '_refreshInput', value: function _refreshInput() { var empty = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; var v = this._vars; var html = this._html; if (empty) html.input.value = ''; if (this._options.mode === 'tokenfield') { this._resizeInput(); var placeholder = v.setItems.length ? '' : this._options.placeholder; html.input.setAttribute('placeholder', placeholder); } else if (this._options.mode === 'list') { html.input.setAttribute('placeholder', this._options.placeholder); } return this; } }, { key: '_resizeInput', value: function _resizeInput() { var val = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var html = this._html; var b = this._getBounds(); var style = window.getComputedStyle(html.container); var compensate = parseInt(style.paddingRight, 10) + parseInt(style.borderRightWidth, 10); var fullCompensate = compensate + parseInt(style.paddingLeft, 10) + parseInt(style.borderLeftWidth, 10); html.sizer.innerHTML = val; html.sizer.style.maxWidth = b.width - compensate + 'px'; if (b.width === 0) { html.input.style.width = '100%'; return; } else { html.input.style.width = '20px'; } var sb = html.sizer.getBoundingClientRect(); var ib = html.input.getBoundingClientRect(); var rw = b.width - (ib.left - b.left) - compensate; if (sb.width > rw) { html.input.style.width = b.width - fullCompensate - 1 + 'px'; } else { html.input.style.width = rw - 1 + 'px'; } } }, { key: '_fetchData', value: function _fetchData(val) { var _this2 = this; var v = this._vars; var o = this._options; var r = o.remote; var reqData = _extends({}, o.params); for (var key in r.params) { reqData[key] = r.params[key]; } if (r.limit) { reqData[r.limit] = o.remote.limit; } reqData[r.queryParam] = val; reqData[r.timestampParam] = Math.round(new Date().getTime() / 1000); v.xhr = (0, _ajax2.default)(reqData, o.remote, function () { if (v.xhr && v.xhr.readyState == 4) { if (v.xhr.status == 200) { var response = JSON.parse(v.xhr.responseText); v.cache[val] = response; var data = _this2._prepareData(_this2.remapData(response)); var items = _this2._filterData(val, data); v.suggestedItems = o.filterSetItems ? _this2._filterSetItems(items) : items; _this2.showSuggestions(); } else if (v.xhr.status > 0) { throw new Error('Error while loading remote data.'); } _this2._abortXhr(); } }); } // Overwriteable method where you can change given data to appropriate format. }, { key: 'remapData', value: function remapData(data) { return data; } }, { key: '_prepareData', value: function _prepareData(data) { var _this3 = this; return data.map(function (item) { return _extends({}, item, _defineProperty({}, _this3.key, guid())); }); } }, { key: '_filterData', value: function _filterData(val, data) { var o = this._options; var regex = o.matchRegex.replace('{value}', escapeRegex(val)); if (o.matchStart) { regex = '^' + regex; } else if (o.matchEnd) { regex = regex + '$'; } var pattern = new RegExp(regex, o.matchFlags); return data.filter(function (item) { return pattern.test(item[o.itemData]); }); } }, { key: '_abortXhr', value: function _abortXhr() { var v = this._vars; if (v.xhr !== null) { v.xhr.abort(); v.xhr = null; } } }, { key: '_filterSetItems', value: function _filterSetItems(items) { var key = this._options.itemValue; var v = this._vars; if (!v.setItems.length) return items; var setKeys = v.setItems.map(function (item) { return item[key]; }); return items.filter(function (item) { if (setKeys.indexOf(item[key]) === -1) { return true; } return false; }); } }, { key: '_setEvents', value: function _setEvents() { var v = this._vars; var html = this._html; v.events.onClick = this._onClick.bind(this); v.events.onMouseDown = this._onMouseDown.bind(this); v.events.onMouseOver = this._onMouseOver.bind(this); v.events.onFocus = this._onFocus.bind(this); v.events.onResize = this._onResize.bind(this); v.events.onReset = this._onReset.bind(this); v.events.onKeyDown = this._onKeyDown.bind(this); v.events.onFocusOut = this._onFocusOut.bind(this); html.container.addEventListener('click', v.events.onClick); // Attach document event only once. if (Object.keys(_tokenfields).length === 1) { document.addEventListener('mousedown', v.events.onMouseDown); window.addEventListener('resize', v.events.onResize); } if (this._form && this._form.nodeName) { this._form.addEventListener('reset', v.events.onReset); } html.suggestList.addEventListener('mouseover', v.events.onMouseOver); html.input.addEventListener('focus', v.events.onFocus); } }, { key: '_onMouseOver', value: function _onMouseOver(e) { var target = e.target; if (target.classList.contains('tokenfield-suggest-item')) { var selected = [].slice.call(this._html.suggestList.querySelectorAll('.selected')); selected.forEach(function (item) { if (item !== target) item.classList.remove('selected'); }); target.classList.add('selected'); this._selectItem(target.key, false); this._refreshItemsSelection(); } } }, { key: '_onReset', value: function _onReset() { this.setItems(this._options.setItems); } }, { key: '_onFocus', value: function _onFocus() { var v = this._vars; var html = this._html; var o = this._options; html.input.removeEventListener('keydown', v.events.onKeyDown); html.input.addEventListener('keydown', v.events.onKeyDown); html.input.addEventListener('focusout', v.events.onFocusOut); if (o.addItemsOnPaste) { v.events.onPaste = this._onPaste.bind(this); html.input.addEventListener('paste', v.events.onPaste); } this._focused = true; this._html.container.classList.add('focused'); if (o.mode === 'tokenfield') { this._resizeInput(); } if (html.input.value.trim().length >= o.minChars) { this.showSuggestions(); } } }, { key: '_onFocusOut', value: function _onFocusOut(e) { var v = this._vars; var o = this._options; var html = this._html; html.input.removeEventListener('keydown', v.events.onKeyDown); html.input.removeEventListener('focusout', v.events.onFocusOut); if (typeof v.events.onPaste !== 'undefined') { html.input.removeEventListener('paste', v.events.onPaste); } if (e.relatedTarget && e.relatedTarget === html.copyHelper) { return; } var canAddItem = o.multiple && !o.maxItems || !o.multiple && !v.setItems.length || o.multiple && o.maxItems && v.setItems.length < o.maxItems; if (this._focused && o.addItemOnBlur && canAddItem && this._newItem(html.input.value)) { this._renderItems()._refreshInput(); } else { this._defocusItems()._renderItems(); } this._focused = false; this._html.container.classList.remove('focused'); } }, { key: '_onMouseDown', value: function _onMouseDown(e) { var tokenfield = null; for (var key in _tokenfields) { if (_tokenfields[key]._html.container.contains(e.target)) { tokenfield = _tokenfields[key]; break; } } if (tokenfield) { for (var _key in _tokenfields) { if (_key !== tokenfield.id) { _tokenfields[_key].hideSuggestions(); _tokenfields[_key].blur(); } } // Prevent input blur. if (e.target !== tokenfield._html.input) { e.preventDefault(); } } else { for (var _key2 in _tokenfields) { _tokenfields[_key2].hideSuggestions(); _tokenfields[_key2].blur(); } } } }, { key: '_onResize', value: function _onResize() { for (var key in _tokenfields) { if (_tokenfields[key]._options.mode === 'tokenfield') { _tokenfields[key]._resizeInput(_tokenfields[key]._html.input.value); } } } }, { key: '_onPaste', value: function _onPaste(e) { var _this4 = this; var v = this._vars; var o = this._options; var val = e.clipboardData.getData('text'); var tokens = [val]; // Break input using delimiters option. if (o.delimiters.length) { var search = o.delimiters.join('|'); var splitRegex = new RegExp('(' + search + ')', 'ig'); tokens = val.split(splitRegex); } var items = tokens.map(function (token) { return token.trim(); }).filter(function (token) { return token.length > 0 && token.length >= o.minLength && typeof v.delimiters[token] === 'undefined'; }).map(function (token) { return _this4._newItem(token); }); if (items.length) { setTimeout(function () { _this4._renderItems()._refreshInput()._deselectItems().hideSuggestions(); }, 1); e.preventDefault(); } } }, { key: '_onKeyDown', value: function _onKeyDown(e) { var _this5 = this; var v = this._vars; var o = this._options; var html = this._html; if (o.maxItems && v.setItems.length >= o.maxItems) { e.preventDefault(); } if (o.mode === 'tokenfield') { setTimeout(function () { _this5._resizeInput(html.input.value); }, 1); } var key = keyToChar(e); if (typeof o.keys[e.keyCode] !== 'undefined' || includes(o.delimiters, key)) { if (this._keyAction(e)) return true; } else { this._defocusItems()._refreshItems(); } clearTimeout(v.timer); this._abortXhr(); if (!o.maxItems || v.setItems.length < o.maxItems) { setTimeout(function () { _this5._keyInput(e); }, 1); } } }, { key: '_keyAction', value: function _keyAction(e) { var _this6 = this; var key = this.key; var item = null; var v = this._vars; var o = this._options; var html = this._html; var keyName = o.keys[e.keyCode]; var val = html.input.value.trim(); var keyChar = keyToChar(e); if (includes(o.delimiters, keyChar) && typeof keyName === 'undefined') { keyName = 'delimiter'; } var selected = this._getSelectedItems(); if (selected.length) { item = selected[0]; } switch (keyName) { case 'esc': this._deselectItems()._defocusItems()._renderItems().hideSuggestions(); break; case 'up': if (this._vars.suggested) { this._selectPrevItem()._refreshItemsSelection(); e.preventDefault(); } this._defocusItems()._renderItems(); break; case 'down': if (this._vars.suggested) { this._selectNextItem()._refreshItemsSelection(); e.preventDefault(); } this._defocusItems()._renderItems(); break; case 'left': if (this.getFocusedItems().length || !html.input.selectionStart && !html.input.selectionEnd) { this._focusPrevItem(e.shiftKey); e.preventDefault(); } break; case 'right': if (this.getFocusedItems().length || html.input.selectionStart === val.length) { this._focusNextItem(e.shiftKey); e.preventDefault(); } break; case 'delimiter': this._abortXhr(); this._defocusItems(); if (!o.multiple && v.setItems.length >= 1) { return false; } val = this.onInput(val, e); if (item) { this._addItem(item); } else if (val.length) { item = this._newItem(val); } if (item) { this._minimizeInput()._renderItems().focus()._refreshInput()._refreshSuggestions()._deselectItems(); } e.preventDefault(); break; case 'select': if (!val.length && (e.ctrlKey || e.metaKey)) { this._vars.setItems.forEach(function (item) { item.focused = true; }); this._refreshItems(); } else { return false; } break; case 'cut': { var focusedItems = this.getFocusedItems(); if (focusedItems.length && (e.ctrlKey || e.metaKey)) { this._copy()._delete(e); } else { return false; } break; } case 'copy': { var _focusedItems = this.getFocusedItems(); if (_focusedItems.length && (e.ctrlKey || e.metaKey)) { this._copy(); } else { return false; } break; } case 'delete': { this._abortXhr(); var _focusedItems2 = this.getFocusedItems(); if (!html.input.selectionEnd && e.keyCode === 8 || html.input.selectionStart === val.length && e.keyCode === 46 || _focusedItems2.length) { this._delete(e); } else { v.timer = setTimeout(function () { _this6._keyInput(e); }, o.delay); } break; } default: break; } return true; } }, { key: '_copy', value: function _copy() { var o = this._options; var html = this._html; var copyString = this.getFocusedItems().map(function (item) { return item[o.copyProperty]; }).join(o.copyDelimiter); html.copyHelper.style.display = 'block'; html.copyHelper.value = copyString; html.copyHelper.focus(); html.copyHelper.select(); document.execCommand('copy'); html.copyHelper.style.display = 'none'; html.copyHelper.value = ''; html.input.focus(); return this; } }, { key: '_delete', value: function _delete(e) { var _this7 = this; var v = this._vars; var o = this._options; var key = this.key; var html = this._html; var focusedItems = this.getFocusedItems(); if (o.mode === 'tokenfield' && v.setItems.length) { if (focusedItems.length) { focusedItems.forEach(function (item) { _this7._removeItem(item[key]); }); this._refreshSuggestions()._keyInput(e); } else if (!html.input.selectionStart) { this._focusItem(v.setItems[v.setItems.length - 1][key]); } } else if (focusedItems.length) { focusedItems.forEach(function (item) { _this7._removeItem(item[key]); }); this._refreshSuggestions()._keyInput(e); } this._minimizeInput()._renderItems()._refreshInput(false); return this; } }, { key: '_keyInput', value: function _keyInput(e) { var _this8 = this; var v = this._vars; var o = this._options; var html = this._html; this._defocusItems()._refreshItems(); var val = this.onInput(html.input.value.trim(), e); if (e.type === 'keydown') { this.emit('input', this, val, e); } if (val.length < o.minChars) { this.hideSuggestions(); return false; } if (!o.multiple && v.setItems.length >= 1) { return false; } // Check if we have cache with this val. if (typeof v.cache[val] === 'undefined') { // Get new data. if (o.remote.url) { v.timer = setTimeout(function () { _this8._fetchData(val); }, o.delay); } else if (!o.remote.url && o.items.length) { var data = this._prepareData(o.items); var items = this._filterData(val, data); v.suggestedItems = o.filterSetItems ? this._filterSetItems(items) : items; this.showSuggestions(); } } else { // Work with cached data. var _data = this._prepareData(this.remapData(v.cache[val])); var _items = this._filterData(val, _data); v.suggestedItems = o.filterSetItems ? this._filterSetItems(_items) : _items; this.showSuggestions(); } return this; } }, { key: '_onClick', value: function _onClick(e) { var target = e.target; if (target.classList.contains('item-remove')) { e.preventDefault(); this._removeItem(target.key)._defocusItems()._minimizeInput()._renderItems()._refreshInput(false)._keyInput(e); } else if (target.classList.contains('tokenfield-suggest-item')) { var item = this._getSuggestedItem(target.key); this._addItem(item)._minimizeInput()._renderItems()._refreshInput()._refreshSuggestions(); } else { var setItem = getPath(target).filter(function (node) { return node.classList && node.classList.contains('tokenfield-set-item'); })[0]; if (setItem) { this._focusItem(setItem.key, e.shiftKey, e.ctrlKey || e.metaKey, true); this._refreshItems(); } else { this._keyInput(e); } } this.focus(); } }, { key: '_selectPrevItem', value: function _selectPrevItem() { var key = this.key; var o = this._options; var items = this._vars.suggestedItems; var index = this._getSelectedItemIndex(); if (!items.length) { return this; } if (index !== null) { if (index === 0) { if (o.newItems) { this._deselectItems(); } else { this._selectItem(items[items.length - 1][key]); } } else { this._selectItem(items[index - 1][key]); } } else { this._selectItem(items[items.length - 1][key]); } return this; } }, { key: '_selectNextItem', value: function _selectNextItem() { var key = this.key; var o = this._options; var items = this._vars.suggestedItems; var index = this._getSelectedItemIndex(); if (!items.length) { return this; } if (index !== null) { if (index === items.length - 1) { if (o.newItems) { this._deselectItems(); } else { this._selectItem(items[0][key]); } } else { this._selectItem(items[index + 1][key]); } } else { this._selectItem(items[0][key]); } return this; } }, { key: '_focusPrevItem', value: function _focusPrevItem() { var multiple = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var key = this.key; var items = this._vars.setItems; var index = this._getFocusedItemIndex(); if (!items.length) { return this; } if (index !== null) { if (index === 0 && !multiple) { this._defocusItems(); } else if (index === 0 && multiple) { var lastFocused = this._getFocusedItemIndex(true); this._defocusItem(items[lastFocused][key]); } else { this._focusItem(items[index - 1][key], multiple, false, true); } } else { this._focusItem(items[items.length - 1][key], false, false, true); } this._refreshItems(); return this; } }, { key: '_focusNextItem', value: function _focusNextItem() { var multiple = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var key = this.key; var items = this._vars.setItems; var index = this._getFocusedItemIndex(true); if (!items.length) { return this; } if (index !== null) { if (index === items.length - 1 && !multiple) { this._defocusItems(); } else if (index === items.length - 1 && multiple) { this._focusItem(items[0][key], multiple); } else { this._focusItem(items[index + 1][key], multiple); } } else { this._focusItem(items[0][key], false); } this._refreshItems(); return this; } }, { key: '_getSelectedItems', value: function _getSelectedItems() { var key = this.key; var setIds = this._vars.setItems.map(function (item) { return item[key]; }); return this._vars.suggestedItems.filter(function (v) { return v.selected && setIds.indexOf(v[key]) < 0; }); } }, { key: '_selectItem', value: function _selectItem(key) { var _this9 = this; var scroll = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; this._vars.suggestedItems.forEach(function (v) { v.selected = v[_this9.key] === key; if (v.selected && scroll) { var height = parseInt(_this9._html.suggest.style.maxHeight, 10); if (height) { var listBounds = _this9._html.suggestList.getBoundingClientRect(); var elBounds = v.el.getBoundingClientRect(); var top = elBounds.top - listBounds.top; var bottom = top + elBounds.height; if (bottom >= height + _this9._html.suggest.scrollTop) { _this9._html.suggest.scrollTop = bottom - height; } else if (top < _this9._html.suggest.scrollTop) { _this9._html.suggest.scrollTop = top; } } } }); } }, { key: '_deselectItem', value: function _deselectItem(key) { var _this10 = this; this._vars.suggestedItems.every(function (v) { if (v[_this10.key] === key) { v.selected = false; return false; } return true; }); return this; } }, { key: '_deselectItems', value: function _deselectItems() { this._vars.suggestedItems.forEach(function (v) { v.selected = false; }); return this; } }, { key: '_refreshItemsSelection', value: function _refreshItemsSelection() { this._vars.suggestedItems.forEach(function (v) { if (v.selected && v.el) { v.el.classList.add('selected'); } else if (v.el) { v.el.classList.remove('selected'); } }); } }, { key: '_getSelectedItemIndex', value: function _getSelectedItemIndex() { var index = null; this._vars.suggestedItems.every(function (v, k) { if (v.selected) { index = k; return false; } return true; }); return index; } }, { key: '_getFocusedItemIndex', value: function _getFocusedItemIndex() { var last = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var index = null; this._vars.setItems.every(function (v, k) { if (v.focused) { index = k; if (!last) { return false; } } return true; }); return index; } }, { key: '_getItem', value: function _getItem(val) { var prop = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; if (prop === null) prop = this.key; var items = this._filterItems(this._vars.setItems, val, prop); return items.length ? items[0] : null; } }, { key: '_getSuggestedItem', value: function _getSuggestedItem(val) { var prop = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; if (prop === null) prop = this.key; var items = this._filterItems(this._vars.suggestedItems, val, prop); return items.length ? items[0] : null; } }, { key: '_getAvailableItem', value: function _getAvailableItem(val) { var prop = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; if (prop === null) prop = this.key; var items = this._filterItems(this._options.items, val, prop); return items.length ? items[0] : null; } }, { key: '_filterItems', value: function _filterItems(items, val, prop) { var matchCase = this._options.filterMatchCase; return items.filter(function (v) { if (typeof v[prop] === 'string' && typeof val === 'string') { if (matchCase) return v[prop] === val; return v[prop].toLowerCase() == val.toLowerCase(); } return v[prop] == val; }); } }, { key: '_removeItem', value: function _removeItem(key) { var _this11 = this; this._vars.setItems.every(function (item, k) { if (item[_this11.key] === key) { _this11.emit('removeToken', _this11, item); _this11._vars.setItems.splice(k, 1); _this11.emit('removedToken', _this11, item); _this11.emit('change', _this11); return false; } return true; }); return this; } }, { key: '_addItem', value: function _addItem(item) { item.focused = false; var o = this._options; // Check if item already exists in a given list. if (item.isNew && !this._getItem(item[o.itemData], o.itemData) || !this._getItem(item[o.itemValue], o.itemValue)) { this.emit('addToken', this, item); if (!this._options.maxItems || this._options.maxItems && this._vars.setItems.length < this._options.maxItems) { item.selected = false; var clonedItem = _extends({}, item); this._vars.setItems.push(clonedItem); this.emit('addedToken', this, clonedItem); this.emit('change', this); } } return this; } }, { key: 'getFocusedItem', value: function getFocusedItem() { var items = this._vars.setItems.filter(function (item) { return item.focused; })[0]; if (items.length) return items[0]; return null; } }, { key: 'getFocusedItems', value: function getFocusedItems() { return this._vars.setItems.filter(function (item) { return item.focused; }); } }, { key: '_focusItem', value: function _focusItem(key) { var shift = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var _this12 = this; var ctrl = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var add = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; if (shift) { var first = null; var last = null; var target = null; var length = this._vars.setItems.length; this._vars.setItems.forEach(function (item, k) { if (item[_this12.key] === key) { target = k; } if (first === null && item.focused) { first = k; } if (item.focused) { last = k; } }); if ((target === 0 || target === length - 1) && first === null && last === null) { return; } else if (first === null && last === null) { this._vars.setItems[target].focused = true; } else if (target === 0 && last === length - 1 && !add) { this._vars.setItems[first].focused = false; } else { first = Math.min(target, first); last = Math.max(target, last); this._vars.setItems.forEach(function (item, k) { item.focused = target === k || k >= first && k <= last; }); } } else { this._vars.setItems.forEach(function (item) { if (ctrl) { item.focused = item[_this12.key] === key ? !item.focused : item.focused; } else { item.focused = item[_this12.key] === key; } }); } return this; } }, { key: '_defocusItem', value: function _defocusItem(key) { var _this13 = this; return this._vars.setItems.filter(function (item) { if (item[_this13.key] === key) { item.focused = false; } }); } }, { key: '_defocusItems', value: function _defocusItems() { this._vars.setItems.forEach(function (item) { item.focused = false; }); return this; } }, { key: '_newItem', value: function _newItem(value) { var o = this._options; if (typeof value === 'string' && (!value.length || value.length < o.minLength)) return null; var item = this._getItem(value, o.itemData) || this._getSuggestedItem(value, o.itemData) || this._getAvailableItem(value, o.itemData); if (!item && o.newItems) { var _item; // If validation is set and returns false - item should not be added. if (typeof o.validateNewItem === 'function' && !o.validateNewItem(value)) { return null; } item = (_item = { isNew: true }, _defineProperty(_item, this.key, guid()), _defineProperty(_item, o.itemData, value), _item); this.emit('newToken', this, item); } if (item) { this._addItem(item); return item; } return null; } // Wrapper for build funct