From dee69177ed1b0fc8e0929f10e3360eb3afb156b1 Mon Sep 17 00:00:00 2001 From: Yair Even Or Date: Tue, 15 Nov 2022 17:41:43 +0200 Subject: [PATCH] build --- dist/jQuery.tagify.min.js | 2 +- dist/react.tagify.js | 258 +-- dist/tagify.esm.js | 76 +- dist/tagify.js | 76 +- dist/tagify.min.js | 3579 +------------------------------------ 5 files changed, 79 insertions(+), 3912 deletions(-) diff --git a/dist/jQuery.tagify.min.js b/dist/jQuery.tagify.min.js index 20e41591..82275c87 100644 --- a/dist/jQuery.tagify.min.js +++ b/dist/jQuery.tagify.min.js @@ -64,5 +64,5 @@ * THE SOFTWARE IS NOT PERMISSIBLE TO BE SOLD. */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Tagify=e()}(this,(function(){"use strict";function t(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(t);e&&(s=s.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,s)}return i}function e(e){for(var s=1;s(t=""+t,e=""+e,s&&(t=t.trim(),e=e.trim()),i?t==e:t.toLowerCase()==e.toLowerCase()),a=(t,e)=>t&&Array.isArray(t)&&t.map((t=>n(t,e)));function n(t,e){var i,s={};for(i in t)e.indexOf(i)<0&&(s[i]=t[i]);return s}function o(t){var e=document.createElement("div");return t.replace(/\&#?[0-9a-z]+;/gi,(function(t){return e.innerHTML=t,e.innerText}))}function r(t){return(new DOMParser).parseFromString(t.trim(),"text/html").body.firstElementChild}function l(t,e){for(e=e||"previous";t=t[e+"Sibling"];)if(3==t.nodeType)return t}function d(t){return"string"==typeof t?t.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/`|'/g,"'"):t}function h(t){var e=Object.prototype.toString.call(t).split(" ")[1].slice(0,-1);return t===Object(t)&&"Array"!=e&&"Function"!=e&&"RegExp"!=e&&"HTMLUnknownElement"!=e}function g(t,e,i){function s(t,e){for(var i in e)if(e.hasOwnProperty(i)){if(h(e[i])){h(t[i])?s(t[i],e[i]):t[i]=Object.assign({},e[i]);continue}if(Array.isArray(e[i])){t[i]=Object.assign([],e[i]);continue}t[i]=e[i]}}return t instanceof Object||(t={}),s(t,e),i&&s(t,i),t}function p(){const t=[],e={};for(let i of arguments)for(let s of i)h(s)?e[s.value]||(t.push(s),e[s.value]=1):t.includes(s)||t.push(s);return t}function c(t){return String.prototype.normalize?"string"==typeof t?t.normalize("NFD").replace(/[\u0300-\u036f]/g,""):void 0:t}var u=()=>/(?=.*chrome)(?=.*android)/i.test(navigator.userAgent);function m(){return([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,(t=>(t^crypto.getRandomValues(new Uint8Array(1))[0]&15>>t/4).toString(16)))}function v(t){return t&&t.classList&&t.classList.contains(this.settings.classNames.tag)}function f(t,e){var i=window.getSelection();return e=e||i.getRangeAt(0),"string"==typeof t&&(t=document.createTextNode(t)),e&&(e.deleteContents(),e.insertNode(t)),t}var T={delimiters:",",pattern:null,tagTextProp:"value",maxTags:1/0,callbacks:{},addTagOnBlur:!0,onChangeAfterBlur:!0,duplicates:!1,whitelist:[],blacklist:[],enforceWhitelist:!1,userInput:!0,keepInvalidTags:!1,createInvalidTags:!0,mixTagsAllowedAfter:/,|\.|\:|\s/,mixTagsInterpolator:["[[","]]"],backspace:!0,skipInvalid:!1,pasteAsTags:!0,editTags:{clicks:2,keepInvalid:!0},transformTag:()=>{},trim:!0,a11y:{focusableTags:!1},mixMode:{insertAfterTag:" "},autoComplete:{enabled:!0,rightKey:!1},classNames:{namespace:"tagify",mixMode:"tagify--mix",selectMode:"tagify--select",input:"tagify__input",focus:"tagify--focus",tagNoAnimation:"tagify--noAnim",tagInvalid:"tagify--invalid",tagNotAllowed:"tagify--notAllowed",scopeLoading:"tagify--loading",hasMaxTags:"tagify--hasMaxTags",hasNoTags:"tagify--noTags",empty:"tagify--empty",inputInvalid:"tagify__input--invalid",dropdown:"tagify__dropdown",dropdownWrapper:"tagify__dropdown__wrapper",dropdownHeader:"tagify__dropdown__header",dropdownFooter:"tagify__dropdown__footer",dropdownItem:"tagify__dropdown__item",dropdownItemActive:"tagify__dropdown__item--active",dropdownItemHidden:"tagify__dropdown__item--hidden",dropdownInital:"tagify__dropdown--initial",tag:"tagify__tag",tagText:"tagify__tag-text",tagX:"tagify__tag__removeBtn",tagLoading:"tagify__tag--loading",tagEditing:"tagify__tag--editable",tagFlash:"tagify__tag--flash",tagHide:"tagify__tag--hide"},dropdown:{classname:"",enabled:2,maxItems:10,searchKeys:["value","searchBy"],fuzzySearch:!0,caseSensitive:!1,accentedSearch:!0,includeSelectedTags:!1,highlightFirst:!1,closeOnSelect:!0,clearOnSelect:!0,position:"all",appendTarget:null},hooks:{beforeRemoveTag:()=>Promise.resolve(),beforePaste:()=>Promise.resolve(),suggestionClick:()=>Promise.resolve()}};function w(){this.dropdown={};for(let t in this._dropdown)this.dropdown[t]="function"==typeof this._dropdown[t]?this._dropdown[t].bind(this):this._dropdown[t];this.dropdown.refs()}var b={refs(){this.DOM.dropdown=this.parseTemplate("dropdown",[this.settings]),this.DOM.dropdown.content=this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-wrapper']")},getHeaderRef(){return this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-header']")},getFooterRef(){return this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-footer']")},getAllSuggestionsRefs(){return[...this.DOM.dropdown.content.querySelectorAll(this.settings.classNames.dropdownItemSelector)]},show(t){var e,i,a,n=this.settings,o="mix"==n.mode&&!n.enforceWhitelist,r=!n.whitelist||!n.whitelist.length,l="manual"==n.dropdown.position;if(t=void 0===t?this.state.inputText:t,!(r&&!o&&!n.templates.dropdownItemNoMatch||!1===n.dropdown.enable||this.state.isLoading||this.settings.readonly)){if(clearTimeout(this.dropdownHide__bindEventsTimeout),this.suggestedListItems=this.dropdown.filterListItems(t),t&&!this.suggestedListItems.length&&(this.trigger("dropdown:noMatch",t),n.templates.dropdownItemNoMatch&&(a=n.templates.dropdownItemNoMatch.call(this,{value:t}))),!a){if(this.suggestedListItems.length)t&&o&&!this.state.editing.scope&&!s(this.suggestedListItems[0].value,t)&&this.suggestedListItems.unshift({value:t});else{if(!t||!o||this.state.editing.scope)return this.input.autocomplete.suggest.call(this),void this.dropdown.hide();this.suggestedListItems=[{value:t}]}i=""+(h(e=this.suggestedListItems[0])?e.value:e),n.autoComplete&&i&&0==i.indexOf(t)&&this.input.autocomplete.suggest.call(this,e)}this.dropdown.fill(a),n.dropdown.highlightFirst&&this.dropdown.highlightOption(this.DOM.dropdown.content.querySelector(n.classNames.dropdownItemSelector)),this.state.dropdown.visible||setTimeout(this.dropdown.events.binding.bind(this)),this.state.dropdown.visible=t||!0,this.state.dropdown.query=t,this.setStateSelection(),l||setTimeout((()=>{this.dropdown.position(),this.dropdown.render()})),setTimeout((()=>{this.trigger("dropdown:show",this.DOM.dropdown)}))}},hide(t){var e=this.DOM,i=e.scope,s=e.dropdown,a="manual"==this.settings.dropdown.position&&!t;if(s&&document.body.contains(s)&&!a)return window.removeEventListener("resize",this.dropdown.position),this.dropdown.events.binding.call(this,!1),i.setAttribute("aria-expanded",!1),s.parentNode.removeChild(s),setTimeout((()=>{this.state.dropdown.visible=!1}),100),this.state.dropdown.query=this.state.ddItemData=this.state.ddItemElm=this.state.selection=null,this.state.tag&&this.state.tag.value.length&&(this.state.flaggedTags[this.state.tag.baseOffset]=this.state.tag),this.trigger("dropdown:hide",s),this},toggle(t){this.dropdown[this.state.dropdown.visible&&!t?"hide":"show"]()},render(){var t,e,i,s=(t=this.DOM.dropdown,(i=t.cloneNode(!0)).style.cssText="position:fixed; top:-9999px; opacity:0",document.body.appendChild(i),e=i.clientHeight,i.parentNode.removeChild(i),e),a=this.settings;return"number"==typeof a.dropdown.enabled&&a.dropdown.enabled>=0?(this.DOM.scope.setAttribute("aria-expanded",!0),document.body.contains(this.DOM.dropdown)||(this.DOM.dropdown.classList.add(a.classNames.dropdownInital),this.dropdown.position(s),a.dropdown.appendTarget.appendChild(this.DOM.dropdown),setTimeout((()=>this.DOM.dropdown.classList.remove(a.classNames.dropdownInital)))),this):this},fill(t){t="string"==typeof t?t:this.dropdown.createListHTML(t||this.suggestedListItems);var e,i=this.settings.templates.dropdownContent.call(this,t);this.DOM.dropdown.content.innerHTML=(e=i)?e.replace(/\>[\r\n ]+\<").replace(/(<.*?>)|\s+/g,((t,e)=>e||" ")):""},fillHeaderFooter(){this.settings.templates;var t=this.dropdown.filterListItems(this.state.dropdown.query),e=this.parseTemplate("dropdownHeader",[t]),i=this.parseTemplate("dropdownFooter",[t]),s=this.dropdown.getHeaderRef(),a=this.dropdown.getFooterRef();e&&(null==s||s.parentNode.replaceChild(e,s)),i&&(null==a||a.parentNode.replaceChild(i,a))},refilter(t){t=t||this.state.dropdown.query||"",this.suggestedListItems=this.dropdown.filterListItems(t),this.dropdown.fill(),this.suggestedListItems.length||this.dropdown.hide(),this.trigger("dropdown:updated",this.DOM.dropdown)},position(t){var e=this.settings.dropdown;if("manual"!=e.position){var i,s,a,n,o,r,l=this.DOM.dropdown,d=e.placeAbove,h=e.appendTarget===document.body,g=h?window.pageYOffset:e.appendTarget.scrollTop,p=document.fullscreenElement||document.webkitFullscreenElement||document.documentElement,c=p.clientHeight,u=Math.max(p.clientWidth||0,window.innerWidth||0)>480?e.position:"all",m=this.DOM["input"==u?"input":"scope"];if(t=t||l.clientHeight,this.state.dropdown.visible){if("text"==u?(a=(i=function(){const t=document.getSelection();if(t.rangeCount){const e=t.getRangeAt(0),i=e.startContainer,s=e.startOffset;let a,n;if(s>0)return n=document.createRange(),n.setStart(i,s-1),n.setEnd(i,s),a=n.getBoundingClientRect(),{left:a.right,top:a.top,bottom:a.bottom};if(i.getBoundingClientRect)return i.getBoundingClientRect()}return{left:-9999,top:-9999}}()).bottom,s=i.top,n=i.left,o="auto"):(r=function(t){for(var e=0,i=0;t&&t!=p;)e+=t.offsetLeft||0,i+=t.offsetTop||0,t=t.parentNode;return{left:e,top:i}}(e.appendTarget),s=(i=m.getBoundingClientRect()).top-r.top,a=i.bottom-1-r.top,n=i.left-r.left,o=i.width+"px"),!h){let t=function(){for(var t=0,i=e.appendTarget.parentNode;i;)t+=i.scrollTop||0,i=i.parentNode;return t}();s+=t,a+=t}s=Math.floor(s),a=Math.ceil(a),d=void 0===d?c-i.bottom0&&void 0!==arguments[0])||arguments[0];var e=this.dropdown.events.callbacks,i=this.listeners.dropdown=this.listeners.dropdown||{position:this.dropdown.position.bind(this,null),onKeyDown:e.onKeyDown.bind(this),onMouseOver:e.onMouseOver.bind(this),onMouseLeave:e.onMouseLeave.bind(this),onClick:e.onClick.bind(this),onScroll:e.onScroll.bind(this)},s=t?"addEventListener":"removeEventListener";"manual"!=this.settings.dropdown.position&&(document[s]("scroll",i.position,!0),window[s]("resize",i.position),window[s]("keydown",i.onKeyDown)),this.DOM.dropdown[s]("mouseover",i.onMouseOver),this.DOM.dropdown[s]("mouseleave",i.onMouseLeave),this.DOM.dropdown[s]("mousedown",i.onClick),this.DOM.dropdown.content[s]("scroll",i.onScroll)},callbacks:{onKeyDown(t){if(this.state.hasFocus&&!this.state.composing){var e=this.DOM.dropdown.querySelector(this.settings.classNames.dropdownItemActiveSelector),i=this.dropdown.getSuggestionDataByNode(e);switch(t.key){case"ArrowDown":case"ArrowUp":case"Down":case"Up":t.preventDefault();var s=this.dropdown.getAllSuggestionsRefs(),a="ArrowUp"==t.key||"Up"==t.key;e&&(e=this.dropdown.getNextOrPrevOption(e,!a)),e&&e.matches(this.settings.classNames.dropdownItemSelector)||(e=s[a?s.length-1:0]),i=this.dropdown.getSuggestionDataByNode(e),this.dropdown.highlightOption(e,!0);break;case"Escape":case"Esc":this.dropdown.hide();break;case"ArrowRight":if(this.state.actions.ArrowLeft)return;case"Tab":if("mix"!=this.settings.mode&&e&&!this.settings.autoComplete.rightKey&&!this.state.editing){t.preventDefault();var n=this.dropdown.getMappedValue(i);return this.input.autocomplete.set.call(this,n),!1}return!0;case"Enter":t.preventDefault(),this.settings.hooks.suggestionClick(t,{tagify:this,tagData:i,suggestionElm:e}).then((()=>{if(e)return this.dropdown.selectOption(e),e=this.dropdown.getNextOrPrevOption(e,!a),void this.dropdown.highlightOption(e);this.dropdown.hide(),"mix"!=this.settings.mode&&this.addTags(this.state.inputText.trim(),!0)})).catch((t=>t));break;case"Backspace":{if("mix"==this.settings.mode||this.state.editing.scope)return;const t=this.input.raw.call(this);""!=t&&8203!=t.charCodeAt(0)||(!0===this.settings.backspace?this.removeTags():"edit"==this.settings.backspace&&setTimeout(this.editTag.bind(this),0))}}}},onMouseOver(t){var e=t.target.closest(this.settings.classNames.dropdownItemSelector);e&&this.dropdown.highlightOption(e)},onMouseLeave(t){this.dropdown.highlightOption()},onClick(t){if(0==t.button&&t.target!=this.DOM.dropdown&&t.target!=this.DOM.dropdown.content){var e=t.target.closest(this.settings.classNames.dropdownItemSelector),i=this.dropdown.getSuggestionDataByNode(e);this.state.actions.selectOption=!0,setTimeout((()=>this.state.actions.selectOption=!1),50),this.settings.hooks.suggestionClick(t,{tagify:this,tagData:i,suggestionElm:e}).then((()=>{e?this.dropdown.selectOption(e,t):this.dropdown.hide()})).catch((t=>console.warn(t)))}},onScroll(t){var e=t.target,i=e.scrollTop/(e.scrollHeight-e.parentNode.clientHeight)*100;this.trigger("dropdown:scroll",{percentage:Math.round(i)})}}},getSuggestionDataByNode(t){var e=t&&t.getAttribute("value");return this.suggestedListItems.find((t=>t.value==e))||null},getNextOrPrevOption(t){let e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];var i=this.dropdown.getAllSuggestionsRefs(),s=i.findIndex((e=>e===t));return e?i[s+1]:i[s-1]},highlightOption(t,e){var i,s=this.settings.classNames.dropdownItemActive;if(this.state.ddItemElm&&(this.state.ddItemElm.classList.remove(s),this.state.ddItemElm.removeAttribute("aria-selected")),!t)return this.state.ddItemData=null,this.state.ddItemElm=null,void this.input.autocomplete.suggest.call(this);i=this.dropdown.getSuggestionDataByNode(t),this.state.ddItemData=i,this.state.ddItemElm=t,t.classList.add(s),t.setAttribute("aria-selected",!0),e&&(t.parentNode.scrollTop=t.clientHeight+t.offsetTop-t.parentNode.clientHeight),this.settings.autoComplete&&(this.input.autocomplete.suggest.call(this,i),this.dropdown.position())},selectOption(t,e){var i=this.settings.dropdown,s=i.clearOnSelect,a=i.closeOnSelect;if(!t)return this.addTags(this.state.inputText,!0),void(a&&this.dropdown.hide());e=e||{};var n=t.getAttribute("value"),o="noMatch"==n,r=this.suggestedListItems.find((t=>(t.value||t)==n));this.trigger("dropdown:select",{data:r,elm:t,event:e}),n&&(r||o)?(this.state.editing?this.onEditTagDone(null,g({__isValid:!0},this.normalizeTags([r])[0])):this["mix"==this.settings.mode?"addMixTags":"addTags"]([r||this.input.raw.call(this)],s),this.DOM.input.parentNode&&(setTimeout((()=>{this.DOM.input.focus(),this.toggleFocusClass(!0),this.setRangeAtStartEnd(!1,this.DOM.input)})),a&&setTimeout(this.dropdown.hide.bind(this)),t.addEventListener("transitionend",(()=>{this.dropdown.fillHeaderFooter(),setTimeout((()=>t.remove()),100)}),{once:!0}),t.classList.add(this.settings.classNames.dropdownItemHidden))):a&&setTimeout(this.dropdown.hide.bind(this))},selectAll(t){this.suggestedListItems.length=0,this.dropdown.hide(),this.dropdown.filterListItems("");var e=this.dropdown.filterListItems("");return t||(e=this.state.dropdown.suggestions),this.addTags(e,!0),this},filterListItems(t,e){var i,s,a,n,o,r=this.settings,l=r.dropdown,d=(e=e||{},[]),g=[],p=r.whitelist,u=l.maxItems>=0?l.maxItems:1/0,m=l.searchKeys,v=0;if(!(t="select"==r.mode&&this.value.length&&this.value[0][r.tagTextProp]==t?"":t)||!m.length)return d=l.includeSelectedTags?p:p.filter((t=>!this.isTagDuplicate(h(t)?t.value:t))),this.state.dropdown.suggestions=d,d.slice(0,u);function f(t,e){return e.toLowerCase().split(" ").every((e=>t.includes(e.toLowerCase())))}for(o=l.caseSensitive?""+t:(""+t).toLowerCase();vm.includes(t)))?["value"]:m;l.fuzzySearch&&!e.exact?(a=u.reduce(((t,e)=>t+" "+(i[e]||"")),"").toLowerCase().trim(),l.accentedSearch&&(a=c(a),o=c(o)),t=0==a.indexOf(o),r=a===o,s=f(a,o)):(t=!0,s=u.some((t=>{var s=""+(i[t]||"");return l.accentedSearch&&(s=c(s),o=c(o)),l.caseSensitive||(s=s.toLowerCase()),r=s===o,e.exact?s===o:0==s.indexOf(o)}))),n=!l.includeSelectedTags&&this.isTagDuplicate(h(i)?i.value:i),s&&!n&&(r&&t?g.push(i):"startsWith"==l.sortby&&t?d.unshift(i):d.push(i))}return this.state.dropdown.suggestions=g.concat(d),"function"==typeof l.sortby?l.sortby(g.concat(d),o):g.concat(d).slice(0,u)},getMappedValue(t){var e=this.settings.dropdown.mapValueTo;return e?"function"==typeof e?e(t):t[e]||t.value:t.value},createListHTML(t){return g([],t).map(((t,i)=>{"string"!=typeof t&&"number"!=typeof t||(t={value:t});var s=this.dropdown.getMappedValue(t);return s="string"==typeof s?d(s):s,this.settings.templates.dropdownItem.apply(this,[e(e({},t),{},{mappedValue:s}),this])})).join("")}};const y="@yaireo/tagify/";var x,D={empty:"empty",exceed:"number of tags exceeded",pattern:"pattern mismatch",duplicate:"already exists",notAllowed:"not allowed"},O={wrapper:(t,e)=>`\n \n ​\n `,tag(t,e){let i=e.settings;return`\n \n
\n ${t[i.tagTextProp]||t.value}\n
\n
`},dropdown(t){var e=t.dropdown,i="manual"==e.position,s=`${t.classNames.dropdown}`;return`
\n
\n
`},dropdownContent(t){var e=this.settings,i=this.state.dropdown.suggestions;return`\n ${e.templates.dropdownHeader.call(this,i)}\n ${t}\n ${e.templates.dropdownFooter.call(this,i)}\n `},dropdownItem(t){return`
${t.mappedValue||t.value}
`},dropdownHeader(t){return`
`},dropdownFooter(t){var e=t.length-this.settings.dropdown.maxItems;return e>0?`
\n ${e} more items. Refine your search.\n
`:""},dropdownItemNoMatch:null};var M={customBinding(){this.customEventsList.forEach((t=>{this.on(t,this.settings.callbacks[t])}))},binding(){let t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];var e,i=this.events.callbacks,s=t?"addEventListener":"removeEventListener";if(!this.state.mainEvents||!t){for(var a in this.state.mainEvents=t,t&&!this.listeners.main&&(this.events.bindGlobal.call(this),this.settings.isJQueryPlugin&&jQuery(this.DOM.originalInput).on("tagify.removeAllTags",this.removeAllTags.bind(this))),e=this.listeners.main=this.listeners.main||{focus:["input",i.onFocusBlur.bind(this)],keydown:["input",i.onKeydown.bind(this)],click:["scope",i.onClickScope.bind(this)],dblclick:["scope",i.onDoubleClickScope.bind(this)],paste:["input",i.onPaste.bind(this)],drop:["input",i.onDrop.bind(this)],compositionstart:["input",i.onCompositionStart.bind(this)],compositionend:["input",i.onCompositionEnd.bind(this)]})this.DOM[e[a][0]][s](a,e[a][1]);clearInterval(this.listeners.main.originalInputValueObserverInterval),this.listeners.main.originalInputValueObserverInterval=setInterval(i.observeOriginalInputValue.bind(this),500);var n=this.listeners.main.inputMutationObserver||new MutationObserver(i.onInputDOMChange.bind(this));n&&n.disconnect(),"mix"==this.settings.mode&&n.observe(this.DOM.input,{childList:!0})}},bindGlobal(t){var e,i=this.events.callbacks,s=t?"removeEventListener":"addEventListener";if(t||!this.listeners.global)for(e of(this.listeners.global=this.listeners&&this.listeners.global||[{type:this.isIE?"keydown":"input",target:this.DOM.input,cb:i[this.isIE?"onInputIE":"onInput"].bind(this)},{type:"keydown",target:window,cb:i.onWindowKeyDown.bind(this)},{type:"blur",target:this.DOM.input,cb:i.onFocusBlur.bind(this)}],this.listeners.global))e.target[s](e.type,e.cb)},unbindGlobal(){this.events.bindGlobal.call(this,!0)},callbacks:{onFocusBlur(t){var e,i,s=this.settings,a=t.target?this.trim(t.target.textContent):"",n=null===(e=this.value)||void 0===e||null===(i=e[0])||void 0===i?void 0:i[s.tagTextProp],o=t.type,r=s.dropdown.enabled>=0,l={relatedTarget:t.relatedTarget},d=this.state.actions.selectOption&&(r||!s.dropdown.closeOnSelect),h=this.state.actions.addNew&&r,g=t.relatedTarget&&v.call(this,t.relatedTarget)&&this.DOM.scope.contains(t.relatedTarget);if("blur"==o){if(t.relatedTarget===this.DOM.scope)return this.dropdown.hide(),void this.DOM.input.focus();this.postUpdate(),s.onChangeAfterBlur&&this.triggerChangeEvent()}if(!d&&!h)if(this.state.hasFocus="focus"==o&&+new Date,this.toggleFocusClass(this.state.hasFocus),"mix"!=s.mode){if("focus"==o)return this.trigger("focus",l),void(0!==s.dropdown.enabled&&s.userInput||this.dropdown.show(this.value.length?"":void 0));"blur"==o&&(this.trigger("blur",l),this.loading(!1),"select"==s.mode&&(g&&(this.removeTags(),a=""),n===a&&(a="")),a&&!this.state.actions.selectOption&&s.addTagOnBlur&&this.addTags(a,!0)),this.DOM.input.removeAttribute("style"),this.dropdown.hide()}else"focus"==o?this.trigger("focus",l):"blur"==t.type&&(this.trigger("blur",l),this.loading(!1),this.dropdown.hide(),this.state.dropdown.visible=void 0,this.setStateSelection())},onCompositionStart(t){this.state.composing=!0},onCompositionEnd(t){this.state.composing=!1},onWindowKeyDown(t){var e,i=document.activeElement;if(v.call(this,i)&&this.DOM.scope.contains(document.activeElement))switch(e=i.nextElementSibling,t.key){case"Backspace":this.settings.readonly||(this.removeTags(i),(e||this.DOM.input).focus());break;case"Enter":setTimeout(this.editTag.bind(this),0,i)}},onKeydown(t){var e=this.settings;if(!this.state.composing&&e.userInput){"select"==e.mode&&e.enforceWhitelist&&this.value.length&&"Tab"!=t.key&&t.preventDefault();var i=this.trim(t.target.textContent);if(this.trigger("keydown",{event:t}),"mix"==e.mode){switch(t.key){case"Left":case"ArrowLeft":this.state.actions.ArrowLeft=!0;break;case"Delete":case"Backspace":if(this.state.editing)return;var s,a,n,r=document.getSelection(),d="Delete"==t.key&&r.anchorOffset==(r.anchorNode.length||0),h=r.anchorNode.previousSibling,g=1==r.anchorNode.nodeType||!r.anchorOffset&&h&&1==h.nodeType&&r.anchorNode.previousSibling,p=o(this.DOM.input.innerHTML),c=this.getTagElms();if("edit"==e.backspace&&g)return s=1==r.anchorNode.nodeType?null:r.anchorNode.previousElementSibling,setTimeout(this.editTag.bind(this),0,s),void t.preventDefault();if(u()&&g)return n=l(g),g.hasAttribute("readonly")||g.remove(),this.DOM.input.focus(),void setTimeout((()=>{this.placeCaretAfterNode(n),this.DOM.input.click()}));if("BR"==r.anchorNode.nodeName)return;if((d||g)&&1==r.anchorNode.nodeType?a=0==r.anchorOffset?d?c[0]:null:c[Math.min(c.length,r.anchorOffset)-1]:d?a=r.anchorNode.nextElementSibling:g&&(a=g),3==r.anchorNode.nodeType&&!r.anchorNode.nodeValue&&r.anchorNode.previousElementSibling&&t.preventDefault(),(g||d)&&!e.backspace)return void t.preventDefault();if("Range"!=r.type&&!r.anchorOffset&&r.anchorNode==this.DOM.input&&"Delete"!=t.key)return void t.preventDefault();if("Range"!=r.type&&a&&a.hasAttribute("readonly"))return void this.placeCaretAfterNode(l(a));clearTimeout(x),x=setTimeout((()=>{var t=document.getSelection(),e=o(this.DOM.input.innerHTML),i=!d&&t.anchorNode.previousSibling;if(e.length>=p.length&&i)if(v.call(this,i)&&!i.hasAttribute("readonly")){if(this.removeTags(i),this.fixFirefoxLastTagNoCaret(),2==this.DOM.input.children.length&&"BR"==this.DOM.input.children[1].tagName)return this.DOM.input.innerHTML="",this.value.length=0,!0}else i.remove();this.value=[].map.call(c,((t,e)=>{var i=this.tagData(t);if(t.parentNode||i.readonly)return i;this.trigger("remove",{tag:t,index:e,data:i})})).filter((t=>t))}),20)}return!0}switch(t.key){case"Backspace":"select"==e.mode&&e.enforceWhitelist&&this.value.length?this.removeTags():this.state.dropdown.visible&&"manual"!=e.dropdown.position||""!=t.target.textContent&&8203!=i.charCodeAt(0)||(!0===e.backspace?this.removeTags():"edit"==e.backspace&&setTimeout(this.editTag.bind(this),0));break;case"Esc":case"Escape":if(this.state.dropdown.visible)return;t.target.blur();break;case"Down":case"ArrowDown":this.state.dropdown.visible||this.dropdown.show();break;case"ArrowRight":{let t=this.state.inputSuggestion||this.state.ddItemData;if(t&&e.autoComplete.rightKey)return void this.addTags([t],!0);break}case"Tab":{let s="select"==e.mode;if(!i||s)return!0;t.preventDefault()}case"Enter":if(this.state.dropdown.visible&&"manual"!=e.dropdown.position)return;t.preventDefault(),setTimeout((()=>{this.state.actions.selectOption||this.addTags(i,!0)}))}}},onInput(t){this.postUpdate();var e=this.settings;if("mix"==e.mode)return this.events.callbacks.onMixTagsInput.call(this,t);var i=this.input.normalize.call(this),s=i.length>=e.dropdown.enabled,a={value:i,inputElm:this.DOM.input},n=this.validateTag({value:i});"select"==e.mode&&this.toggleScopeValidation(n),a.isValid=n,this.state.inputText!=i&&(this.input.set.call(this,i,!1),-1!=i.search(e.delimiters)?this.addTags(i)&&this.input.set.call(this):e.dropdown.enabled>=0&&this.dropdown[s?"show":"hide"](i),this.trigger("input",a))},onMixTagsInput(t){var e,i,s,a,n,o,r,l,d=this.settings,h=this.value.length,p=this.getTagElms(),c=document.createDocumentFragment(),m=window.getSelection().getRangeAt(0),v=[].map.call(p,(t=>this.tagData(t).value));if("deleteContentBackward"==t.inputType&&u()&&this.events.callbacks.onKeydown.call(this,{target:t.target,key:"Backspace"}),this.value.slice().forEach((t=>{t.readonly&&!v.includes(t.value)&&c.appendChild(this.createTagElem(t))})),c.childNodes.length&&(m.insertNode(c),this.setRangeAtStartEnd(!1,c.lastChild)),p.length!=h)return this.value=[].map.call(this.getTagElms(),(t=>this.tagData(t))),void this.update({withoutChangeEvent:!0});if(this.hasMaxTags())return!0;if(window.getSelection&&(o=window.getSelection()).rangeCount>0&&3==o.anchorNode.nodeType){if((m=o.getRangeAt(0).cloneRange()).collapse(!0),m.setStart(o.focusNode,0),s=(e=m.toString().slice(0,m.endOffset)).split(d.pattern).length-1,(i=e.match(d.pattern))&&(a=e.slice(e.lastIndexOf(i[i.length-1]))),a){if(this.state.actions.ArrowLeft=!1,this.state.tag={prefix:a.match(d.pattern)[0],value:a.replace(d.pattern,"")},this.state.tag.baseOffset=o.baseOffset-this.state.tag.value.length,l=this.state.tag.value.match(d.delimiters))return this.state.tag.value=this.state.tag.value.replace(d.delimiters,""),this.state.tag.delimiters=l[0],this.addTags(this.state.tag.value,d.dropdown.clearOnSelect),void this.dropdown.hide();n=this.state.tag.value.length>=d.dropdown.enabled;try{r=(r=this.state.flaggedTags[this.state.tag.baseOffset]).prefix==this.state.tag.prefix&&r.value[0]==this.state.tag.value[0],this.state.flaggedTags[this.state.tag.baseOffset]&&!this.state.tag.value&&delete this.state.flaggedTags[this.state.tag.baseOffset]}catch(t){}(r||s{this.update({withoutChangeEvent:!0}),this.trigger("input",g({},this.state.tag,{textContent:this.DOM.input.textContent})),this.state.tag&&this.dropdown[n?"show":"hide"](this.state.tag.value)}),10)},onInputIE(t){var e=this;setTimeout((function(){e.events.callbacks.onInput.call(e,t)}))},observeOriginalInputValue(){this.DOM.originalInput.parentNode||this.destroy(),this.DOM.originalInput.value!=this.DOM.originalInput.tagifyValue&&this.loadOriginalValues()},onClickScope(t){var e=this.settings,i=t.target.closest("."+e.classNames.tag),s=+new Date-this.state.hasFocus;if(t.target!=this.DOM.scope){if(!t.target.classList.contains(e.classNames.tagX))return i?(this.trigger("click",{tag:i,index:this.getNodeIndex(i),data:this.tagData(i),event:t}),void(1!==e.editTags&&1!==e.editTags.clicks||this.events.callbacks.onDoubleClickScope.call(this,t))):void(t.target==this.DOM.input&&("mix"==e.mode&&this.fixFirefoxLastTagNoCaret(),s>500)?this.state.dropdown.visible?this.dropdown.hide():0===e.dropdown.enabled&&"mix"!=e.mode&&this.dropdown.show(this.value.length?"":void 0):"select"!=e.mode||0!==e.dropdown.enabled||this.state.dropdown.visible||this.dropdown.show());this.removeTags(t.target.parentNode)}else this.state.hasFocus||this.DOM.input.focus()},onPaste(t){t.preventDefault();var e,i,s=this.settings;if("select"==s.mode&&s.enforceWhitelist||!s.userInput)return!1;s.readonly||(e=t.clipboardData||window.clipboardData,i=e.getData("Text"),s.hooks.beforePaste(t,{tagify:this,pastedText:i,clipboardData:e}).then((e=>{void 0===e&&(e=i),e&&(this.injectAtCaret(e,window.getSelection().getRangeAt(0)),"mix"==this.settings.mode?this.events.callbacks.onMixTagsInput.call(this,t):this.settings.pasteAsTags?this.addTags(this.state.inputText+e,!0):this.state.inputText=e)})).catch((t=>t)))},onDrop(t){t.preventDefault()},onEditTagInput(t,e){var i=t.closest("."+this.settings.classNames.tag),s=this.getNodeIndex(i),a=this.tagData(i),n=this.input.normalize.call(this,t),o={[this.settings.tagTextProp]:n,__tagId:a.__tagId},r=this.validateTag(o);this.editTagChangeDetected(g(a,o))||!0!==t.originalIsValid||(r=!0),i.classList.toggle(this.settings.classNames.tagInvalid,!0!==r),a.__isValid=r,i.title=!0===r?a.title||a.value:r,n.length>=this.settings.dropdown.enabled&&(this.state.editing&&(this.state.editing.value=n),this.dropdown.show(n)),this.trigger("edit:input",{tag:i,index:s,data:g({},this.value[s],{newValue:n}),event:e})},onEditTagPaste(t,e){var i=(e.clipboardData||window.clipboardData).getData("Text");window.getSelection(),e.preventDefault();var s=f(i);this.setRangeAtStartEnd(!1,s)},onEditTagFocus(t){this.state.editing={scope:t,input:t.querySelector("[contenteditable]")}},onEditTagBlur(t){if(this.state.hasFocus||this.toggleFocusClass(),this.DOM.scope.contains(t)){var e,i,s=this.settings,a=t.closest("."+s.classNames.tag),n=this.input.normalize.call(this,t),o=this.tagData(a),r=o.__originalData,l=this.editTagChangeDetected(o),d=this.validateTag({[s.tagTextProp]:n,__tagId:o.__tagId});if(n)if(l){if(e=this.hasMaxTags(),i=g({},r,{[s.tagTextProp]:this.trim(n),__isValid:d}),s.transformTag.call(this,i,r),!0!==(d=(!e||!0===r.__isValid)&&this.validateTag(i))){if(this.trigger("invalid",{data:i,tag:a,message:d}),s.editTags.keepInvalid)return;s.keepInvalidTags?i.__isValid=d:i=r}else s.keepInvalidTags&&(delete i.title,delete i["aria-invalid"],delete i.class);this.onEditTagDone(a,i)}else this.onEditTagDone(a,r);else this.onEditTagDone(a)}},onEditTagkeydown(t,e){if(!this.state.composing)switch(this.trigger("edit:keydown",{event:t}),t.key){case"Esc":case"Escape":e.parentNode.replaceChild(e.__tagifyTagData.__originalHTML,e),this.state.editing=!1;case"Enter":case"Tab":t.preventDefault(),t.target.blur()}},onDoubleClickScope(t){var e,i,s=t.target.closest("."+this.settings.classNames.tag),a=this.tagData(s),n=this.settings;s&&n.userInput&&!1!==a.editable&&(e=s.classList.contains(this.settings.classNames.tagEditing),i=s.hasAttribute("readonly"),"select"==n.mode||n.readonly||e||i||!this.settings.editTags||this.editTag(s),this.toggleFocusClass(!0),this.trigger("dblclick",{tag:s,index:this.getNodeIndex(s),data:this.tagData(s)}))},onInputDOMChange(t){t.forEach((t=>{t.addedNodes.forEach((t=>{if("

"==t.outerHTML)t.replaceWith(document.createElement("br"));else if(1==t.nodeType&&t.querySelector(this.settings.classNames.tagSelector)){let e=document.createTextNode("");3==t.childNodes[0].nodeType&&"BR"!=t.previousSibling.nodeName&&(e=document.createTextNode("\n")),t.replaceWith(e,...[...t.childNodes].slice(0,-1)),this.placeCaretAfterNode(e)}else if(v.call(this,t)){var e;if(3!=(null===(e=t.previousSibling)||void 0===e?void 0:e.nodeType)||t.previousSibling.textContent||t.previousSibling.remove(),t.previousSibling&&"BR"==t.previousSibling.nodeName){t.previousSibling.replaceWith("\n​");let e=t.nextSibling,i="";for(;e;)i+=e.textContent,e=e.nextSibling;i.trim()&&this.placeCaretAfterNode(t.previousSibling)}}})),t.removedNodes.forEach((t=>{t&&"BR"==t.nodeName&&v.call(this,e)&&(this.removeTags(e),this.fixFirefoxLastTagNoCaret())}))}));var e=this.DOM.input.lastChild;e&&""==e.nodeValue&&e.remove(),e&&"BR"==e.nodeName||this.DOM.input.appendChild(document.createElement("br"))}}};function I(t,e){if(!t){console.warn("Tagify:","input element not found",t);const e=new Proxy(this,{get:()=>()=>e});return e}if(t.__tagify)return console.warn("Tagify: ","input element is already Tagified - Same instance is returned.",t),t.__tagify;var i;g(this,function(t){var e=document.createTextNode("");function i(t,i,s){s&&i.split(/\s+/g).forEach((i=>e[t+"EventListener"].call(e,i,s)))}return{off(t,e){return i("remove",t,e),this},on(t,e){return e&&"function"==typeof e&&i("add",t,e),this},trigger(i,s,a){var n;if(a=a||{cloneData:!0},i)if(t.settings.isJQueryPlugin)"remove"==i&&(i="removeTag"),jQuery(t.DOM.originalInput).triggerHandler(i,[s]);else{try{var o="object"==typeof s?s:{value:s};if((o=a.cloneData?g({},o):o).tagify=this,s.event&&(o.event=this.cloneEvent(s.event)),s instanceof Object)for(var r in s)s[r]instanceof HTMLElement&&(o[r]=s[r]);n=new CustomEvent(i,{detail:o})}catch(t){console.warn(t)}e.dispatchEvent(n)}}}}(this)),this.isFirefox=/firefox|fxios/i.test(navigator.userAgent)&&!/seamonkey/i.test(navigator.userAgent),this.isIE=window.document.documentMode,e=e||{},this.getPersistedData=(i=e.id,t=>{let e,s="/"+t;if(1==localStorage.getItem(y+i+"/v",1))try{e=JSON.parse(localStorage[y+i+s])}catch(t){}return e}),this.setPersistedData=(t=>t?(localStorage.setItem(y+t+"/v",1),(e,i)=>{let s="/"+i,a=JSON.stringify(e);e&&i&&(localStorage.setItem(y+t+s,a),dispatchEvent(new Event("storage")))}):()=>{})(e.id),this.clearPersistedData=(t=>e=>{const i=y+"/"+t+"/";if(e)localStorage.removeItem(i+e);else for(let t in localStorage)t.includes(i)&&localStorage.removeItem(t)})(e.id),this.applySettings(t,e),this.state={inputText:"",editing:!1,composing:!1,actions:{},mixMode:{},dropdown:{},flaggedTags:{}},this.value=[],this.listeners={},this.DOM={},this.build(t),w.call(this),this.getCSSVars(),this.loadOriginalValues(),this.events.customBinding.call(this),this.events.binding.call(this),t.autofocus&&this.DOM.input.focus(),t.__tagify=this}return I.prototype={_dropdown:b,helpers:{sameStr:s,removeCollectionProp:a,omit:n,isObject:h,parseHTML:r,escapeHTML:d,extend:g,concatWithoutDups:p,getUID:m,isNodeTag:v},customEventsList:["change","add","remove","invalid","input","click","keydown","focus","blur","edit:input","edit:beforeUpdate","edit:updated","edit:start","edit:keydown","dropdown:show","dropdown:hide","dropdown:select","dropdown:updated","dropdown:noMatch","dropdown:scroll"],dataProps:["__isValid","__removed","__originalData","__originalHTML","__tagId"],trim(t){return this.settings.trim&&t&&"string"==typeof t?t.trim():t},parseHTML:r,templates:O,parseTemplate(t,e){return t=this.settings.templates[t]||t,this.parseHTML(t.apply(this,e))},set whitelist(t){const e=t&&Array.isArray(t);this.settings.whitelist=e?t:[],this.setPersistedData(e?t:[],"whitelist")},get whitelist(){return this.settings.whitelist},generateClassSelectors(t){for(let e in t){let i=e;Object.defineProperty(t,i+"Selector",{get(){return"."+this[i].split(" ")[0]}})}},applySettings(t,i){T.templates=this.templates;var s=this.settings=g({},T,i);if(s.disabled=t.hasAttribute("disabled"),s.readonly=s.readonly||t.hasAttribute("readonly"),s.placeholder=d(t.getAttribute("placeholder")||s.placeholder||""),s.required=t.hasAttribute("required"),this.generateClassSelectors(s.classNames),void 0===s.dropdown.includeSelectedTags&&(s.dropdown.includeSelectedTags=s.duplicates),this.isIE&&(s.autoComplete=!1),["whitelist","blacklist"].forEach((e=>{var i=t.getAttribute("data-"+e);i&&(i=i.split(s.delimiters))instanceof Array&&(s[e]=i)})),"autoComplete"in i&&!h(i.autoComplete)&&(s.autoComplete=T.autoComplete,s.autoComplete.enabled=i.autoComplete),"mix"==s.mode&&(s.autoComplete.rightKey=!0,s.delimiters=i.delimiters||null,s.tagTextProp&&!s.dropdown.searchKeys.includes(s.tagTextProp)&&s.dropdown.searchKeys.push(s.tagTextProp)),t.pattern)try{s.pattern=new RegExp(t.pattern)}catch(t){}if(s.delimiters){s._delimiters=s.delimiters;try{s.delimiters=new RegExp(this.settings.delimiters,"g")}catch(t){}}s.disabled&&(s.userInput=!1),this.TEXTS=e(e({},D),s.texts||{}),("select"!=s.mode||i.dropdown.enabled)&&s.userInput||(s.dropdown.enabled=0),s.dropdown.appendTarget=i.dropdown&&i.dropdown.appendTarget?i.dropdown.appendTarget:document.body;let a=this.getPersistedData("whitelist");Array.isArray(a)&&(this.whitelist=Array.isArray(s.whitelist)?p(s.whitelist,a):a)},getAttributes(t){var e,i=this.getCustomAttributes(t),s="";for(e in i)s+=" "+e+(void 0!==t[e]?`="${i[e]}"`:"");return s},getCustomAttributes(t){if(!h(t))return"";var e,i={};for(e in t)"__"!=e.slice(0,2)&&"class"!=e&&t.hasOwnProperty(e)&&void 0!==t[e]&&(i[e]=d(t[e]));return i},setStateSelection(){var t=window.getSelection(),e={anchorOffset:t.anchorOffset,anchorNode:t.anchorNode,range:t.getRangeAt&&t.rangeCount&&t.getRangeAt(0)};return this.state.selection=e,e},getCSSVars(){var t=getComputedStyle(this.DOM.scope,null);var e;this.CSSVars={tagHideTransition:(t=>{let e=t.value;return"s"==t.unit?1e3*e:e})(function(t){if(!t)return{};var e=(t=t.trim().split(" ")[0]).split(/\d+/g).filter((t=>t)).pop().trim();return{value:+t.split(e).filter((t=>t))[0].trim(),unit:e}}((e="tag-hide-transition",t.getPropertyValue("--"+e))))}},build(t){var e=this.DOM;this.settings.mixMode.integrated?(e.originalInput=null,e.scope=t,e.input=t):(e.originalInput=t,e.originalInput_tabIndex=t.tabIndex,e.scope=this.parseTemplate("wrapper",[t,this.settings]),e.input=e.scope.querySelector(this.settings.classNames.inputSelector),t.parentNode.insertBefore(e.scope,t),t.tabIndex=-1)},destroy(){this.events.unbindGlobal.call(this),this.DOM.scope.parentNode.removeChild(this.DOM.scope),this.DOM.originalInput.tabIndex=this.DOM.originalInput_tabIndex,delete this.DOM.originalInput.__tagify,this.dropdown.hide(!0),clearTimeout(this.dropdownHide__bindEventsTimeout),clearInterval(this.listeners.main.originalInputValueObserverInterval)},loadOriginalValues(t){var e,i=this.settings;if(this.state.blockChangeEvent=!0,void 0===t){const e=this.getPersistedData("value");t=e&&!this.DOM.originalInput.value?e:i.mixMode.integrated?this.DOM.input.textContent:this.DOM.originalInput.value}if(this.removeAllTags(),t)if("mix"==i.mode)this.parseMixTags(t),(e=this.DOM.input.lastChild)&&"BR"==e.tagName||this.DOM.input.insertAdjacentHTML("beforeend","
");else{try{JSON.parse(t)instanceof Array&&(t=JSON.parse(t))}catch(t){}this.addTags(t,!0).forEach((t=>t&&t.classList.add(i.classNames.tagNoAnimation)))}else this.postUpdate();this.state.lastOriginalValueReported=i.mixMode.integrated?"":this.DOM.originalInput.value,this.state.blockChangeEvent=!1},cloneEvent(t){var e={};for(var i in t)"path"!=i&&(e[i]=t[i]);return e},loading(t){return this.state.isLoading=t,this.DOM.scope.classList[t?"add":"remove"](this.settings.classNames.scopeLoading),this},tagLoading(t,e){return t&&t.classList[e?"add":"remove"](this.settings.classNames.tagLoading),this},toggleClass(t,e){"string"==typeof t&&this.DOM.scope.classList.toggle(t,e)},toggleScopeValidation(t){var e=!0===t||void 0===t;!this.settings.required&&t&&t===this.TEXTS.empty&&(e=!0),this.toggleClass(this.settings.classNames.tagInvalid,!e),this.DOM.scope.title=e?"":t},toggleFocusClass(t){this.toggleClass(this.settings.classNames.focus,!!t)},triggerChangeEvent:function(){if(!this.settings.mixMode.integrated){var t=this.DOM.originalInput,e=this.state.lastOriginalValueReported!==t.value,i=new CustomEvent("change",{bubbles:!0});e&&(this.state.lastOriginalValueReported=t.value,i.simulated=!0,t._valueTracker&&t._valueTracker.setValue(Math.random()),t.dispatchEvent(i),this.trigger("change",this.state.lastOriginalValueReported),t.value=this.state.lastOriginalValueReported)}},events:M,fixFirefoxLastTagNoCaret(){},setRangeAtStartEnd(t,e){t="number"==typeof t?t:!!t,e=e.lastChild||e;var i=document.getSelection();if(i.focusNode&&i.focusNode!==this.DOM.input)return!0;try{i.rangeCount>=1&&["Start","End"].forEach((s=>i.getRangeAt(0)["set"+s](e,t||e.length)))}catch(t){}},placeCaretAfterNode(t){if(t&&t.parentNode){var e=t,i=window.getSelection(),s=i.getRangeAt(0);i.rangeCount&&(s.setStartAfter(e||t),s.collapse(!0),i.removeAllRanges(),i.addRange(s))}},insertAfterTag(t,e){if(e=e||this.settings.mixMode.insertAfterTag,t&&t.parentNode&&e)return e="string"==typeof e?document.createTextNode(e):e,t.parentNode.insertBefore(e,t.nextSibling),e},editTagChangeDetected(t){var e=t.__originalData;for(var i in e)if(!this.dataProps.includes(i)&&t[i]!=e[i])return!0;return!1},getTagTextNode(t){return t.querySelector(this.settings.classNames.tagTextSelector)},setTagTextNode(t,e){this.getTagTextNode(t).innerHTML=d(e)},editTag(t,e){t=t||this.getLastTag(),e=e||{},this.dropdown.hide();var i=this.settings,s=this.getTagTextNode(t),a=this.getNodeIndex(t),n=this.tagData(t),o=this.events.callbacks,r=this,l=!0;if(s){if(!(n instanceof Object&&"editable"in n)||n.editable)return n=this.tagData(t,{__originalData:g({},n),__originalHTML:t.cloneNode(!0)}),this.tagData(n.__originalHTML,n.__originalData),s.setAttribute("contenteditable",!0),t.classList.add(i.classNames.tagEditing),s.addEventListener("focus",o.onEditTagFocus.bind(this,t)),s.addEventListener("blur",(function(){setTimeout((()=>o.onEditTagBlur.call(r,r.getTagTextNode(t))))})),s.addEventListener("input",o.onEditTagInput.bind(this,s)),s.addEventListener("paste",o.onEditTagPaste.bind(this,s)),s.addEventListener("keydown",(e=>o.onEditTagkeydown.call(this,e,t))),s.addEventListener("compositionstart",o.onCompositionStart.bind(this)),s.addEventListener("compositionend",o.onCompositionEnd.bind(this)),e.skipValidation||(l=this.editTagToggleValidity(t)),s.originalIsValid=l,this.trigger("edit:start",{tag:t,index:a,data:n,isValid:l}),s.focus(),this.setRangeAtStartEnd(!1,s),this}else console.warn("Cannot find element in Tag template: .",i.classNames.tagTextSelector)},editTagToggleValidity(t,e){var i;if(e=e||this.tagData(t))return(i=!("__isValid"in e)||!0===e.__isValid)||this.removeTagsFromValue(t),this.update(),t.classList.toggle(this.settings.classNames.tagNotAllowed,!i),e.__isValid;console.warn("tag has no data: ",t,e)},onEditTagDone(t,e){e=e||{};var i={tag:t=t||this.state.editing.scope,index:this.getNodeIndex(t),previousData:this.tagData(t),data:e};this.trigger("edit:beforeUpdate",i,{cloneData:!1}),this.state.editing=!1,delete e.__originalData,delete e.__originalHTML,t&&e[this.settings.tagTextProp]?(t=this.replaceTag(t,e),this.editTagToggleValidity(t,e),this.settings.a11y.focusableTags?t.focus():this.placeCaretAfterNode(t)):t&&this.removeTags(t),this.trigger("edit:updated",i),this.dropdown.hide(),this.settings.keepInvalidTags&&this.reCheckInvalidTags()},replaceTag(t,e){e&&e.value||(e=t.__tagifyTagData),e.__isValid&&1!=e.__isValid&&g(e,this.getInvalidTagAttrs(e,e.__isValid));var i=this.createTagElem(e);return t.parentNode.replaceChild(i,t),this.updateValueByDOMTags(),i},updateValueByDOMTags(){this.value.length=0,[].forEach.call(this.getTagElms(),(t=>{t.classList.contains(this.settings.classNames.tagNotAllowed.split(" ")[0])||this.value.push(this.tagData(t))})),this.update()},injectAtCaret(t,e){var i;return!(e=e||(null===(i=this.state.selection)||void 0===i?void 0:i.range))&&t?(this.appendMixTags(t),this):(f(t,e),this.setRangeAtStartEnd(!1,content),this.updateValueByDOMTags(),this.update(),this)},input:{set(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];var i=this.settings.dropdown.closeOnSelect;this.state.inputText=t,e&&(this.DOM.input.innerHTML=d(""+t)),!t&&i&&this.dropdown.hide.bind(this),this.input.autocomplete.suggest.call(this),this.input.validate.call(this)},raw(){return this.DOM.input.textContent},validate(){var t=!this.state.inputText||!0===this.validateTag({value:this.state.inputText});return this.DOM.input.classList.toggle(this.settings.classNames.inputInvalid,!t),t},normalize(t){var e=t||this.DOM.input,i=[];e.childNodes.forEach((t=>3==t.nodeType&&i.push(t.nodeValue))),i=i.join("\n");try{i=i.replace(/(?:\r\n|\r|\n)/g,this.settings.delimiters.source.charAt(0))}catch(t){}return i=i.replace(/\s/g," "),this.trim(i)},autocomplete:{suggest(t){if(this.settings.autoComplete.enabled){"string"==typeof(t=t||{})&&(t={value:t});var e=t.value?""+t.value:"",i=e.substr(0,this.state.inputText.length).toLowerCase(),s=e.substring(this.state.inputText.length);e&&this.state.inputText&&i==this.state.inputText.toLowerCase()?(this.DOM.input.setAttribute("data-suggest",s),this.state.inputSuggestion=t):(this.DOM.input.removeAttribute("data-suggest"),delete this.state.inputSuggestion)}},set(t){var e=this.DOM.input.getAttribute("data-suggest"),i=t||(e?this.state.inputText+e:null);return!!i&&("mix"==this.settings.mode?this.replaceTextWithNode(document.createTextNode(this.state.tag.prefix+i)):(this.input.set.call(this,i),this.setRangeAtStartEnd(!1,this.DOM.input)),this.input.autocomplete.suggest.call(this),this.dropdown.hide(),!0)}}},getTagIdx(t){return this.value.findIndex((e=>e.__tagId==(t||{}).__tagId))},getNodeIndex(t){var e=0;if(t)for(;t=t.previousElementSibling;)e++;return e},getTagElms(){for(var t=arguments.length,e=new Array(t),i=0;it?(e&&(t.__tagifyTagData=i?e:g({},t.__tagifyTagData||{},e)),t.__tagifyTagData):(console.warn("tag element doesn't exist",t,e),e),isTagDuplicate(t,e,i){var a=0;if("select"==this.settings.mode)return!1;for(let n of this.value){s(this.trim(""+t),n.value,e)&&i!=n.__tagId&&a++}return a},getTagIndexByValue(t){var e=[];return this.getTagElms().forEach(((i,a)=>{s(this.trim(i.textContent),t,this.settings.dropdown.caseSensitive)&&e.push(a)})),e},getTagElmByValue(t){var e=this.getTagIndexByValue(t)[0];return this.getTagElms()[e]},flashTag(t){t&&(t.classList.add(this.settings.classNames.tagFlash),setTimeout((()=>{t.classList.remove(this.settings.classNames.tagFlash)}),100))},isTagBlacklisted(t){return t=this.trim(t.toLowerCase()),this.settings.blacklist.filter((e=>(""+e).toLowerCase()==t)).length},isTagWhitelisted(t){return!!this.getWhitelistItem(t)},getWhitelistItem(t,e,i){e=e||"value";var a,n=this.settings;return(i=i||n.whitelist).some((i=>{var o="string"==typeof i?i:i[e]||i.value;if(s(o,t,n.dropdown.caseSensitive,n.trim))return a="string"==typeof i?{value:i}:i,!0})),a||"value"!=e||"value"==n.tagTextProp||(a=this.getWhitelistItem(t,n.tagTextProp,i)),a},validateTag(t){var e=this.settings,i="value"in t?"value":e.tagTextProp,s=this.trim(t[i]+"");return(t[i]+"").trim()?e.pattern&&e.pattern instanceof RegExp&&!e.pattern.test(s)?this.TEXTS.pattern:!e.duplicates&&this.isTagDuplicate(s,e.dropdown.caseSensitive,t.__tagId)?this.TEXTS.duplicate:this.isTagBlacklisted(s)||e.enforceWhitelist&&!this.isTagWhitelisted(s)?this.TEXTS.notAllowed:!e.validate||e.validate(t):this.TEXTS.empty},getInvalidTagAttrs(t,e){return{"aria-invalid":!0,class:`${t.class||""} ${this.settings.classNames.tagNotAllowed}`.trim(),title:e}},hasMaxTags(){return this.value.length>=this.settings.maxTags&&this.TEXTS.exceed},setReadonly(t,e){var i=this.settings;document.activeElement.blur(),i[e||"readonly"]=t,this.DOM.scope[(t?"set":"remove")+"Attribute"](e||"readonly",!0),this.setContentEditable(!t)},setContentEditable(t){this.settings.userInput&&(this.DOM.input.contentEditable=t,this.DOM.input.tabIndex=t?0:-1)},setDisabled(t){this.setReadonly(t,"disabled")},normalizeTags(t){var e=this.settings,i=e.whitelist,s=e.delimiters,a=e.mode,n=e.tagTextProp;e.enforceWhitelist;var o=[],r=!!i&&i[0]instanceof Object,l=Array.isArray(t),d=l&&t[0].value,h=t=>(t+"").split(s).filter((t=>t)).map((t=>({[n]:this.trim(t),value:this.trim(t)})));if("number"==typeof t&&(t=t.toString()),"string"==typeof t){if(!t.trim())return[];t=h(t)}else l&&(t=[].concat(...t.map((t=>t.value?t:h(t)))));return r&&!d&&(t.forEach((t=>{var e=o.map((t=>t.value)),i=this.dropdown.filterListItems.call(this,t[n],{exact:!0});this.settings.duplicates||(i=i.filter((t=>!e.includes(t.value))));var s=i.length>1?this.getWhitelistItem(t[n],n,i):i[0];s&&s instanceof Object?o.push(s):"mix"!=a&&(null==t.value&&(t.value=t[n]),o.push(t))})),o.length&&(t=o)),t},parseMixTags(t){var e=this.settings,i=e.mixTagsInterpolator,s=e.duplicates,a=e.transformTag,n=e.enforceWhitelist,o=e.maxTags,r=e.tagTextProp,l=[];return t=t.split(i[0]).map(((t,e)=>{var d,h,g,p=t.split(i[1]),c=p[0],u=l.length==o;try{if(c==+c)throw Error;h=JSON.parse(c)}catch(t){h=this.normalizeTags(c)[0]||{value:c}}if(a.call(this,h),u||!(p.length>1)||n&&!this.isTagWhitelisted(h.value)||!s&&this.isTagDuplicate(h.value)){if(t)return e?i[0]+t:t}else h[d=h[r]?r:"value"]=this.trim(h[d]),g=this.createTagElem(h),l.push(h),g.classList.add(this.settings.classNames.tagNoAnimation),p[0]=g.outerHTML,this.value.push(h);return p.join("")})).join(""),this.DOM.input.innerHTML=t,this.DOM.input.appendChild(document.createTextNode("")),this.DOM.input.normalize(),this.getTagElms().forEach(((t,e)=>this.tagData(t,l[e]))),this.update({withoutChangeEvent:!0}),t},replaceTextWithNode(t,e){if(this.state.tag||e){e=e||this.state.tag.prefix+this.state.tag.value;var i,s,a=this.state.selection||window.getSelection(),n=a.anchorNode,o=this.state.tag.delimiters?this.state.tag.delimiters.length:0;return n.splitText(a.anchorOffset-o),-1==(i=n.nodeValue.lastIndexOf(e))?!0:(s=n.splitText(i),t&&n.parentNode.replaceChild(t,s),!0)}},selectTag(t,e){var i=this.settings;if(!i.enforceWhitelist||this.isTagWhitelisted(e.value)){this.input.set.call(this,e[i.tagTextProp]||e.value,!0),this.state.actions.selectOption&&setTimeout((()=>this.setRangeAtStartEnd(!1,this.DOM.input)));var s=this.getLastTag();return s?this.replaceTag(s,e):this.appendTag(t),this.value[0]=e,this.update(),this.trigger("add",{tag:t,data:e}),[t]}},addEmptyTag(t){var e=g({value:""},t||{}),i=this.createTagElem(e);this.tagData(i,e),this.appendTag(i),this.editTag(i,{skipValidation:!0})},addTags(t,e,i){var s=[],a=this.settings,n=[],o=document.createDocumentFragment();if(i=i||a.skipInvalid,!t||0==t.length)return s;switch(t=this.normalizeTags(t),a.mode){case"mix":return this.addMixTags(t);case"select":e=!1,this.removeAllTags()}return this.DOM.input.removeAttribute("style"),t.forEach((t=>{var e,r={},l=Object.assign({},t,{value:t.value+""});if(t=Object.assign({},l),a.transformTag.call(this,t),t.__isValid=this.hasMaxTags()||this.validateTag(t),!0!==t.__isValid){if(i)return;if(g(r,this.getInvalidTagAttrs(t,t.__isValid),{__preInvalidData:l}),t.__isValid==this.TEXTS.duplicate&&this.flashTag(this.getTagElmByValue(t.value)),!a.createInvalidTags)return void n.push(t.value)}if("readonly"in t&&(t.readonly?r["aria-readonly"]=!0:delete t.readonly),e=this.createTagElem(t,r),s.push(e),"select"==a.mode)return this.selectTag(e,t);o.appendChild(e),t.__isValid&&!0===t.__isValid?(this.value.push(t),this.trigger("add",{tag:e,index:this.value.length-1,data:t})):(this.trigger("invalid",{data:t,index:this.value.length,tag:e,message:t.__isValid}),a.keepInvalidTags||setTimeout((()=>this.removeTags(e,!0)),1e3)),this.dropdown.position()})),this.appendTag(o),this.update(),t.length&&e&&(this.input.set.call(this,a.createInvalidTags?"":n.join(a._delimiters)),this.setRangeAtStartEnd(!1,this.DOM.input)),a.dropdown.enabled&&this.dropdown.refilter(),s},addMixTags(t){if((t=this.normalizeTags(t))[0].prefix||this.state.tag)return this.prefixedTextToTag(t[0]);"string"==typeof t&&(t=[{value:t}]),this.state.selection;var e=document.createDocumentFragment();return t.forEach((t=>{var i=this.createTagElem(t);e.appendChild(i),this.insertAfterTag(i)})),this.appendMixTags(e),e},appendMixTags(t){var e=!!this.state.selection;e?this.injectAtCaret(t):(this.DOM.input.focus(),(e=this.setStateSelection()).range.setStart(this.DOM.input,e.range.endOffset),e.range.setEnd(this.DOM.input,e.range.endOffset),this.DOM.input.appendChild(t),this.updateValueByDOMTags(),this.update())},prefixedTextToTag(t){var e,i=this.settings,s=this.state.tag.delimiters;if(i.transformTag.call(this,t),t.prefix=t.prefix||this.state.tag?this.state.tag.prefix:(i.pattern.source||i.pattern)[0],e=this.createTagElem(t),this.replaceTextWithNode(e)||this.DOM.input.appendChild(e),setTimeout((()=>e.classList.add(this.settings.classNames.tagNoAnimation)),300),this.value.push(t),this.update(),!s){var a=this.insertAfterTag(e)||e;this.placeCaretAfterNode(a)}return this.state.tag=null,this.trigger("add",g({},{tag:e},{data:t})),e},appendTag(t){var e=this.DOM,i=e.input;i===e.input?e.scope.insertBefore(t,i):e.scope.appendChild(t)},createTagElem(t,i){t.__tagId=m();var s,a=g({},t,e({value:d(t.value+"")},i));return function(t){for(var e,i=document.createNodeIterator(t,NodeFilter.SHOW_TEXT,null,!1);e=i.nextNode();)e.textContent.trim()||e.parentNode.removeChild(e)}(s=this.parseTemplate("tag",[a,this])),this.tagData(s,t),s},reCheckInvalidTags(){var t=this.settings;this.getTagElms(t.classNames.tagNotAllowed).forEach(((e,i)=>{var s=this.tagData(e),a=this.hasMaxTags(),n=this.validateTag(s),o=!0===n&&!a;if("select"==t.mode&&this.toggleScopeValidation(n),o)return s=s.__preInvalidData?s.__preInvalidData:{value:s.value},this.replaceTag(e,s);e.title=a||n}))},removeTags(t,e,i){var s,a=this.settings;if(t=t&&t instanceof HTMLElement?[t]:t instanceof Array?t:t?[t]:[this.getLastTag()],s=t.reduce(((t,e)=>{e&&"string"==typeof e&&(e=this.getTagElmByValue(e));var i=this.tagData(e);return e&&i&&!i.readonly&&t.push({node:e,idx:this.getTagIdx(i),data:this.tagData(e,{__removed:!0})}),t}),[]),i="number"==typeof i?i:this.CSSVars.tagHideTransition,"select"==a.mode&&(i=0,this.input.set.call(this)),1==s.length&&"select"!=a.mode&&s[0].node.classList.contains(a.classNames.tagNotAllowed)&&(e=!0),s.length)return a.hooks.beforeRemoveTag(s,{tagify:this}).then((()=>{function t(t){t.node.parentNode&&(t.node.parentNode.removeChild(t.node),e?a.keepInvalidTags&&this.trigger("remove",{tag:t.node,index:t.idx}):(this.trigger("remove",{tag:t.node,index:t.idx,data:t.data}),this.dropdown.refilter(),this.dropdown.position(),this.DOM.input.normalize(),a.keepInvalidTags&&this.reCheckInvalidTags()))}i&&i>10&&1==s.length?function(e){e.node.style.width=parseFloat(window.getComputedStyle(e.node).width)+"px",document.body.clientTop,e.node.classList.add(a.classNames.tagHide),setTimeout(t.bind(this),i,e)}.call(this,s[0]):s.forEach(t.bind(this)),e||(this.removeTagsFromValue(s.map((t=>t.node))),this.update(),"select"==a.mode&&this.setContentEditable(!0))})).catch((t=>{}))},removeTagsFromDOM(){[].slice.call(this.getTagElms()).forEach((t=>t.parentNode.removeChild(t)))},removeTagsFromValue(t){(t=Array.isArray(t)?t:[t]).forEach((t=>{var e=this.tagData(t),i=this.getTagIdx(e);i>-1&&this.value.splice(i,1)}))},removeAllTags(t){t=t||{},this.value=[],"mix"==this.settings.mode?this.DOM.input.innerHTML="":this.removeTagsFromDOM(),this.dropdown.refilter(),this.dropdown.position(),this.state.dropdown.visible&&setTimeout((()=>{this.DOM.input.focus()})),"select"==this.settings.mode&&(this.input.set.call(this),this.setContentEditable(!0)),this.update(t)},postUpdate(){var t,e,i=this.settings,s=i.classNames,a="mix"==i.mode?i.mixMode.integrated?this.DOM.input.textContent:this.DOM.originalInput.value.trim():this.value.length+this.input.raw.call(this).length;(this.toggleClass(s.hasMaxTags,this.value.length>=i.maxTags),this.toggleClass(s.hasNoTags,!this.value.length),this.toggleClass(s.empty,!a),"select"==i.mode)&&this.toggleScopeValidation(null===(t=this.value)||void 0===t||null===(e=t[0])||void 0===e?void 0:e.__isValid)},setOriginalInputValue(t){var e=this.DOM.originalInput;this.settings.mixMode.integrated||(e.value=t,e.tagifyValue=e.value,this.setPersistedData(t,"value"))},update(t){clearTimeout(this.debouncedUpdateTimeout),this.debouncedUpdateTimeout=setTimeout(function(){var e=this.getInputValue();this.setOriginalInputValue(e),this.postUpdate(),this.settings.onChangeAfterBlur&&(t||{}).withoutChangeEvent||this.state.blockChangeEvent||this.triggerChangeEvent()}.bind(this),100)},getInputValue(){var t=this.getCleanValue();return"mix"==this.settings.mode?this.getMixedTagsAsString(t):t.length?this.settings.originalInputValueFormat?this.settings.originalInputValueFormat(t):JSON.stringify(t):""},getCleanValue(t){return a(t||this.value,this.dataProps)},getMixedTagsAsString(){var t="",e=this,i=this.settings,s=i.originalInputValueFormat||JSON.stringify,a=i.mixTagsInterpolator;return function i(o){o.childNodes.forEach((o=>{if(1==o.nodeType){const r=e.tagData(o);if("BR"==o.tagName&&(t+="\r\n"),r&&v.call(e,o)){if(r.__removed)return;t+=a[0]+s(n(r,e.dataProps))+a[1]}else o.getAttribute("style")||["B","I","U"].includes(o.tagName)?t+=o.textContent:"DIV"!=o.tagName&&"P"!=o.tagName||(t+="\r\n",i(o))}else t+=o.textContent}))}(this.DOM.input),t}},I.prototype.removeTag=I.prototype.removeTags,I})); +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Tagify=e()}(this,(function(){"use strict";function t(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(t);e&&(s=s.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,s)}return i}function e(e){for(var s=1;s(t=""+t,e=""+e,s&&(t=t.trim(),e=e.trim()),i?t==e:t.toLowerCase()==e.toLowerCase()),a=(t,e)=>t&&Array.isArray(t)&&t.map((t=>n(t,e)));function n(t,e){var i,s={};for(i in t)e.indexOf(i)<0&&(s[i]=t[i]);return s}function o(t){var e=document.createElement("div");return t.replace(/\&#?[0-9a-z]+;/gi,(function(t){return e.innerHTML=t,e.innerText}))}function r(t){return(new DOMParser).parseFromString(t.trim(),"text/html").body.firstElementChild}function l(t,e){for(e=e||"previous";t=t[e+"Sibling"];)if(3==t.nodeType)return t}function d(t){return"string"==typeof t?t.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/`|'/g,"'"):t}function h(t){var e=Object.prototype.toString.call(t).split(" ")[1].slice(0,-1);return t===Object(t)&&"Array"!=e&&"Function"!=e&&"RegExp"!=e&&"HTMLUnknownElement"!=e}function g(t,e,i){function s(t,e){for(var i in e)if(e.hasOwnProperty(i)){if(h(e[i])){h(t[i])?s(t[i],e[i]):t[i]=Object.assign({},e[i]);continue}if(Array.isArray(e[i])){t[i]=Object.assign([],e[i]);continue}t[i]=e[i]}}return t instanceof Object||(t={}),s(t,e),i&&s(t,i),t}function p(){const t=[],e={};for(let i of arguments)for(let s of i)h(s)?e[s.value]||(t.push(s),e[s.value]=1):t.includes(s)||t.push(s);return t}function c(t){return String.prototype.normalize?"string"==typeof t?t.normalize("NFD").replace(/[\u0300-\u036f]/g,""):void 0:t}var u=()=>/(?=.*chrome)(?=.*android)/i.test(navigator.userAgent);function m(){return([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,(t=>(t^crypto.getRandomValues(new Uint8Array(1))[0]&15>>t/4).toString(16)))}function v(t){return t&&t.classList&&t.classList.contains(this.settings.classNames.tag)}function f(t,e){var i=window.getSelection();return e=e||i.getRangeAt(0),"string"==typeof t&&(t=document.createTextNode(t)),e&&(e.deleteContents(),e.insertNode(t)),t}function T(t,e,i){return t?(e&&(t.__tagifyTagData=i?e:g({},t.__tagifyTagData||{},e)),t.__tagifyTagData):(console.warn("tag element doesn't exist",t,e),e)}var w={delimiters:",",pattern:null,tagTextProp:"value",maxTags:1/0,callbacks:{},addTagOnBlur:!0,onChangeAfterBlur:!0,duplicates:!1,whitelist:[],blacklist:[],enforceWhitelist:!1,userInput:!0,keepInvalidTags:!1,createInvalidTags:!0,mixTagsAllowedAfter:/,|\.|\:|\s/,mixTagsInterpolator:["[[","]]"],backspace:!0,skipInvalid:!1,pasteAsTags:!0,editTags:{clicks:2,keepInvalid:!0},transformTag:()=>{},trim:!0,a11y:{focusableTags:!1},mixMode:{insertAfterTag:" "},autoComplete:{enabled:!0,rightKey:!1},classNames:{namespace:"tagify",mixMode:"tagify--mix",selectMode:"tagify--select",input:"tagify__input",focus:"tagify--focus",tagNoAnimation:"tagify--noAnim",tagInvalid:"tagify--invalid",tagNotAllowed:"tagify--notAllowed",scopeLoading:"tagify--loading",hasMaxTags:"tagify--hasMaxTags",hasNoTags:"tagify--noTags",empty:"tagify--empty",inputInvalid:"tagify__input--invalid",dropdown:"tagify__dropdown",dropdownWrapper:"tagify__dropdown__wrapper",dropdownHeader:"tagify__dropdown__header",dropdownFooter:"tagify__dropdown__footer",dropdownItem:"tagify__dropdown__item",dropdownItemActive:"tagify__dropdown__item--active",dropdownItemHidden:"tagify__dropdown__item--hidden",dropdownInital:"tagify__dropdown--initial",tag:"tagify__tag",tagText:"tagify__tag-text",tagX:"tagify__tag__removeBtn",tagLoading:"tagify__tag--loading",tagEditing:"tagify__tag--editable",tagFlash:"tagify__tag--flash",tagHide:"tagify__tag--hide"},dropdown:{classname:"",enabled:2,maxItems:10,searchKeys:["value","searchBy"],fuzzySearch:!0,caseSensitive:!1,accentedSearch:!0,includeSelectedTags:!1,highlightFirst:!1,closeOnSelect:!0,clearOnSelect:!0,position:"all",appendTarget:null},hooks:{beforeRemoveTag:()=>Promise.resolve(),beforePaste:()=>Promise.resolve(),suggestionClick:()=>Promise.resolve()}};function b(){this.dropdown={};for(let t in this._dropdown)this.dropdown[t]="function"==typeof this._dropdown[t]?this._dropdown[t].bind(this):this._dropdown[t];this.dropdown.refs()}var y={refs(){this.DOM.dropdown=this.parseTemplate("dropdown",[this.settings]),this.DOM.dropdown.content=this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-wrapper']")},getHeaderRef(){return this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-header']")},getFooterRef(){return this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-footer']")},getAllSuggestionsRefs(){return[...this.DOM.dropdown.content.querySelectorAll(this.settings.classNames.dropdownItemSelector)]},show(t){var e,i,a,n=this.settings,o="mix"==n.mode&&!n.enforceWhitelist,r=!n.whitelist||!n.whitelist.length,l="manual"==n.dropdown.position;if(t=void 0===t?this.state.inputText:t,!(r&&!o&&!n.templates.dropdownItemNoMatch||!1===n.dropdown.enable||this.state.isLoading||this.settings.readonly)){if(clearTimeout(this.dropdownHide__bindEventsTimeout),this.suggestedListItems=this.dropdown.filterListItems(t),t&&!this.suggestedListItems.length&&(this.trigger("dropdown:noMatch",t),n.templates.dropdownItemNoMatch&&(a=n.templates.dropdownItemNoMatch.call(this,{value:t}))),!a){if(this.suggestedListItems.length)t&&o&&!this.state.editing.scope&&!s(this.suggestedListItems[0].value,t)&&this.suggestedListItems.unshift({value:t});else{if(!t||!o||this.state.editing.scope)return this.input.autocomplete.suggest.call(this),void this.dropdown.hide();this.suggestedListItems=[{value:t}]}i=""+(h(e=this.suggestedListItems[0])?e.value:e),n.autoComplete&&i&&0==i.indexOf(t)&&this.input.autocomplete.suggest.call(this,e)}this.dropdown.fill(a),n.dropdown.highlightFirst&&this.dropdown.highlightOption(this.DOM.dropdown.content.querySelector(n.classNames.dropdownItemSelector)),this.state.dropdown.visible||setTimeout(this.dropdown.events.binding.bind(this)),this.state.dropdown.visible=t||!0,this.state.dropdown.query=t,this.setStateSelection(),l||setTimeout((()=>{this.dropdown.position(),this.dropdown.render()})),setTimeout((()=>{this.trigger("dropdown:show",this.DOM.dropdown)}))}},hide(t){var e=this.DOM,i=e.scope,s=e.dropdown,a="manual"==this.settings.dropdown.position&&!t;if(s&&document.body.contains(s)&&!a)return window.removeEventListener("resize",this.dropdown.position),this.dropdown.events.binding.call(this,!1),i.setAttribute("aria-expanded",!1),s.parentNode.removeChild(s),setTimeout((()=>{this.state.dropdown.visible=!1}),100),this.state.dropdown.query=this.state.ddItemData=this.state.ddItemElm=this.state.selection=null,this.state.tag&&this.state.tag.value.length&&(this.state.flaggedTags[this.state.tag.baseOffset]=this.state.tag),this.trigger("dropdown:hide",s),this},toggle(t){this.dropdown[this.state.dropdown.visible&&!t?"hide":"show"]()},render(){var t,e,i,s=(t=this.DOM.dropdown,(i=t.cloneNode(!0)).style.cssText="position:fixed; top:-9999px; opacity:0",document.body.appendChild(i),e=i.clientHeight,i.parentNode.removeChild(i),e),a=this.settings;return"number"==typeof a.dropdown.enabled&&a.dropdown.enabled>=0?(this.DOM.scope.setAttribute("aria-expanded",!0),document.body.contains(this.DOM.dropdown)||(this.DOM.dropdown.classList.add(a.classNames.dropdownInital),this.dropdown.position(s),a.dropdown.appendTarget.appendChild(this.DOM.dropdown),setTimeout((()=>this.DOM.dropdown.classList.remove(a.classNames.dropdownInital)))),this):this},fill(t){t="string"==typeof t?t:this.dropdown.createListHTML(t||this.suggestedListItems);var e,i=this.settings.templates.dropdownContent.call(this,t);this.DOM.dropdown.content.innerHTML=(e=i)?e.replace(/\>[\r\n ]+\<").replace(/(<.*?>)|\s+/g,((t,e)=>e||" ")):""},fillHeaderFooter(){this.settings.templates;var t=this.dropdown.filterListItems(this.state.dropdown.query),e=this.parseTemplate("dropdownHeader",[t]),i=this.parseTemplate("dropdownFooter",[t]),s=this.dropdown.getHeaderRef(),a=this.dropdown.getFooterRef();e&&(null==s||s.parentNode.replaceChild(e,s)),i&&(null==a||a.parentNode.replaceChild(i,a))},refilter(t){t=t||this.state.dropdown.query||"",this.suggestedListItems=this.dropdown.filterListItems(t),this.dropdown.fill(),this.suggestedListItems.length||this.dropdown.hide(),this.trigger("dropdown:updated",this.DOM.dropdown)},position(t){var e=this.settings.dropdown;if("manual"!=e.position){var i,s,a,n,o,r,l=this.DOM.dropdown,d=e.placeAbove,h=e.appendTarget===document.body,g=h?window.pageYOffset:e.appendTarget.scrollTop,p=document.fullscreenElement||document.webkitFullscreenElement||document.documentElement,c=p.clientHeight,u=Math.max(p.clientWidth||0,window.innerWidth||0)>480?e.position:"all",m=this.DOM["input"==u?"input":"scope"];if(t=t||l.clientHeight,this.state.dropdown.visible){if("text"==u?(a=(i=function(){const t=document.getSelection();if(t.rangeCount){const e=t.getRangeAt(0),i=e.startContainer,s=e.startOffset;let a,n;if(s>0)return n=document.createRange(),n.setStart(i,s-1),n.setEnd(i,s),a=n.getBoundingClientRect(),{left:a.right,top:a.top,bottom:a.bottom};if(i.getBoundingClientRect)return i.getBoundingClientRect()}return{left:-9999,top:-9999}}()).bottom,s=i.top,n=i.left,o="auto"):(r=function(t){for(var e=0,i=0;t&&t!=p;)e+=t.offsetLeft||0,i+=t.offsetTop||0,t=t.parentNode;return{left:e,top:i}}(e.appendTarget),s=(i=m.getBoundingClientRect()).top-r.top,a=i.bottom-1-r.top,n=i.left-r.left,o=i.width+"px"),!h){let t=function(){for(var t=0,i=e.appendTarget.parentNode;i;)t+=i.scrollTop||0,i=i.parentNode;return t}();s+=t,a+=t}s=Math.floor(s),a=Math.ceil(a),d=void 0===d?c-i.bottom0&&void 0!==arguments[0])||arguments[0];var e=this.dropdown.events.callbacks,i=this.listeners.dropdown=this.listeners.dropdown||{position:this.dropdown.position.bind(this,null),onKeyDown:e.onKeyDown.bind(this),onMouseOver:e.onMouseOver.bind(this),onMouseLeave:e.onMouseLeave.bind(this),onClick:e.onClick.bind(this),onScroll:e.onScroll.bind(this)},s=t?"addEventListener":"removeEventListener";"manual"!=this.settings.dropdown.position&&(document[s]("scroll",i.position,!0),window[s]("resize",i.position),window[s]("keydown",i.onKeyDown)),this.DOM.dropdown[s]("mouseover",i.onMouseOver),this.DOM.dropdown[s]("mouseleave",i.onMouseLeave),this.DOM.dropdown[s]("mousedown",i.onClick),this.DOM.dropdown.content[s]("scroll",i.onScroll)},callbacks:{onKeyDown(t){if(this.state.hasFocus&&!this.state.composing){var e=this.DOM.dropdown.querySelector(this.settings.classNames.dropdownItemActiveSelector),i=this.dropdown.getSuggestionDataByNode(e);switch(t.key){case"ArrowDown":case"ArrowUp":case"Down":case"Up":t.preventDefault();var s=this.dropdown.getAllSuggestionsRefs(),a="ArrowUp"==t.key||"Up"==t.key;e&&(e=this.dropdown.getNextOrPrevOption(e,!a)),e&&e.matches(this.settings.classNames.dropdownItemSelector)||(e=s[a?s.length-1:0]),i=this.dropdown.getSuggestionDataByNode(e),this.dropdown.highlightOption(e,!0);break;case"Escape":case"Esc":this.dropdown.hide();break;case"ArrowRight":if(this.state.actions.ArrowLeft)return;case"Tab":if("mix"!=this.settings.mode&&e&&!this.settings.autoComplete.rightKey&&!this.state.editing){t.preventDefault();var n=this.dropdown.getMappedValue(i);return this.input.autocomplete.set.call(this,n),!1}return!0;case"Enter":t.preventDefault(),this.settings.hooks.suggestionClick(t,{tagify:this,tagData:i,suggestionElm:e}).then((()=>{if(e)return this.dropdown.selectOption(e),e=this.dropdown.getNextOrPrevOption(e,!a),void this.dropdown.highlightOption(e);this.dropdown.hide(),"mix"!=this.settings.mode&&this.addTags(this.state.inputText.trim(),!0)})).catch((t=>t));break;case"Backspace":{if("mix"==this.settings.mode||this.state.editing.scope)return;const t=this.input.raw.call(this);""!=t&&8203!=t.charCodeAt(0)||(!0===this.settings.backspace?this.removeTags():"edit"==this.settings.backspace&&setTimeout(this.editTag.bind(this),0))}}}},onMouseOver(t){var e=t.target.closest(this.settings.classNames.dropdownItemSelector);e&&this.dropdown.highlightOption(e)},onMouseLeave(t){this.dropdown.highlightOption()},onClick(t){if(0==t.button&&t.target!=this.DOM.dropdown&&t.target!=this.DOM.dropdown.content){var e=t.target.closest(this.settings.classNames.dropdownItemSelector),i=this.dropdown.getSuggestionDataByNode(e);this.state.actions.selectOption=!0,setTimeout((()=>this.state.actions.selectOption=!1),50),this.settings.hooks.suggestionClick(t,{tagify:this,tagData:i,suggestionElm:e}).then((()=>{e?this.dropdown.selectOption(e,t):this.dropdown.hide()})).catch((t=>console.warn(t)))}},onScroll(t){var e=t.target,i=e.scrollTop/(e.scrollHeight-e.parentNode.clientHeight)*100;this.trigger("dropdown:scroll",{percentage:Math.round(i)})}}},getSuggestionDataByNode(t){var e=t&&t.getAttribute("value");return this.suggestedListItems.find((t=>t.value==e))||null},getNextOrPrevOption(t){let e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];var i=this.dropdown.getAllSuggestionsRefs(),s=i.findIndex((e=>e===t));return e?i[s+1]:i[s-1]},highlightOption(t,e){var i,s=this.settings.classNames.dropdownItemActive;if(this.state.ddItemElm&&(this.state.ddItemElm.classList.remove(s),this.state.ddItemElm.removeAttribute("aria-selected")),!t)return this.state.ddItemData=null,this.state.ddItemElm=null,void this.input.autocomplete.suggest.call(this);i=this.dropdown.getSuggestionDataByNode(t),this.state.ddItemData=i,this.state.ddItemElm=t,t.classList.add(s),t.setAttribute("aria-selected",!0),e&&(t.parentNode.scrollTop=t.clientHeight+t.offsetTop-t.parentNode.clientHeight),this.settings.autoComplete&&(this.input.autocomplete.suggest.call(this,i),this.dropdown.position())},selectOption(t,e){var i=this.settings.dropdown,s=i.clearOnSelect,a=i.closeOnSelect;if(!t)return this.addTags(this.state.inputText,!0),void(a&&this.dropdown.hide());e=e||{};var n=t.getAttribute("value"),o="noMatch"==n,r=this.suggestedListItems.find((t=>(t.value||t)==n));this.trigger("dropdown:select",{data:r,elm:t,event:e}),n&&(r||o)?(this.state.editing?this.onEditTagDone(null,g({__isValid:!0},this.normalizeTags([r])[0])):this["mix"==this.settings.mode?"addMixTags":"addTags"]([r||this.input.raw.call(this)],s),this.DOM.input.parentNode&&(setTimeout((()=>{this.DOM.input.focus(),this.toggleFocusClass(!0),this.setRangeAtStartEnd(!1,this.DOM.input)})),a&&setTimeout(this.dropdown.hide.bind(this)),t.addEventListener("transitionend",(()=>{this.dropdown.fillHeaderFooter(),setTimeout((()=>t.remove()),100)}),{once:!0}),t.classList.add(this.settings.classNames.dropdownItemHidden))):a&&setTimeout(this.dropdown.hide.bind(this))},selectAll(t){this.suggestedListItems.length=0,this.dropdown.hide(),this.dropdown.filterListItems("");var e=this.dropdown.filterListItems("");return t||(e=this.state.dropdown.suggestions),this.addTags(e,!0),this},filterListItems(t,e){var i,s,a,n,o,r=this.settings,l=r.dropdown,d=(e=e||{},[]),g=[],p=r.whitelist,u=l.maxItems>=0?l.maxItems:1/0,m=l.searchKeys,v=0;if(!(t="select"==r.mode&&this.value.length&&this.value[0][r.tagTextProp]==t?"":t)||!m.length)return d=l.includeSelectedTags?p:p.filter((t=>!this.isTagDuplicate(h(t)?t.value:t))),this.state.dropdown.suggestions=d,d.slice(0,u);function f(t,e){return e.toLowerCase().split(" ").every((e=>t.includes(e.toLowerCase())))}for(o=l.caseSensitive?""+t:(""+t).toLowerCase();vm.includes(t)))?["value"]:m;l.fuzzySearch&&!e.exact?(a=u.reduce(((t,e)=>t+" "+(i[e]||"")),"").toLowerCase().trim(),l.accentedSearch&&(a=c(a),o=c(o)),t=0==a.indexOf(o),r=a===o,s=f(a,o)):(t=!0,s=u.some((t=>{var s=""+(i[t]||"");return l.accentedSearch&&(s=c(s),o=c(o)),l.caseSensitive||(s=s.toLowerCase()),r=s===o,e.exact?s===o:0==s.indexOf(o)}))),n=!l.includeSelectedTags&&this.isTagDuplicate(h(i)?i.value:i),s&&!n&&(r&&t?g.push(i):"startsWith"==l.sortby&&t?d.unshift(i):d.push(i))}return this.state.dropdown.suggestions=g.concat(d),"function"==typeof l.sortby?l.sortby(g.concat(d),o):g.concat(d).slice(0,u)},getMappedValue(t){var e=this.settings.dropdown.mapValueTo;return e?"function"==typeof e?e(t):t[e]||t.value:t.value},createListHTML(t){return g([],t).map(((t,i)=>{"string"!=typeof t&&"number"!=typeof t||(t={value:t});var s=this.dropdown.getMappedValue(t);return s="string"==typeof s?d(s):s,this.settings.templates.dropdownItem.apply(this,[e(e({},t),{},{mappedValue:s}),this])})).join("")}};const x="@yaireo/tagify/";var O,D={empty:"empty",exceed:"number of tags exceeded",pattern:"pattern mismatch",duplicate:"already exists",notAllowed:"not allowed"},M={wrapper:(t,e)=>`\n \n ​\n `,tag(t,e){let i=e.settings;return`\n \n
\n ${t[i.tagTextProp]||t.value}\n
\n
`},dropdown(t){var e=t.dropdown,i="manual"==e.position,s=`${t.classNames.dropdown}`;return`
\n
\n
`},dropdownContent(t){var e=this.settings,i=this.state.dropdown.suggestions;return`\n ${e.templates.dropdownHeader.call(this,i)}\n ${t}\n ${e.templates.dropdownFooter.call(this,i)}\n `},dropdownItem(t){return`
${t.mappedValue||t.value}
`},dropdownHeader(t){return`
`},dropdownFooter(t){var e=t.length-this.settings.dropdown.maxItems;return e>0?`
\n ${e} more items. Refine your search.\n
`:""},dropdownItemNoMatch:null};var I={customBinding(){this.customEventsList.forEach((t=>{this.on(t,this.settings.callbacks[t])}))},binding(){let t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];var e,i=this.events.callbacks,s=t?"addEventListener":"removeEventListener";if(!this.state.mainEvents||!t){for(var a in this.state.mainEvents=t,t&&!this.listeners.main&&(this.events.bindGlobal.call(this),this.settings.isJQueryPlugin&&jQuery(this.DOM.originalInput).on("tagify.removeAllTags",this.removeAllTags.bind(this))),e=this.listeners.main=this.listeners.main||{focus:["input",i.onFocusBlur.bind(this)],keydown:["input",i.onKeydown.bind(this)],click:["scope",i.onClickScope.bind(this)],dblclick:["scope",i.onDoubleClickScope.bind(this)],paste:["input",i.onPaste.bind(this)],drop:["input",i.onDrop.bind(this)],compositionstart:["input",i.onCompositionStart.bind(this)],compositionend:["input",i.onCompositionEnd.bind(this)]})this.DOM[e[a][0]][s](a,e[a][1]);clearInterval(this.listeners.main.originalInputValueObserverInterval),this.listeners.main.originalInputValueObserverInterval=setInterval(i.observeOriginalInputValue.bind(this),500);var n=this.listeners.main.inputMutationObserver||new MutationObserver(i.onInputDOMChange.bind(this));n&&n.disconnect(),"mix"==this.settings.mode&&n.observe(this.DOM.input,{childList:!0})}},bindGlobal(t){var e,i=this.events.callbacks,s=t?"removeEventListener":"addEventListener";if(t||!this.listeners.global)for(e of(this.listeners.global=this.listeners&&this.listeners.global||[{type:this.isIE?"keydown":"input",target:this.DOM.input,cb:i[this.isIE?"onInputIE":"onInput"].bind(this)},{type:"keydown",target:window,cb:i.onWindowKeyDown.bind(this)},{type:"blur",target:this.DOM.input,cb:i.onFocusBlur.bind(this)}],this.listeners.global))e.target[s](e.type,e.cb)},unbindGlobal(){this.events.bindGlobal.call(this,!0)},callbacks:{onFocusBlur(t){var e,i,s=this.settings,a=t.target?this.trim(t.target.textContent):"",n=null===(e=this.value)||void 0===e||null===(i=e[0])||void 0===i?void 0:i[s.tagTextProp],o=t.type,r=s.dropdown.enabled>=0,l={relatedTarget:t.relatedTarget},d=this.state.actions.selectOption&&(r||!s.dropdown.closeOnSelect),h=this.state.actions.addNew&&r,g=t.relatedTarget&&v.call(this,t.relatedTarget)&&this.DOM.scope.contains(t.relatedTarget);if("blur"==o){if(t.relatedTarget===this.DOM.scope)return this.dropdown.hide(),void this.DOM.input.focus();this.postUpdate(),s.onChangeAfterBlur&&this.triggerChangeEvent()}if(!d&&!h)if(this.state.hasFocus="focus"==o&&+new Date,this.toggleFocusClass(this.state.hasFocus),"mix"!=s.mode){if("focus"==o)return this.trigger("focus",l),void(0!==s.dropdown.enabled&&s.userInput||this.dropdown.show(this.value.length?"":void 0));"blur"==o&&(this.trigger("blur",l),this.loading(!1),"select"==s.mode&&(g&&(this.removeTags(),a=""),n===a&&(a="")),a&&!this.state.actions.selectOption&&s.addTagOnBlur&&this.addTags(a,!0)),this.DOM.input.removeAttribute("style"),this.dropdown.hide()}else"focus"==o?this.trigger("focus",l):"blur"==t.type&&(this.trigger("blur",l),this.loading(!1),this.dropdown.hide(),this.state.dropdown.visible=void 0,this.setStateSelection())},onCompositionStart(t){this.state.composing=!0},onCompositionEnd(t){this.state.composing=!1},onWindowKeyDown(t){var e,i=document.activeElement;if(v.call(this,i)&&this.DOM.scope.contains(document.activeElement))switch(e=i.nextElementSibling,t.key){case"Backspace":this.settings.readonly||(this.removeTags(i),(e||this.DOM.input).focus());break;case"Enter":setTimeout(this.editTag.bind(this),0,i)}},onKeydown(t){var e=this.settings;if(!this.state.composing&&e.userInput){"select"==e.mode&&e.enforceWhitelist&&this.value.length&&"Tab"!=t.key&&t.preventDefault();var i=this.trim(t.target.textContent);if(this.trigger("keydown",{event:t}),"mix"==e.mode){switch(t.key){case"Left":case"ArrowLeft":this.state.actions.ArrowLeft=!0;break;case"Delete":case"Backspace":if(this.state.editing)return;var s,a,n,r=document.getSelection(),d="Delete"==t.key&&r.anchorOffset==(r.anchorNode.length||0),h=r.anchorNode.previousSibling,g=1==r.anchorNode.nodeType||!r.anchorOffset&&h&&1==h.nodeType&&r.anchorNode.previousSibling,p=o(this.DOM.input.innerHTML),c=this.getTagElms();if("edit"==e.backspace&&g)return s=1==r.anchorNode.nodeType?null:r.anchorNode.previousElementSibling,setTimeout(this.editTag.bind(this),0,s),void t.preventDefault();if(u()&&g)return n=l(g),g.hasAttribute("readonly")||g.remove(),this.DOM.input.focus(),void setTimeout((()=>{this.placeCaretAfterNode(n),this.DOM.input.click()}));if("BR"==r.anchorNode.nodeName)return;if((d||g)&&1==r.anchorNode.nodeType?a=0==r.anchorOffset?d?c[0]:null:c[Math.min(c.length,r.anchorOffset)-1]:d?a=r.anchorNode.nextElementSibling:g&&(a=g),3==r.anchorNode.nodeType&&!r.anchorNode.nodeValue&&r.anchorNode.previousElementSibling&&t.preventDefault(),(g||d)&&!e.backspace)return void t.preventDefault();if("Range"!=r.type&&!r.anchorOffset&&r.anchorNode==this.DOM.input&&"Delete"!=t.key)return void t.preventDefault();if("Range"!=r.type&&a&&a.hasAttribute("readonly"))return void this.placeCaretAfterNode(l(a));clearTimeout(O),O=setTimeout((()=>{var t=document.getSelection(),e=o(this.DOM.input.innerHTML),i=!d&&t.anchorNode.previousSibling;if(e.length>=p.length&&i)if(v.call(this,i)&&!i.hasAttribute("readonly")){if(this.removeTags(i),this.fixFirefoxLastTagNoCaret(),2==this.DOM.input.children.length&&"BR"==this.DOM.input.children[1].tagName)return this.DOM.input.innerHTML="",this.value.length=0,!0}else i.remove();this.value=[].map.call(c,((t,e)=>{var i=T(t);if(t.parentNode||i.readonly)return i;this.trigger("remove",{tag:t,index:e,data:i})})).filter((t=>t))}),20)}return!0}switch(t.key){case"Backspace":"select"==e.mode&&e.enforceWhitelist&&this.value.length?this.removeTags():this.state.dropdown.visible&&"manual"!=e.dropdown.position||""!=t.target.textContent&&8203!=i.charCodeAt(0)||(!0===e.backspace?this.removeTags():"edit"==e.backspace&&setTimeout(this.editTag.bind(this),0));break;case"Esc":case"Escape":if(this.state.dropdown.visible)return;t.target.blur();break;case"Down":case"ArrowDown":this.state.dropdown.visible||this.dropdown.show();break;case"ArrowRight":{let t=this.state.inputSuggestion||this.state.ddItemData;if(t&&e.autoComplete.rightKey)return void this.addTags([t],!0);break}case"Tab":{let s="select"==e.mode;if(!i||s)return!0;t.preventDefault()}case"Enter":if(this.state.dropdown.visible&&"manual"!=e.dropdown.position)return;t.preventDefault(),setTimeout((()=>{this.state.actions.selectOption||this.addTags(i,!0)}))}}},onInput(t){this.postUpdate();var e=this.settings;if("mix"==e.mode)return this.events.callbacks.onMixTagsInput.call(this,t);var i=this.input.normalize.call(this),s=i.length>=e.dropdown.enabled,a={value:i,inputElm:this.DOM.input},n=this.validateTag({value:i});"select"==e.mode&&this.toggleScopeValidation(n),a.isValid=n,this.state.inputText!=i&&(this.input.set.call(this,i,!1),-1!=i.search(e.delimiters)?this.addTags(i)&&this.input.set.call(this):e.dropdown.enabled>=0&&this.dropdown[s?"show":"hide"](i),this.trigger("input",a))},onMixTagsInput(t){var e,i,s,a,n,o,r,l,d=this.settings,h=this.value.length,p=this.getTagElms(),c=document.createDocumentFragment(),m=window.getSelection().getRangeAt(0),v=[].map.call(p,(t=>T(t).value));if("deleteContentBackward"==t.inputType&&u()&&this.events.callbacks.onKeydown.call(this,{target:t.target,key:"Backspace"}),this.value.slice().forEach((t=>{t.readonly&&!v.includes(t.value)&&c.appendChild(this.createTagElem(t))})),c.childNodes.length&&(m.insertNode(c),this.setRangeAtStartEnd(!1,c.lastChild)),p.length!=h)return this.value=[].map.call(this.getTagElms(),(t=>T(t))),void this.update({withoutChangeEvent:!0});if(this.hasMaxTags())return!0;if(window.getSelection&&(o=window.getSelection()).rangeCount>0&&3==o.anchorNode.nodeType){if((m=o.getRangeAt(0).cloneRange()).collapse(!0),m.setStart(o.focusNode,0),s=(e=m.toString().slice(0,m.endOffset)).split(d.pattern).length-1,(i=e.match(d.pattern))&&(a=e.slice(e.lastIndexOf(i[i.length-1]))),a){if(this.state.actions.ArrowLeft=!1,this.state.tag={prefix:a.match(d.pattern)[0],value:a.replace(d.pattern,"")},this.state.tag.baseOffset=o.baseOffset-this.state.tag.value.length,l=this.state.tag.value.match(d.delimiters))return this.state.tag.value=this.state.tag.value.replace(d.delimiters,""),this.state.tag.delimiters=l[0],this.addTags(this.state.tag.value,d.dropdown.clearOnSelect),void this.dropdown.hide();n=this.state.tag.value.length>=d.dropdown.enabled;try{r=(r=this.state.flaggedTags[this.state.tag.baseOffset]).prefix==this.state.tag.prefix&&r.value[0]==this.state.tag.value[0],this.state.flaggedTags[this.state.tag.baseOffset]&&!this.state.tag.value&&delete this.state.flaggedTags[this.state.tag.baseOffset]}catch(t){}(r||s{this.update({withoutChangeEvent:!0}),this.trigger("input",g({},this.state.tag,{textContent:this.DOM.input.textContent})),this.state.tag&&this.dropdown[n?"show":"hide"](this.state.tag.value)}),10)},onInputIE(t){var e=this;setTimeout((function(){e.events.callbacks.onInput.call(e,t)}))},observeOriginalInputValue(){this.DOM.originalInput.parentNode||this.destroy(),this.DOM.originalInput.value!=this.DOM.originalInput.tagifyValue&&this.loadOriginalValues()},onClickScope(t){var e=this.settings,i=t.target.closest("."+e.classNames.tag),s=+new Date-this.state.hasFocus;if(t.target!=this.DOM.scope){if(!t.target.classList.contains(e.classNames.tagX))return i?(this.trigger("click",{tag:i,index:this.getNodeIndex(i),data:T(i),event:t}),void(1!==e.editTags&&1!==e.editTags.clicks||this.events.callbacks.onDoubleClickScope.call(this,t))):void(t.target==this.DOM.input&&("mix"==e.mode&&this.fixFirefoxLastTagNoCaret(),s>500)?this.state.dropdown.visible?this.dropdown.hide():0===e.dropdown.enabled&&"mix"!=e.mode&&this.dropdown.show(this.value.length?"":void 0):"select"!=e.mode||0!==e.dropdown.enabled||this.state.dropdown.visible||this.dropdown.show());this.removeTags(t.target.parentNode)}else this.state.hasFocus||this.DOM.input.focus()},onPaste(t){t.preventDefault();var e,i,s=this.settings;if("select"==s.mode&&s.enforceWhitelist||!s.userInput)return!1;s.readonly||(e=t.clipboardData||window.clipboardData,i=e.getData("Text"),s.hooks.beforePaste(t,{tagify:this,pastedText:i,clipboardData:e}).then((e=>{void 0===e&&(e=i),e&&(this.injectAtCaret(e,window.getSelection().getRangeAt(0)),"mix"==this.settings.mode?this.events.callbacks.onMixTagsInput.call(this,t):this.settings.pasteAsTags?this.addTags(this.state.inputText+e,!0):this.state.inputText=e)})).catch((t=>t)))},onDrop(t){t.preventDefault()},onEditTagInput(t,e){var i=t.closest("."+this.settings.classNames.tag),s=this.getNodeIndex(i),a=T(i),n=this.input.normalize.call(this,t),o={[this.settings.tagTextProp]:n,__tagId:a.__tagId},r=this.validateTag(o);this.editTagChangeDetected(g(a,o))||!0!==t.originalIsValid||(r=!0),i.classList.toggle(this.settings.classNames.tagInvalid,!0!==r),a.__isValid=r,i.title=!0===r?a.title||a.value:r,n.length>=this.settings.dropdown.enabled&&(this.state.editing&&(this.state.editing.value=n),this.dropdown.show(n)),this.trigger("edit:input",{tag:i,index:s,data:g({},this.value[s],{newValue:n}),event:e})},onEditTagPaste(t,e){var i=(e.clipboardData||window.clipboardData).getData("Text");e.preventDefault();var s=f(i);this.setRangeAtStartEnd(!1,s)},onEditTagFocus(t){this.state.editing={scope:t,input:t.querySelector("[contenteditable]")}},onEditTagBlur(t){if(this.state.hasFocus||this.toggleFocusClass(),this.DOM.scope.contains(t)){var e,i,s=this.settings,a=t.closest("."+s.classNames.tag),n=this.input.normalize.call(this,t),o=T(a),r=o.__originalData,l=this.editTagChangeDetected(o),d=this.validateTag({[s.tagTextProp]:n,__tagId:o.__tagId});if(n)if(l){if(e=this.hasMaxTags(),i=g({},r,{[s.tagTextProp]:this.trim(n),__isValid:d}),s.transformTag.call(this,i,r),!0!==(d=(!e||!0===r.__isValid)&&this.validateTag(i))){if(this.trigger("invalid",{data:i,tag:a,message:d}),s.editTags.keepInvalid)return;s.keepInvalidTags?i.__isValid=d:i=r}else s.keepInvalidTags&&(delete i.title,delete i["aria-invalid"],delete i.class);this.onEditTagDone(a,i)}else this.onEditTagDone(a,r);else this.onEditTagDone(a)}},onEditTagkeydown(t,e){if(!this.state.composing)switch(this.trigger("edit:keydown",{event:t}),t.key){case"Esc":case"Escape":e.parentNode.replaceChild(e.__tagifyTagData.__originalHTML,e),this.state.editing=!1;case"Enter":case"Tab":t.preventDefault(),t.target.blur()}},onDoubleClickScope(t){var e,i,s=t.target.closest("."+this.settings.classNames.tag),a=T(s),n=this.settings;s&&n.userInput&&!1!==a.editable&&(e=s.classList.contains(this.settings.classNames.tagEditing),i=s.hasAttribute("readonly"),"select"==n.mode||n.readonly||e||i||!this.settings.editTags||this.editTag(s),this.toggleFocusClass(!0),this.trigger("dblclick",{tag:s,index:this.getNodeIndex(s),data:T(s)}))},onInputDOMChange(t){t.forEach((t=>{t.addedNodes.forEach((t=>{if("

"==t.outerHTML)t.replaceWith(document.createElement("br"));else if(1==t.nodeType&&t.querySelector(this.settings.classNames.tagSelector)){let e=document.createTextNode("");3==t.childNodes[0].nodeType&&"BR"!=t.previousSibling.nodeName&&(e=document.createTextNode("\n")),t.replaceWith(e,...[...t.childNodes].slice(0,-1)),this.placeCaretAfterNode(e)}else if(v.call(this,t)){var e;if(3!=(null===(e=t.previousSibling)||void 0===e?void 0:e.nodeType)||t.previousSibling.textContent||t.previousSibling.remove(),t.previousSibling&&"BR"==t.previousSibling.nodeName){t.previousSibling.replaceWith("\n​");let e=t.nextSibling,i="";for(;e;)i+=e.textContent,e=e.nextSibling;i.trim()&&this.placeCaretAfterNode(t.previousSibling)}}})),t.removedNodes.forEach((t=>{t&&"BR"==t.nodeName&&v.call(this,e)&&(this.removeTags(e),this.fixFirefoxLastTagNoCaret())}))}));var e=this.DOM.input.lastChild;e&&""==e.nodeValue&&e.remove(),e&&"BR"==e.nodeName||this.DOM.input.appendChild(document.createElement("br"))}}};function N(t,e){if(!t){console.warn("Tagify:","input element not found",t);const e=new Proxy(this,{get:()=>()=>e});return e}if(t.__tagify)return console.warn("Tagify: ","input element is already Tagified - Same instance is returned.",t),t.__tagify;var i;g(this,function(t){var e=document.createTextNode("");function i(t,i,s){s&&i.split(/\s+/g).forEach((i=>e[t+"EventListener"].call(e,i,s)))}return{off(t,e){return i("remove",t,e),this},on(t,e){return e&&"function"==typeof e&&i("add",t,e),this},trigger(i,s,a){var n;if(a=a||{cloneData:!0},i)if(t.settings.isJQueryPlugin)"remove"==i&&(i="removeTag"),jQuery(t.DOM.originalInput).triggerHandler(i,[s]);else{try{var o="object"==typeof s?s:{value:s};if((o=a.cloneData?g({},o):o).tagify=this,s.event&&(o.event=this.cloneEvent(s.event)),s instanceof Object)for(var r in s)s[r]instanceof HTMLElement&&(o[r]=s[r]);n=new CustomEvent(i,{detail:o})}catch(t){console.warn(t)}e.dispatchEvent(n)}}}}(this)),this.isFirefox=/firefox|fxios/i.test(navigator.userAgent)&&!/seamonkey/i.test(navigator.userAgent),this.isIE=window.document.documentMode,e=e||{},this.getPersistedData=(i=e.id,t=>{let e,s="/"+t;if(1==localStorage.getItem(x+i+"/v",1))try{e=JSON.parse(localStorage[x+i+s])}catch(t){}return e}),this.setPersistedData=(t=>t?(localStorage.setItem(x+t+"/v",1),(e,i)=>{let s="/"+i,a=JSON.stringify(e);e&&i&&(localStorage.setItem(x+t+s,a),dispatchEvent(new Event("storage")))}):()=>{})(e.id),this.clearPersistedData=(t=>e=>{const i=x+"/"+t+"/";if(e)localStorage.removeItem(i+e);else for(let t in localStorage)t.includes(i)&&localStorage.removeItem(t)})(e.id),this.applySettings(t,e),this.state={inputText:"",editing:!1,composing:!1,actions:{},mixMode:{},dropdown:{},flaggedTags:{}},this.value=[],this.listeners={},this.DOM={},this.build(t),b.call(this),this.getCSSVars(),this.loadOriginalValues(),this.events.customBinding.call(this),this.events.binding.call(this),t.autofocus&&this.DOM.input.focus(),t.__tagify=this}return N.prototype={_dropdown:y,helpers:{sameStr:s,removeCollectionProp:a,omit:n,isObject:h,parseHTML:r,escapeHTML:d,extend:g,concatWithoutDups:p,getUID:m,isNodeTag:v},customEventsList:["change","add","remove","invalid","input","click","keydown","focus","blur","edit:input","edit:beforeUpdate","edit:updated","edit:start","edit:keydown","dropdown:show","dropdown:hide","dropdown:select","dropdown:updated","dropdown:noMatch","dropdown:scroll"],dataProps:["__isValid","__removed","__originalData","__originalHTML","__tagId"],trim(t){return this.settings.trim&&t&&"string"==typeof t?t.trim():t},parseHTML:r,templates:M,parseTemplate(t,e){return r((t=this.settings.templates[t]||t).apply(this,e))},set whitelist(t){const e=t&&Array.isArray(t);this.settings.whitelist=e?t:[],this.setPersistedData(e?t:[],"whitelist")},get whitelist(){return this.settings.whitelist},generateClassSelectors(t){for(let e in t){let i=e;Object.defineProperty(t,i+"Selector",{get(){return"."+this[i].split(" ")[0]}})}},applySettings(t,i){w.templates=this.templates;var s=this.settings=g({},w,i);if(s.disabled=t.hasAttribute("disabled"),s.readonly=s.readonly||t.hasAttribute("readonly"),s.placeholder=d(t.getAttribute("placeholder")||s.placeholder||""),s.required=t.hasAttribute("required"),this.generateClassSelectors(s.classNames),void 0===s.dropdown.includeSelectedTags&&(s.dropdown.includeSelectedTags=s.duplicates),this.isIE&&(s.autoComplete=!1),["whitelist","blacklist"].forEach((e=>{var i=t.getAttribute("data-"+e);i&&(i=i.split(s.delimiters))instanceof Array&&(s[e]=i)})),"autoComplete"in i&&!h(i.autoComplete)&&(s.autoComplete=w.autoComplete,s.autoComplete.enabled=i.autoComplete),"mix"==s.mode&&(s.autoComplete.rightKey=!0,s.delimiters=i.delimiters||null,s.tagTextProp&&!s.dropdown.searchKeys.includes(s.tagTextProp)&&s.dropdown.searchKeys.push(s.tagTextProp)),t.pattern)try{s.pattern=new RegExp(t.pattern)}catch(t){}if(s.delimiters){s._delimiters=s.delimiters;try{s.delimiters=new RegExp(this.settings.delimiters,"g")}catch(t){}}s.disabled&&(s.userInput=!1),this.TEXTS=e(e({},D),s.texts||{}),("select"!=s.mode||i.dropdown.enabled)&&s.userInput||(s.dropdown.enabled=0),s.dropdown.appendTarget=i.dropdown&&i.dropdown.appendTarget?i.dropdown.appendTarget:document.body;let a=this.getPersistedData("whitelist");Array.isArray(a)&&(this.whitelist=Array.isArray(s.whitelist)?p(s.whitelist,a):a)},getAttributes(t){var e,i=this.getCustomAttributes(t),s="";for(e in i)s+=" "+e+(void 0!==t[e]?`="${i[e]}"`:"");return s},getCustomAttributes(t){if(!h(t))return"";var e,i={};for(e in t)"__"!=e.slice(0,2)&&"class"!=e&&t.hasOwnProperty(e)&&void 0!==t[e]&&(i[e]=d(t[e]));return i},setStateSelection(){var t=window.getSelection(),e={anchorOffset:t.anchorOffset,anchorNode:t.anchorNode,range:t.getRangeAt&&t.rangeCount&&t.getRangeAt(0)};return this.state.selection=e,e},getCSSVars(){var t=getComputedStyle(this.DOM.scope,null);var e;this.CSSVars={tagHideTransition:(t=>{let e=t.value;return"s"==t.unit?1e3*e:e})(function(t){if(!t)return{};var e=(t=t.trim().split(" ")[0]).split(/\d+/g).filter((t=>t)).pop().trim();return{value:+t.split(e).filter((t=>t))[0].trim(),unit:e}}((e="tag-hide-transition",t.getPropertyValue("--"+e))))}},build(t){var e=this.DOM;this.settings.mixMode.integrated?(e.originalInput=null,e.scope=t,e.input=t):(e.originalInput=t,e.originalInput_tabIndex=t.tabIndex,e.scope=this.parseTemplate("wrapper",[t,this.settings]),e.input=e.scope.querySelector(this.settings.classNames.inputSelector),t.parentNode.insertBefore(e.scope,t),t.tabIndex=-1)},destroy(){this.events.unbindGlobal.call(this),this.DOM.scope.parentNode.removeChild(this.DOM.scope),this.DOM.originalInput.tabIndex=this.DOM.originalInput_tabIndex,delete this.DOM.originalInput.__tagify,this.dropdown.hide(!0),clearTimeout(this.dropdownHide__bindEventsTimeout),clearInterval(this.listeners.main.originalInputValueObserverInterval)},loadOriginalValues(t){var e,i=this.settings;if(this.state.blockChangeEvent=!0,void 0===t){const e=this.getPersistedData("value");t=e&&!this.DOM.originalInput.value?e:i.mixMode.integrated?this.DOM.input.textContent:this.DOM.originalInput.value}if(this.removeAllTags(),t)if("mix"==i.mode)this.parseMixTags(t),(e=this.DOM.input.lastChild)&&"BR"==e.tagName||this.DOM.input.insertAdjacentHTML("beforeend","
");else{try{JSON.parse(t)instanceof Array&&(t=JSON.parse(t))}catch(t){}this.addTags(t,!0).forEach((t=>t&&t.classList.add(i.classNames.tagNoAnimation)))}else this.postUpdate();this.state.lastOriginalValueReported=i.mixMode.integrated?"":this.DOM.originalInput.value,this.state.blockChangeEvent=!1},cloneEvent(t){var e={};for(var i in t)"path"!=i&&(e[i]=t[i]);return e},loading(t){return this.state.isLoading=t,this.DOM.scope.classList[t?"add":"remove"](this.settings.classNames.scopeLoading),this},tagLoading(t,e){return t&&t.classList[e?"add":"remove"](this.settings.classNames.tagLoading),this},toggleClass(t,e){"string"==typeof t&&this.DOM.scope.classList.toggle(t,e)},toggleScopeValidation(t){var e=!0===t||void 0===t;!this.settings.required&&t&&t===this.TEXTS.empty&&(e=!0),this.toggleClass(this.settings.classNames.tagInvalid,!e),this.DOM.scope.title=e?"":t},toggleFocusClass(t){this.toggleClass(this.settings.classNames.focus,!!t)},triggerChangeEvent:function(){if(!this.settings.mixMode.integrated){var t=this.DOM.originalInput,e=this.state.lastOriginalValueReported!==t.value,i=new CustomEvent("change",{bubbles:!0});e&&(this.state.lastOriginalValueReported=t.value,i.simulated=!0,t._valueTracker&&t._valueTracker.setValue(Math.random()),t.dispatchEvent(i),this.trigger("change",this.state.lastOriginalValueReported),t.value=this.state.lastOriginalValueReported)}},events:I,fixFirefoxLastTagNoCaret(){},setRangeAtStartEnd(t,e){t="number"==typeof t?t:!!t,e=e.lastChild||e;var i=document.getSelection();if(i.focusNode&&i.focusNode!==this.DOM.input)return!0;try{i.rangeCount>=1&&["Start","End"].forEach((s=>i.getRangeAt(0)["set"+s](e,t||e.length)))}catch(t){}},placeCaretAfterNode(t){if(t&&t.parentNode){var e=t,i=window.getSelection(),s=i.getRangeAt(0);i.rangeCount&&(s.setStartAfter(e||t),s.collapse(!0),i.removeAllRanges(),i.addRange(s))}},insertAfterTag(t,e){if(e=e||this.settings.mixMode.insertAfterTag,t&&t.parentNode&&e)return e="string"==typeof e?document.createTextNode(e):e,t.parentNode.insertBefore(e,t.nextSibling),e},editTagChangeDetected(t){var e=t.__originalData;for(var i in e)if(!this.dataProps.includes(i)&&t[i]!=e[i])return!0;return!1},getTagTextNode(t){return t.querySelector(this.settings.classNames.tagTextSelector)},setTagTextNode(t,e){this.getTagTextNode(t).innerHTML=d(e)},editTag(t,e){t=t||this.getLastTag(),e=e||{},this.dropdown.hide();var i=this.settings,s=this.getTagTextNode(t),a=this.getNodeIndex(t),n=T(t),o=this.events.callbacks,r=this,l=!0;if(s){if(!(n instanceof Object&&"editable"in n)||n.editable)return n=T(t,{__originalData:g({},n),__originalHTML:t.cloneNode(!0)}),T(n.__originalHTML,n.__originalData),s.setAttribute("contenteditable",!0),t.classList.add(i.classNames.tagEditing),s.addEventListener("focus",o.onEditTagFocus.bind(this,t)),s.addEventListener("blur",(function(){setTimeout((()=>o.onEditTagBlur.call(r,r.getTagTextNode(t))))})),s.addEventListener("input",o.onEditTagInput.bind(this,s)),s.addEventListener("paste",o.onEditTagPaste.bind(this,s)),s.addEventListener("keydown",(e=>o.onEditTagkeydown.call(this,e,t))),s.addEventListener("compositionstart",o.onCompositionStart.bind(this)),s.addEventListener("compositionend",o.onCompositionEnd.bind(this)),e.skipValidation||(l=this.editTagToggleValidity(t)),s.originalIsValid=l,this.trigger("edit:start",{tag:t,index:a,data:n,isValid:l}),s.focus(),this.setRangeAtStartEnd(!1,s),this}else console.warn("Cannot find element in Tag template: .",i.classNames.tagTextSelector)},editTagToggleValidity(t,e){var i;if(e=e||T(t))return(i=!("__isValid"in e)||!0===e.__isValid)||this.removeTagsFromValue(t),this.update(),t.classList.toggle(this.settings.classNames.tagNotAllowed,!i),e.__isValid;console.warn("tag has no data: ",t,e)},onEditTagDone(t,e){e=e||{};var i={tag:t=t||this.state.editing.scope,index:this.getNodeIndex(t),previousData:T(t),data:e};this.trigger("edit:beforeUpdate",i,{cloneData:!1}),this.state.editing=!1,delete e.__originalData,delete e.__originalHTML,t&&e[this.settings.tagTextProp]?(t=this.replaceTag(t,e),this.editTagToggleValidity(t,e),this.settings.a11y.focusableTags?t.focus():this.placeCaretAfterNode(t)):t&&this.removeTags(t),this.trigger("edit:updated",i),this.dropdown.hide(),this.settings.keepInvalidTags&&this.reCheckInvalidTags()},replaceTag(t,e){e&&e.value||(e=t.__tagifyTagData),e.__isValid&&1!=e.__isValid&&g(e,this.getInvalidTagAttrs(e,e.__isValid));var i=this.createTagElem(e);return t.parentNode.replaceChild(i,t),this.updateValueByDOMTags(),i},updateValueByDOMTags(){this.value.length=0,[].forEach.call(this.getTagElms(),(t=>{t.classList.contains(this.settings.classNames.tagNotAllowed.split(" ")[0])||this.value.push(T(t))})),this.update()},injectAtCaret(t,e){var i;return!(e=e||(null===(i=this.state.selection)||void 0===i?void 0:i.range))&&t?(this.appendMixTags(t),this):(f(t,e),this.setRangeAtStartEnd(!1,content),this.updateValueByDOMTags(),this.update(),this)},input:{set(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];var i=this.settings.dropdown.closeOnSelect;this.state.inputText=t,e&&(this.DOM.input.innerHTML=d(""+t)),!t&&i&&this.dropdown.hide.bind(this),this.input.autocomplete.suggest.call(this),this.input.validate.call(this)},raw(){return this.DOM.input.textContent},validate(){var t=!this.state.inputText||!0===this.validateTag({value:this.state.inputText});return this.DOM.input.classList.toggle(this.settings.classNames.inputInvalid,!t),t},normalize(t){var e=t||this.DOM.input,i=[];e.childNodes.forEach((t=>3==t.nodeType&&i.push(t.nodeValue))),i=i.join("\n");try{i=i.replace(/(?:\r\n|\r|\n)/g,this.settings.delimiters.source.charAt(0))}catch(t){}return i=i.replace(/\s/g," "),this.trim(i)},autocomplete:{suggest(t){if(this.settings.autoComplete.enabled){"string"==typeof(t=t||{})&&(t={value:t});var e=t.value?""+t.value:"",i=e.substr(0,this.state.inputText.length).toLowerCase(),s=e.substring(this.state.inputText.length);e&&this.state.inputText&&i==this.state.inputText.toLowerCase()?(this.DOM.input.setAttribute("data-suggest",s),this.state.inputSuggestion=t):(this.DOM.input.removeAttribute("data-suggest"),delete this.state.inputSuggestion)}},set(t){var e=this.DOM.input.getAttribute("data-suggest"),i=t||(e?this.state.inputText+e:null);return!!i&&("mix"==this.settings.mode?this.replaceTextWithNode(document.createTextNode(this.state.tag.prefix+i)):(this.input.set.call(this,i),this.setRangeAtStartEnd(!1,this.DOM.input)),this.input.autocomplete.suggest.call(this),this.dropdown.hide(),!0)}}},getTagIdx(t){return this.value.findIndex((e=>e.__tagId==(t||{}).__tagId))},getNodeIndex(t){var e=0;if(t)for(;t=t.previousElementSibling;)e++;return e},getTagElms(){for(var t=arguments.length,e=new Array(t),i=0;i{s(this.trim(i.textContent),t,this.settings.dropdown.caseSensitive)&&e.push(a)})),e},getTagElmByValue(t){var e=this.getTagIndexByValue(t)[0];return this.getTagElms()[e]},flashTag(t){t&&(t.classList.add(this.settings.classNames.tagFlash),setTimeout((()=>{t.classList.remove(this.settings.classNames.tagFlash)}),100))},isTagBlacklisted(t){return t=this.trim(t.toLowerCase()),this.settings.blacklist.filter((e=>(""+e).toLowerCase()==t)).length},isTagWhitelisted(t){return!!this.getWhitelistItem(t)},getWhitelistItem(t,e,i){e=e||"value";var a,n=this.settings;return(i=i||n.whitelist).some((i=>{var o="string"==typeof i?i:i[e]||i.value;if(s(o,t,n.dropdown.caseSensitive,n.trim))return a="string"==typeof i?{value:i}:i,!0})),a||"value"!=e||"value"==n.tagTextProp||(a=this.getWhitelistItem(t,n.tagTextProp,i)),a},validateTag(t){var e=this.settings,i="value"in t?"value":e.tagTextProp,s=this.trim(t[i]+"");return(t[i]+"").trim()?e.pattern&&e.pattern instanceof RegExp&&!e.pattern.test(s)?this.TEXTS.pattern:!e.duplicates&&this.isTagDuplicate(s,e.dropdown.caseSensitive,t.__tagId)?this.TEXTS.duplicate:this.isTagBlacklisted(s)||e.enforceWhitelist&&!this.isTagWhitelisted(s)?this.TEXTS.notAllowed:!e.validate||e.validate(t):this.TEXTS.empty},getInvalidTagAttrs(t,e){return{"aria-invalid":!0,class:`${t.class||""} ${this.settings.classNames.tagNotAllowed}`.trim(),title:e}},hasMaxTags(){return this.value.length>=this.settings.maxTags&&this.TEXTS.exceed},setReadonly(t,e){var i=this.settings;document.activeElement.blur(),i[e||"readonly"]=t,this.DOM.scope[(t?"set":"remove")+"Attribute"](e||"readonly",!0),this.setContentEditable(!t)},setContentEditable(t){this.settings.userInput&&(this.DOM.input.contentEditable=t,this.DOM.input.tabIndex=t?0:-1)},setDisabled(t){this.setReadonly(t,"disabled")},normalizeTags(t){var e=this.settings,i=e.whitelist,s=e.delimiters,a=e.mode,n=e.tagTextProp;e.enforceWhitelist;var o=[],r=!!i&&i[0]instanceof Object,l=Array.isArray(t),d=l&&t[0].value,h=t=>(t+"").split(s).filter((t=>t)).map((t=>({[n]:this.trim(t),value:this.trim(t)})));if("number"==typeof t&&(t=t.toString()),"string"==typeof t){if(!t.trim())return[];t=h(t)}else l&&(t=[].concat(...t.map((t=>t.value?t:h(t)))));return r&&!d&&(t.forEach((t=>{var e=o.map((t=>t.value)),i=this.dropdown.filterListItems.call(this,t[n],{exact:!0});this.settings.duplicates||(i=i.filter((t=>!e.includes(t.value))));var s=i.length>1?this.getWhitelistItem(t[n],n,i):i[0];s&&s instanceof Object?o.push(s):"mix"!=a&&(null==t.value&&(t.value=t[n]),o.push(t))})),o.length&&(t=o)),t},parseMixTags(t){var e=this.settings,i=e.mixTagsInterpolator,s=e.duplicates,a=e.transformTag,n=e.enforceWhitelist,o=e.maxTags,r=e.tagTextProp,l=[];return t=t.split(i[0]).map(((t,e)=>{var d,h,g,p=t.split(i[1]),c=p[0],u=l.length==o;try{if(c==+c)throw Error;h=JSON.parse(c)}catch(t){h=this.normalizeTags(c)[0]||{value:c}}if(a.call(this,h),u||!(p.length>1)||n&&!this.isTagWhitelisted(h.value)||!s&&this.isTagDuplicate(h.value)){if(t)return e?i[0]+t:t}else h[d=h[r]?r:"value"]=this.trim(h[d]),g=this.createTagElem(h),l.push(h),g.classList.add(this.settings.classNames.tagNoAnimation),p[0]=g.outerHTML,this.value.push(h);return p.join("")})).join(""),this.DOM.input.innerHTML=t,this.DOM.input.appendChild(document.createTextNode("")),this.DOM.input.normalize(),this.getTagElms().forEach(((t,e)=>T(t,l[e]))),this.update({withoutChangeEvent:!0}),t},replaceTextWithNode(t,e){if(this.state.tag||e){e=e||this.state.tag.prefix+this.state.tag.value;var i,s,a=this.state.selection||window.getSelection(),n=a.anchorNode,o=this.state.tag.delimiters?this.state.tag.delimiters.length:0;return n.splitText(a.anchorOffset-o),-1==(i=n.nodeValue.lastIndexOf(e))?!0:(s=n.splitText(i),t&&n.parentNode.replaceChild(t,s),!0)}},selectTag(t,e){var i=this.settings;if(!i.enforceWhitelist||this.isTagWhitelisted(e.value)){this.input.set.call(this,e[i.tagTextProp]||e.value,!0),this.state.actions.selectOption&&setTimeout((()=>this.setRangeAtStartEnd(!1,this.DOM.input)));var s=this.getLastTag();return s?this.replaceTag(s,e):this.appendTag(t),this.value[0]=e,this.update(),this.trigger("add",{tag:t,data:e}),[t]}},addEmptyTag(t){var e=g({value:""},t||{}),i=this.createTagElem(e);T(i,e),this.appendTag(i),this.editTag(i,{skipValidation:!0})},addTags(t,e,i){var s=[],a=this.settings,n=[],o=document.createDocumentFragment();if(i=i||a.skipInvalid,!t||0==t.length)return s;switch(t=this.normalizeTags(t),a.mode){case"mix":return this.addMixTags(t);case"select":e=!1,this.removeAllTags()}return this.DOM.input.removeAttribute("style"),t.forEach((t=>{var e,r={},l=Object.assign({},t,{value:t.value+""});if(t=Object.assign({},l),a.transformTag.call(this,t),t.__isValid=this.hasMaxTags()||this.validateTag(t),!0!==t.__isValid){if(i)return;if(g(r,this.getInvalidTagAttrs(t,t.__isValid),{__preInvalidData:l}),t.__isValid==this.TEXTS.duplicate&&this.flashTag(this.getTagElmByValue(t.value)),!a.createInvalidTags)return void n.push(t.value)}if("readonly"in t&&(t.readonly?r["aria-readonly"]=!0:delete t.readonly),e=this.createTagElem(t,r),s.push(e),"select"==a.mode)return this.selectTag(e,t);o.appendChild(e),t.__isValid&&!0===t.__isValid?(this.value.push(t),this.trigger("add",{tag:e,index:this.value.length-1,data:t})):(this.trigger("invalid",{data:t,index:this.value.length,tag:e,message:t.__isValid}),a.keepInvalidTags||setTimeout((()=>this.removeTags(e,!0)),1e3)),this.dropdown.position()})),this.appendTag(o),this.update(),t.length&&e&&(this.input.set.call(this,a.createInvalidTags?"":n.join(a._delimiters)),this.setRangeAtStartEnd(!1,this.DOM.input)),a.dropdown.enabled&&this.dropdown.refilter(),s},addMixTags(t){if((t=this.normalizeTags(t))[0].prefix||this.state.tag)return this.prefixedTextToTag(t[0]);"string"==typeof t&&(t=[{value:t}]),this.state.selection;var e=document.createDocumentFragment();return t.forEach((t=>{var i=this.createTagElem(t);e.appendChild(i),this.insertAfterTag(i)})),this.appendMixTags(e),e},appendMixTags(t){var e=!!this.state.selection;e?this.injectAtCaret(t):(this.DOM.input.focus(),(e=this.setStateSelection()).range.setStart(this.DOM.input,e.range.endOffset),e.range.setEnd(this.DOM.input,e.range.endOffset),this.DOM.input.appendChild(t),this.updateValueByDOMTags(),this.update())},prefixedTextToTag(t){var e,i=this.settings,s=this.state.tag.delimiters;if(i.transformTag.call(this,t),t.prefix=t.prefix||this.state.tag?this.state.tag.prefix:(i.pattern.source||i.pattern)[0],e=this.createTagElem(t),this.replaceTextWithNode(e)||this.DOM.input.appendChild(e),setTimeout((()=>e.classList.add(this.settings.classNames.tagNoAnimation)),300),this.value.push(t),this.update(),!s){var a=this.insertAfterTag(e)||e;this.placeCaretAfterNode(a)}return this.state.tag=null,this.trigger("add",g({},{tag:e},{data:t})),e},appendTag(t){var e=this.DOM,i=e.input;i===e.input?e.scope.insertBefore(t,i):e.scope.appendChild(t)},createTagElem(t,i){t.__tagId=m();var s,a=g({},t,e({value:d(t.value+"")},i));return function(t){for(var e,i=document.createNodeIterator(t,NodeFilter.SHOW_TEXT,null,!1);e=i.nextNode();)e.textContent.trim()||e.parentNode.removeChild(e)}(s=this.parseTemplate("tag",[a,this])),T(s,t),s},reCheckInvalidTags(){var t=this.settings;this.getTagElms(t.classNames.tagNotAllowed).forEach(((e,i)=>{var s=T(e),a=this.hasMaxTags(),n=this.validateTag(s),o=!0===n&&!a;if("select"==t.mode&&this.toggleScopeValidation(n),o)return s=s.__preInvalidData?s.__preInvalidData:{value:s.value},this.replaceTag(e,s);e.title=a||n}))},removeTags(t,e,i){var s,a=this.settings;if(t=t&&t instanceof HTMLElement?[t]:t instanceof Array?t:t?[t]:[this.getLastTag()],s=t.reduce(((t,e)=>{e&&"string"==typeof e&&(e=this.getTagElmByValue(e));var i=T(e);return e&&i&&!i.readonly&&t.push({node:e,idx:this.getTagIdx(i),data:T(e,{__removed:!0})}),t}),[]),i="number"==typeof i?i:this.CSSVars.tagHideTransition,"select"==a.mode&&(i=0,this.input.set.call(this)),1==s.length&&"select"!=a.mode&&s[0].node.classList.contains(a.classNames.tagNotAllowed)&&(e=!0),s.length)return a.hooks.beforeRemoveTag(s,{tagify:this}).then((()=>{function t(t){t.node.parentNode&&(t.node.parentNode.removeChild(t.node),e?a.keepInvalidTags&&this.trigger("remove",{tag:t.node,index:t.idx}):(this.trigger("remove",{tag:t.node,index:t.idx,data:t.data}),this.dropdown.refilter(),this.dropdown.position(),this.DOM.input.normalize(),a.keepInvalidTags&&this.reCheckInvalidTags()))}i&&i>10&&1==s.length?function(e){e.node.style.width=parseFloat(window.getComputedStyle(e.node).width)+"px",document.body.clientTop,e.node.classList.add(a.classNames.tagHide),setTimeout(t.bind(this),i,e)}.call(this,s[0]):s.forEach(t.bind(this)),e||(this.removeTagsFromValue(s.map((t=>t.node))),this.update(),"select"==a.mode&&this.setContentEditable(!0))})).catch((t=>{}))},removeTagsFromDOM(){[].slice.call(this.getTagElms()).forEach((t=>t.parentNode.removeChild(t)))},removeTagsFromValue(t){(t=Array.isArray(t)?t:[t]).forEach((t=>{var e=T(t),i=this.getTagIdx(e);i>-1&&this.value.splice(i,1)}))},removeAllTags(t){t=t||{},this.value=[],"mix"==this.settings.mode?this.DOM.input.innerHTML="":this.removeTagsFromDOM(),this.dropdown.refilter(),this.dropdown.position(),this.state.dropdown.visible&&setTimeout((()=>{this.DOM.input.focus()})),"select"==this.settings.mode&&(this.input.set.call(this),this.setContentEditable(!0)),this.update(t)},postUpdate(){var t,e,i=this.settings,s=i.classNames,a="mix"==i.mode?i.mixMode.integrated?this.DOM.input.textContent:this.DOM.originalInput.value.trim():this.value.length+this.input.raw.call(this).length;(this.toggleClass(s.hasMaxTags,this.value.length>=i.maxTags),this.toggleClass(s.hasNoTags,!this.value.length),this.toggleClass(s.empty,!a),"select"==i.mode)&&this.toggleScopeValidation(null===(t=this.value)||void 0===t||null===(e=t[0])||void 0===e?void 0:e.__isValid)},setOriginalInputValue(t){var e=this.DOM.originalInput;this.settings.mixMode.integrated||(e.value=t,e.tagifyValue=e.value,this.setPersistedData(t,"value"))},update(t){clearTimeout(this.debouncedUpdateTimeout),this.debouncedUpdateTimeout=setTimeout(function(){var e=this.getInputValue();this.setOriginalInputValue(e),this.postUpdate(),this.settings.onChangeAfterBlur&&(t||{}).withoutChangeEvent||this.state.blockChangeEvent||this.triggerChangeEvent()}.bind(this),100)},getInputValue(){var t=this.getCleanValue();return"mix"==this.settings.mode?this.getMixedTagsAsString(t):t.length?this.settings.originalInputValueFormat?this.settings.originalInputValueFormat(t):JSON.stringify(t):""},getCleanValue(t){return a(t||this.value,this.dataProps)},getMixedTagsAsString(){var t="",e=this,i=this.settings,s=i.originalInputValueFormat||JSON.stringify,a=i.mixTagsInterpolator;return function i(o){o.childNodes.forEach((o=>{if(1==o.nodeType){const r=T(o);if("BR"==o.tagName&&(t+="\r\n"),r&&v.call(e,o)){if(r.__removed)return;t+=a[0]+s(n(r,e.dataProps))+a[1]}else o.getAttribute("style")||["B","I","U"].includes(o.tagName)?t+=o.textContent:"DIV"!=o.tagName&&"P"!=o.tagName||(t+="\r\n",i(o))}else t+=o.textContent}))}(this.DOM.input),t}},N.prototype.removeTag=N.prototype.removeTags,N})); })(jQuery); \ No newline at end of file diff --git a/dist/react.tagify.js b/dist/react.tagify.js index d0d6bdbd..d7a2be17 100644 --- a/dist/react.tagify.js +++ b/dist/react.tagify.js @@ -23,260 +23,4 @@ * THE SOFTWARE IS NOT PERMISSIBLE TO BE SOLD. */ -;(function(root, factory) { - if (typeof define === 'function' && define.amd) { - define([], factory); - } else if (typeof exports === 'object') { - module.exports = factory(); - } else { - root.React.tagify = factory(); - } -}(this, function() { -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = exports.MixedTags = void 0; -var _react = _interopRequireWildcard(require("react")); -var _server = require("react-dom/server"); -var _propTypes = require("prop-types"); -var _tagify = _interopRequireDefault(require("./tagify.js")); -const _excluded = ["children"]; -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } -function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } -function _extends() { _extends = Object.assign ? Object.assign.bind() : 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; }; return _extends.apply(this, arguments); } -function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } -function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } -const noop = _ => _; -const isSameDeep = (a, b) => { - const trans = x => typeof x == 'string' ? x : JSON.stringify(x); - return trans(a) == trans(b); -}; - -// if a template is a React component, it should be outputed as a String (and not as a React component) -function templatesToString(templates) { - if (templates) { - for (let templateName in templates) { - let Template = templates[templateName]; - let isReactComp = String(Template).includes("jsxRuntime"); - if (isReactComp) templates[templateName] = function () { - for (var _len = arguments.length, props = new Array(_len), _key = 0; _key < _len; _key++) { - props[_key] = arguments[_key]; - } - return (0, _server.renderToStaticMarkup)( /*#__PURE__*/_react.default.createElement(Template, { - props: props - })); - }; - } - } -} -const TagifyWrapper = _ref => { - let name = _ref.name, - value = _ref.value, - _ref$loading = _ref.loading, - loading = _ref$loading === void 0 ? false : _ref$loading, - _ref$onInput = _ref.onInput, - onInput = _ref$onInput === void 0 ? noop : _ref$onInput, - _ref$onAdd = _ref.onAdd, - onAdd = _ref$onAdd === void 0 ? noop : _ref$onAdd, - _ref$onRemove = _ref.onRemove, - onRemove = _ref$onRemove === void 0 ? noop : _ref$onRemove, - _ref$onEditInput = _ref.onEditInput, - onEditInput = _ref$onEditInput === void 0 ? noop : _ref$onEditInput, - _ref$onEditBeforeUpda = _ref.onEditBeforeUpdate, - onEditBeforeUpdate = _ref$onEditBeforeUpda === void 0 ? noop : _ref$onEditBeforeUpda, - _ref$onEditUpdated = _ref.onEditUpdated, - onEditUpdated = _ref$onEditUpdated === void 0 ? noop : _ref$onEditUpdated, - _ref$onEditStart = _ref.onEditStart, - onEditStart = _ref$onEditStart === void 0 ? noop : _ref$onEditStart, - _ref$onEditKeydown = _ref.onEditKeydown, - onEditKeydown = _ref$onEditKeydown === void 0 ? noop : _ref$onEditKeydown, - _ref$onInvalid = _ref.onInvalid, - onInvalid = _ref$onInvalid === void 0 ? noop : _ref$onInvalid, - _ref$onClick = _ref.onClick, - onClick = _ref$onClick === void 0 ? noop : _ref$onClick, - _ref$onKeydown = _ref.onKeydown, - onKeydown = _ref$onKeydown === void 0 ? noop : _ref$onKeydown, - _ref$onFocus = _ref.onFocus, - onFocus = _ref$onFocus === void 0 ? noop : _ref$onFocus, - _ref$onBlur = _ref.onBlur, - onBlur = _ref$onBlur === void 0 ? noop : _ref$onBlur, - _ref$onChange = _ref.onChange, - onChange = _ref$onChange === void 0 ? noop : _ref$onChange, - _ref$onDropdownShow = _ref.onDropdownShow, - onDropdownShow = _ref$onDropdownShow === void 0 ? noop : _ref$onDropdownShow, - _ref$onDropdownHide = _ref.onDropdownHide, - onDropdownHide = _ref$onDropdownHide === void 0 ? noop : _ref$onDropdownHide, - _ref$onDropdownSelect = _ref.onDropdownSelect, - onDropdownSelect = _ref$onDropdownSelect === void 0 ? noop : _ref$onDropdownSelect, - _ref$onDropdownScroll = _ref.onDropdownScroll, - onDropdownScroll = _ref$onDropdownScroll === void 0 ? noop : _ref$onDropdownScroll, - _ref$onDropdownNoMatc = _ref.onDropdownNoMatch, - onDropdownNoMatch = _ref$onDropdownNoMatc === void 0 ? noop : _ref$onDropdownNoMatc, - _ref$onDropdownUpdate = _ref.onDropdownUpdated, - onDropdownUpdated = _ref$onDropdownUpdate === void 0 ? noop : _ref$onDropdownUpdate, - readOnly = _ref.readOnly, - disabled = _ref.disabled, - children = _ref.children, - _ref$settings = _ref.settings, - settings = _ref$settings === void 0 ? {} : _ref$settings, - _ref$InputMode = _ref.InputMode, - InputMode = _ref$InputMode === void 0 ? "input" : _ref$InputMode, - autoFocus = _ref.autoFocus, - className = _ref.className, - whitelist = _ref.whitelist, - tagifyRef = _ref.tagifyRef, - _ref$placeholder = _ref.placeholder, - placeholder = _ref$placeholder === void 0 ? "" : _ref$placeholder, - defaultValue = _ref.defaultValue, - showDropdown = _ref.showDropdown; - const mountedRef = (0, _react.useRef)(); - const inputElmRef = (0, _react.useRef)(); - const tagify = (0, _react.useRef)(); - const _value = defaultValue || value; - const inputAttrs = (0, _react.useMemo)(() => ({ - ref: inputElmRef, - name, - defaultValue: children || typeof _value == 'string' ? _value : JSON.stringify(_value), - className, - readOnly, - disabled, - autoFocus, - placeholder - }), []); - const setFocus = (0, _react.useCallback)(() => { - autoFocus && tagify.current && tagify.current.DOM.input.focus(); - }, [tagify]); - (0, _react.useEffect)(() => { - templatesToString(settings.templates); - if (InputMode == "textarea") settings.mode = "mix"; - - // "whitelist" prop takes precedence - if (whitelist && whitelist.length) settings.whitelist = whitelist; - const t = new _tagify.default(inputElmRef.current, settings); - t.on("input", onInput).on("add", onAdd).on("remove", onRemove).on("invalid", onInvalid).on("keydown", onKeydown).on("focus", onFocus).on("blur", onBlur).on("click", onClick).on("change", onChange).on("edit:input", onEditInput).on("edit:beforeUpdate", onEditBeforeUpdate).on("edit:updated", onEditUpdated).on("edit:start", onEditStart).on("edit:keydown", onEditKeydown).on("dropdown:show", onDropdownShow).on("dropdown:hide", onDropdownHide).on("dropdown:select", onDropdownSelect).on("dropdown:scroll", onDropdownScroll).on("dropdown:noMatch", onDropdownNoMatch).on("dropdown:updated", onDropdownUpdated); - - // Bridge Tagify instance with parent component - if (tagifyRef) { - tagifyRef.current = t; - } - tagify.current = t; - setFocus(); - - // cleanup - return () => { - t.destroy(); - }; - }, []); - (0, _react.useEffect)(() => { - setFocus(); - }, [autoFocus]); - (0, _react.useEffect)(() => { - if (mountedRef.current) { - tagify.current.settings.whitelist.length = 0; - - // replace whitelist array items - whitelist && whitelist.length && tagify.current.settings.whitelist.push(...whitelist); - } - }, [whitelist]); - (0, _react.useEffect)(() => { - const currentValue = tagify.current.getInputValue(); - if (mountedRef.current && !isSameDeep(value, currentValue)) { - tagify.current.loadOriginalValues(value); - } - }, [value]); - (0, _react.useEffect)(() => { - if (mountedRef.current) { - tagify.current.toggleClass(className); - } - }, [className]); - (0, _react.useEffect)(() => { - if (mountedRef.current) { - tagify.current.loading(loading); - } - }, [loading]); - (0, _react.useEffect)(() => { - if (mountedRef.current) { - tagify.current.setReadonly(readOnly); - } - }, [readOnly]); - (0, _react.useEffect)(() => { - if (mountedRef.current) { - tagify.current.setDisabled(disabled); - } - }, [disabled]); - (0, _react.useEffect)(() => { - const t = tagify.current; - if (mountedRef.current) { - if (showDropdown) { - t.dropdown.show.call(t, showDropdown); - t.toggleFocusClass(true); - } else { - t.dropdown.hide.call(t); - } - } - }, [showDropdown]); - (0, _react.useEffect)(() => { - mountedRef.current = true; - }, []); - return ( - /*#__PURE__*/ - // a wrapper must be used because Tagify will appened inside it it's component, - // keeping the virtual-DOM out of the way - _react.default.createElement("div", { - className: "tags-input" - }, /*#__PURE__*/_react.default.createElement(InputMode, inputAttrs)) - ); -}; -TagifyWrapper.propTypes = { - name: _propTypes.string, - value: (0, _propTypes.oneOfType)([_propTypes.string, _propTypes.array]), - loading: _propTypes.bool, - children: (0, _propTypes.oneOfType)([_propTypes.string, _propTypes.array]), - onChange: _propTypes.func, - readOnly: _propTypes.bool, - settings: _propTypes.object, - InputMode: _propTypes.string, - autoFocus: _propTypes.bool, - className: _propTypes.string, - tagifyRef: _propTypes.object, - whitelist: _propTypes.array, - placeholder: _propTypes.string, - defaultValue: (0, _propTypes.oneOfType)([_propTypes.string, _propTypes.array]), - showDropdown: (0, _propTypes.oneOfType)([_propTypes.string, _propTypes.bool]), - onInput: _propTypes.func, - onAdd: _propTypes.func, - onRemove: _propTypes.func, - onEditInput: _propTypes.func, - onEditBeforeUpdate: _propTypes.func, - onEditUpdated: _propTypes.func, - onEditStart: _propTypes.func, - onEditKeydown: _propTypes.func, - onInvalid: _propTypes.func, - onClick: _propTypes.func, - onKeydown: _propTypes.func, - onFocus: _propTypes.func, - onBlur: _propTypes.func, - onDropdownShow: _propTypes.func, - onDropdownHide: _propTypes.func, - onDropdownSelect: _propTypes.func, - onDropdownScroll: _propTypes.func, - onDropdownNoMatch: _propTypes.func, - onDropdownUpdated: _propTypes.func -}; -const Tags = /*#__PURE__*/_react.default.memo(TagifyWrapper); -Tags.displayName = "Tags"; -const MixedTags = _ref2 => { - let children = _ref2.children, - rest = _objectWithoutProperties(_ref2, _excluded); - return /*#__PURE__*/_react.default.createElement(Tags, _extends({ - InputMode: "textarea" - }, rest), children); -}; -exports.MixedTags = MixedTags; -var _default = Tags; -exports.default = _default; -return exports; -})); +!function(e,n){"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?module.exports=n():e.React.tagify=n()}(this,(function(){"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=exports.MixedTags=void 0;var e,n=function(e,n){if(!n&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var t=u(n);if(t&&t.has(e))return t.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var d in e)if("default"!==d&&Object.prototype.hasOwnProperty.call(e,d)){var i=r?Object.getOwnPropertyDescriptor(e,d):null;i&&(i.get||i.set)?Object.defineProperty(o,d,i):o[d]=e[d]}o.default=e,t&&t.set(e,o);return o}(require("react")),t=require("react-dom/server"),o=require("prop-types"),r=(e=require("./tagify.js"))&&e.__esModule?e:{default:e};const d=["children"];function u(e){if("function"!=typeof WeakMap)return null;var n=new WeakMap,t=new WeakMap;return(u=function(e){return e?t:n})(e)}function i(){return i=Object.assign?Object.assign.bind():function(e){for(var n=1;n=0||(r[t]=e[t]);return r}(e,n);if(Object.getOwnPropertySymbols){var d=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}const c=e=>e;const l=e=>{let o=e.name,d=e.value,u=e.loading,i=void 0!==u&&u,a=e.onInput,l=void 0===a?c:a,s=e.onAdd,f=void 0===s?c:s,p=e.onRemove,y=void 0===p?c:p,g=e.onEditInput,v=void 0===g?c:g,w=e.onEditBeforeUpdate,h=void 0===w?c:w,O=e.onEditUpdated,b=void 0===O?c:O,m=e.onEditStart,E=void 0===m?c:m,j=e.onEditKeydown,D=void 0===j?c:j,M=e.onInvalid,x=void 0===M?c:M,S=e.onClick,k=void 0===S?c:S,I=e.onKeydown,R=void 0===I?c:I,N=e.onFocus,P=void 0===N?c:N,T=e.onBlur,C=void 0===T?c:T,U=e.onChange,F=void 0===U?c:U,_=e.onDropdownShow,V=void 0===_?c:_,q=e.onDropdownHide,B=void 0===q?c:q,K=e.onDropdownSelect,A=void 0===K?c:K,W=e.onDropdownScroll,H=void 0===W?c:W,J=e.onDropdownNoMatch,z=void 0===J?c:J,G=e.onDropdownUpdated,L=void 0===G?c:G,Q=e.readOnly,X=e.disabled,Y=e.children,Z=e.settings,$=void 0===Z?{}:Z,ee=e.InputMode,ne=void 0===ee?"input":ee,te=e.autoFocus,oe=e.className,re=e.whitelist,de=e.tagifyRef,ue=e.placeholder,ie=void 0===ue?"":ue,ae=e.defaultValue,ce=e.showDropdown;const le=(0,n.useRef)(),se=(0,n.useRef)(),fe=(0,n.useRef)(),pe=ae||d,ye=(0,n.useMemo)((()=>({ref:se,name:o,defaultValue:Y||"string"==typeof pe?pe:JSON.stringify(pe),className:oe,readOnly:Q,disabled:X,autoFocus:te,placeholder:ie})),[]),ge=(0,n.useCallback)((()=>{te&&fe.current&&fe.current.DOM.input.focus()}),[fe]);return(0,n.useEffect)((()=>{!function(e){if(e)for(let o in e){let r=e[o];String(r).includes("jsxRuntime")&&(e[o]=function(){for(var e=arguments.length,o=new Array(e),d=0;d{e.destroy()}}),[]),(0,n.useEffect)((()=>{ge()}),[te]),(0,n.useEffect)((()=>{le.current&&(fe.current.settings.whitelist.length=0,re&&re.length&&fe.current.settings.whitelist.push(...re))}),[re]),(0,n.useEffect)((()=>{const e=fe.current.getInputValue();le.current&&!((e,n)=>{const t=e=>"string"==typeof e?e:JSON.stringify(e);return t(e)==t(n)})(d,e)&&fe.current.loadOriginalValues(d)}),[d]),(0,n.useEffect)((()=>{le.current&&fe.current.toggleClass(oe)}),[oe]),(0,n.useEffect)((()=>{le.current&&fe.current.loading(i)}),[i]),(0,n.useEffect)((()=>{le.current&&fe.current.setReadonly(Q)}),[Q]),(0,n.useEffect)((()=>{le.current&&fe.current.setDisabled(X)}),[X]),(0,n.useEffect)((()=>{const e=fe.current;le.current&&(ce?(e.dropdown.show.call(e,ce),e.toggleFocusClass(!0)):e.dropdown.hide.call(e))}),[ce]),(0,n.useEffect)((()=>{le.current=!0}),[]),n.default.createElement("div",{className:"tags-input"},n.default.createElement(ne,ye))};l.propTypes={name:o.string,value:(0,o.oneOfType)([o.string,o.array]),loading:o.bool,children:(0,o.oneOfType)([o.string,o.array]),onChange:o.func,readOnly:o.bool,settings:o.object,InputMode:o.string,autoFocus:o.bool,className:o.string,tagifyRef:o.object,whitelist:o.array,placeholder:o.string,defaultValue:(0,o.oneOfType)([o.string,o.array]),showDropdown:(0,o.oneOfType)([o.string,o.bool]),onInput:o.func,onAdd:o.func,onRemove:o.func,onEditInput:o.func,onEditBeforeUpdate:o.func,onEditUpdated:o.func,onEditStart:o.func,onEditKeydown:o.func,onInvalid:o.func,onClick:o.func,onKeydown:o.func,onFocus:o.func,onBlur:o.func,onDropdownShow:o.func,onDropdownHide:o.func,onDropdownSelect:o.func,onDropdownScroll:o.func,onDropdownNoMatch:o.func,onDropdownUpdated:o.func};const s=n.default.memo(l);s.displayName="Tags";exports.MixedTags=e=>{let t=e.children,o=a(e,d);return n.default.createElement(s,i({InputMode:"textarea"},o),t)};var f=s;return exports.default=f,exports})); \ No newline at end of file diff --git a/dist/tagify.esm.js b/dist/tagify.esm.js index 2afaae65..70e42848 100644 --- a/dist/tagify.esm.js +++ b/dist/tagify.esm.js @@ -271,6 +271,20 @@ function injectAtCaret(content, range) { return content; } +/** Setter/Getter + * Each tag DOM node contains a custom property called "__tagifyTagData" which hosts its data + * @param {Node} tagElm + * @param {Object} data + */ +function getSetTagData(tagElm, data, override) { + if (!tagElm) { + console.warn("tag element doesn't exist", tagElm, data); + return data; + } + if (data) tagElm.__tagifyTagData = override ? data : extend({}, tagElm.__tagifyTagData || {}, data); + return tagElm.__tagifyTagData; +} + var DEFAULTS = { delimiters: ",", // [RegEx] split tags by any of these delimiters ("null" to cancel) Example: ",| |." @@ -303,7 +317,7 @@ var DEFAULTS = { mixTagsAllowedAfter: /,|\.|\:|\s/, // RegEx - Define conditions in which mix-tags content allows a tag to be added after mixTagsInterpolator: ['[[', ']]'], - // Interpolation for mix mode. Everything between this will becmoe a tag + // Interpolation for mix mode. Everything between these will become a tag, if is a valid Object backspace: true, // false / true / "edit" skipInvalid: false, @@ -1591,7 +1605,7 @@ var events = { // find out which tag(s) were deleted and trigger "remove" event // iterate over the list of tags still in the document and then filter only those from the "this.value" collection this.value = [].map.call(lastTagElems, (node, nodeIdx) => { - var tagData = this.tagData(node); + var tagData = getSetTagData(node); // since readonly cannot be removed (it's technically resurrected if removed somehow) if (node.parentNode || tagData.readonly) return tagData;else this.trigger('remove', { @@ -1709,7 +1723,7 @@ var events = { tagsElems = this.getTagElms(), fragment = document.createDocumentFragment(), range = window.getSelection().getRangeAt(0), - remainingTagsValues = [].map.call(tagsElems, node => this.tagData(node).value); + remainingTagsValues = [].map.call(tagsElems, node => getSetTagData(node).value); // Android Chrome "keydown" event argument does not report the correct "key". // this workaround is needed to manually call "onKeydown" method with a synthesized event object @@ -1731,7 +1745,7 @@ var events = { // check if tags were "magically" added/removed (browser redo/undo or CTRL-A -> delete) if (tagsElems.length != lastTagsCount) { - this.value = [].map.call(this.getTagElms(), node => this.tagData(node)); + this.value = [].map.call(this.getTagElms(), node => getSetTagData(node)); this.update({ withoutChangeEvent: true }); @@ -1843,7 +1857,7 @@ var events = { this.trigger("click", { tag: tagElm, index: this.getNodeIndex(tagElm), - data: this.tagData(tagElm), + data: getSetTagData(tagElm), event: e }); if (_s.editTags === 1 || _s.editTags.clicks === 1) this.events.callbacks.onDoubleClickScope.call(this, e); @@ -1900,7 +1914,7 @@ var events = { onEditTagInput(editableElm, e) { var tagElm = editableElm.closest('.' + this.settings.classNames.tag), tagElmIdx = this.getNodeIndex(tagElm), - tagData = this.tagData(tagElm), + tagData = getSetTagData(tagElm), textValue = this.input.normalize.call(this, editableElm), dataForChangedProp = { [this.settings.tagTextProp]: textValue, @@ -1937,7 +1951,6 @@ var events = { // Get pasted data via clipboard API var clipboardData = e.clipboardData || window.clipboardData, pastedText = clipboardData.getData('Text'); - window.getSelection(); e.preventDefault(); var newNode = injectAtCaret(pastedText); this.setRangeAtStartEnd(false, newNode); @@ -1958,7 +1971,7 @@ var events = { var _s = this.settings, tagElm = editableElm.closest('.' + _s.classNames.tag), textValue = this.input.normalize.call(this, editableElm), - tagData = this.tagData(tagElm), + tagData = getSetTagData(tagElm), originalData = tagData.__originalData, // pre-edit data hasChanged = this.editTagChangeDetected(tagData), @@ -2044,7 +2057,7 @@ var events = { }, onDoubleClickScope(e) { var tagElm = e.target.closest('.' + this.settings.classNames.tag), - tagData = this.tagData(tagElm), + tagData = getSetTagData(tagElm), _s = this.settings, isEditingTag, isReadyOnlyTag; @@ -2056,7 +2069,7 @@ var events = { this.trigger('dblclick', { tag: tagElm, index: this.getNodeIndex(tagElm), - data: this.tagData(tagElm) + data: getSetTagData(tagElm) }); }, /** @@ -2211,7 +2224,7 @@ Tagify.prototype = { templates, parseTemplate(template, data) { template = this.settings.templates[template] || template; - return this.parseHTML(template.apply(this, data)); + return parseHTML(template.apply(this, data)); }, set whitelist(arr) { const isArray = arr && Array.isArray(arr); @@ -2533,7 +2546,7 @@ Tagify.prototype = { var _s = this.settings, editableElm = this.getTagTextNode(tagElm), tagIdx = this.getNodeIndex(tagElm), - tagData = this.tagData(tagElm), + tagData = getSetTagData(tagElm), _CB = this.events.callbacks, that = this, isValid = true, @@ -2547,12 +2560,12 @@ Tagify.prototype = { if (tagData instanceof Object && "editable" in tagData && !tagData.editable) return; // cache the original data, on the DOM node, before any modification ocurs, for possible revert - tagData = this.tagData(tagElm, { + tagData = getSetTagData(tagElm, { __originalData: extend({}, tagData), __originalHTML: tagElm.cloneNode(true) }); // re-set the tagify custom-prop on the clones element (because cloning removed it) - this.tagData(tagData.__originalHTML, tagData.__originalData); + getSetTagData(tagData.__originalHTML, tagData.__originalData); editableElm.setAttribute('contenteditable', true); tagElm.classList.add(_s.classNames.tagEditing); editableElm.addEventListener('focus', _CB.onEditTagFocus.bind(this, tagElm)); @@ -2582,7 +2595,7 @@ Tagify.prototype = { * @returns true if valid, a string (reason) if not */ editTagToggleValidity(tagElm, tagData) { - var tagData = tagData || this.tagData(tagElm), + var tagData = tagData || getSetTagData(tagElm), isValid; if (!tagData) { console.warn("tag has no data: ", tagElm, tagData); @@ -2605,7 +2618,7 @@ Tagify.prototype = { var eventData = { tag: tagElm, index: this.getNodeIndex(tagElm), - previousData: this.tagData(tagElm), + previousData: getSetTagData(tagElm), data: tagData }; this.trigger("edit:beforeUpdate", eventData, { @@ -2651,7 +2664,7 @@ Tagify.prototype = { this.value.length = 0; [].forEach.call(this.getTagElms(), node => { if (node.classList.contains(this.settings.classNames.tagNotAllowed.split(' ')[0])) return; - this.value.push(this.tagData(node)); + this.value.push(getSetTagData(node)); }); this.update(); }, @@ -2792,19 +2805,6 @@ Tagify.prototype = { var lastTag = this.DOM.scope.querySelectorAll(`${this.settings.classNames.tagSelector}:not(.${this.settings.classNames.tagHide}):not([readonly])`); return lastTag[lastTag.length - 1]; }, - /** Setter/Getter - * Each tag DOM node contains a custom property called "__tagifyTagData" which hosts its data - * @param {Node} tagElm - * @param {Object} data - */ - tagData(tagElm, data, override) { - if (!tagElm) { - console.warn("tag element doesn't exist", tagElm, data); - return data; - } - if (data) tagElm.__tagifyTagData = override ? data : extend({}, tagElm.__tagifyTagData || {}, data); - return tagElm.__tagifyTagData; - }, /** * Searches if any tag with a certain value already exis * @param {String/Object} value [text value / tag data object] @@ -3057,7 +3057,7 @@ Tagify.prototype = { this.DOM.input.innerHTML = s; this.DOM.input.appendChild(document.createTextNode('')); this.DOM.input.normalize(); - this.getTagElms().forEach((elm, idx) => this.tagData(elm, tagsDataSet[idx])); + this.getTagElms().forEach((elm, idx) => getSetTagData(elm, tagsDataSet[idx])); this.update({ withoutChangeEvent: true }); @@ -3135,7 +3135,7 @@ Tagify.prototype = { value: "" }, initialData || {}), tagElm = this.createTagElem(tagData); - this.tagData(tagElm, tagData); + getSetTagData(tagElm, tagData); // add the tag to the component's DOM this.appendTag(tagElm); @@ -3357,7 +3357,7 @@ Tagify.prototype = { // while( tagElm.lastChild.nodeType == 3 ) // tagElm.lastChild.parentNode.removeChild(tagElm.lastChild) - this.tagData(tagElm, tagData); + getSetTagData(tagElm, tagData); return tagElm; }, /** @@ -3367,7 +3367,7 @@ Tagify.prototype = { reCheckInvalidTags() { var _s = this.settings; this.getTagElms(_s.classNames.tagNotAllowed).forEach((tagElm, i) => { - var tagData = this.tagData(tagElm), + var tagData = getSetTagData(tagElm), hasMaxTags = this.hasMaxTags(), tagValidation = this.validateTag(tagData), isValid = tagValidation === true && !hasMaxTags; @@ -3404,7 +3404,7 @@ Tagify.prototype = { // 4. return a collection of Objects tagsToRemove = tagElms.reduce((elms, tagElm) => { if (tagElm && typeof tagElm == 'string') tagElm = this.getTagElmByValue(tagElm); - var tagData = this.tagData(tagElm); + var tagData = getSetTagData(tagElm); if (tagElm && tagData && !tagData.readonly) // make sure it's a tag and not some other node // because the DOM node might be removed by async animation, the state will be updated while @@ -3413,7 +3413,7 @@ Tagify.prototype = { node: tagElm, idx: this.getTagIdx(tagData), // this.getNodeIndex(tagElm); // this.getTagIndexByValue(tagElm.textContent) - data: this.tagData(tagElm, { + data: getSetTagData(tagElm, { '__removed': true }) }); @@ -3487,7 +3487,7 @@ Tagify.prototype = { removeTagsFromValue(tags) { tags = Array.isArray(tags) ? tags : [tags]; tags.forEach(tag => { - var tagData = this.tagData(tag), + var tagData = getSetTagData(tag), tagIdx = this.getTagIdx(tagData); // delete tagData.__removed @@ -3569,7 +3569,7 @@ Tagify.prototype = { function iterateChildren(rootNode) { rootNode.childNodes.forEach(node => { if (node.nodeType == 1) { - const tagData = that.tagData(node); + const tagData = getSetTagData(node); if (node.tagName == 'BR') { result += "\r\n"; } diff --git a/dist/tagify.js b/dist/tagify.js index a4114826..4c32b62f 100644 --- a/dist/tagify.js +++ b/dist/tagify.js @@ -277,6 +277,20 @@ return content; } + /** Setter/Getter + * Each tag DOM node contains a custom property called "__tagifyTagData" which hosts its data + * @param {Node} tagElm + * @param {Object} data + */ + function getSetTagData(tagElm, data, override) { + if (!tagElm) { + console.warn("tag element doesn't exist", tagElm, data); + return data; + } + if (data) tagElm.__tagifyTagData = override ? data : extend({}, tagElm.__tagifyTagData || {}, data); + return tagElm.__tagifyTagData; + } + var DEFAULTS = { delimiters: ",", // [RegEx] split tags by any of these delimiters ("null" to cancel) Example: ",| |." @@ -309,7 +323,7 @@ mixTagsAllowedAfter: /,|\.|\:|\s/, // RegEx - Define conditions in which mix-tags content allows a tag to be added after mixTagsInterpolator: ['[[', ']]'], - // Interpolation for mix mode. Everything between this will becmoe a tag + // Interpolation for mix mode. Everything between these will become a tag, if is a valid Object backspace: true, // false / true / "edit" skipInvalid: false, @@ -1597,7 +1611,7 @@ // find out which tag(s) were deleted and trigger "remove" event // iterate over the list of tags still in the document and then filter only those from the "this.value" collection this.value = [].map.call(lastTagElems, (node, nodeIdx) => { - var tagData = this.tagData(node); + var tagData = getSetTagData(node); // since readonly cannot be removed (it's technically resurrected if removed somehow) if (node.parentNode || tagData.readonly) return tagData;else this.trigger('remove', { @@ -1715,7 +1729,7 @@ tagsElems = this.getTagElms(), fragment = document.createDocumentFragment(), range = window.getSelection().getRangeAt(0), - remainingTagsValues = [].map.call(tagsElems, node => this.tagData(node).value); + remainingTagsValues = [].map.call(tagsElems, node => getSetTagData(node).value); // Android Chrome "keydown" event argument does not report the correct "key". // this workaround is needed to manually call "onKeydown" method with a synthesized event object @@ -1737,7 +1751,7 @@ // check if tags were "magically" added/removed (browser redo/undo or CTRL-A -> delete) if (tagsElems.length != lastTagsCount) { - this.value = [].map.call(this.getTagElms(), node => this.tagData(node)); + this.value = [].map.call(this.getTagElms(), node => getSetTagData(node)); this.update({ withoutChangeEvent: true }); @@ -1849,7 +1863,7 @@ this.trigger("click", { tag: tagElm, index: this.getNodeIndex(tagElm), - data: this.tagData(tagElm), + data: getSetTagData(tagElm), event: e }); if (_s.editTags === 1 || _s.editTags.clicks === 1) this.events.callbacks.onDoubleClickScope.call(this, e); @@ -1906,7 +1920,7 @@ onEditTagInput(editableElm, e) { var tagElm = editableElm.closest('.' + this.settings.classNames.tag), tagElmIdx = this.getNodeIndex(tagElm), - tagData = this.tagData(tagElm), + tagData = getSetTagData(tagElm), textValue = this.input.normalize.call(this, editableElm), dataForChangedProp = { [this.settings.tagTextProp]: textValue, @@ -1943,7 +1957,6 @@ // Get pasted data via clipboard API var clipboardData = e.clipboardData || window.clipboardData, pastedText = clipboardData.getData('Text'); - window.getSelection(); e.preventDefault(); var newNode = injectAtCaret(pastedText); this.setRangeAtStartEnd(false, newNode); @@ -1964,7 +1977,7 @@ var _s = this.settings, tagElm = editableElm.closest('.' + _s.classNames.tag), textValue = this.input.normalize.call(this, editableElm), - tagData = this.tagData(tagElm), + tagData = getSetTagData(tagElm), originalData = tagData.__originalData, // pre-edit data hasChanged = this.editTagChangeDetected(tagData), @@ -2050,7 +2063,7 @@ }, onDoubleClickScope(e) { var tagElm = e.target.closest('.' + this.settings.classNames.tag), - tagData = this.tagData(tagElm), + tagData = getSetTagData(tagElm), _s = this.settings, isEditingTag, isReadyOnlyTag; @@ -2062,7 +2075,7 @@ this.trigger('dblclick', { tag: tagElm, index: this.getNodeIndex(tagElm), - data: this.tagData(tagElm) + data: getSetTagData(tagElm) }); }, /** @@ -2217,7 +2230,7 @@ templates, parseTemplate(template, data) { template = this.settings.templates[template] || template; - return this.parseHTML(template.apply(this, data)); + return parseHTML(template.apply(this, data)); }, set whitelist(arr) { const isArray = arr && Array.isArray(arr); @@ -2539,7 +2552,7 @@ var _s = this.settings, editableElm = this.getTagTextNode(tagElm), tagIdx = this.getNodeIndex(tagElm), - tagData = this.tagData(tagElm), + tagData = getSetTagData(tagElm), _CB = this.events.callbacks, that = this, isValid = true, @@ -2553,12 +2566,12 @@ if (tagData instanceof Object && "editable" in tagData && !tagData.editable) return; // cache the original data, on the DOM node, before any modification ocurs, for possible revert - tagData = this.tagData(tagElm, { + tagData = getSetTagData(tagElm, { __originalData: extend({}, tagData), __originalHTML: tagElm.cloneNode(true) }); // re-set the tagify custom-prop on the clones element (because cloning removed it) - this.tagData(tagData.__originalHTML, tagData.__originalData); + getSetTagData(tagData.__originalHTML, tagData.__originalData); editableElm.setAttribute('contenteditable', true); tagElm.classList.add(_s.classNames.tagEditing); editableElm.addEventListener('focus', _CB.onEditTagFocus.bind(this, tagElm)); @@ -2588,7 +2601,7 @@ * @returns true if valid, a string (reason) if not */ editTagToggleValidity(tagElm, tagData) { - var tagData = tagData || this.tagData(tagElm), + var tagData = tagData || getSetTagData(tagElm), isValid; if (!tagData) { console.warn("tag has no data: ", tagElm, tagData); @@ -2611,7 +2624,7 @@ var eventData = { tag: tagElm, index: this.getNodeIndex(tagElm), - previousData: this.tagData(tagElm), + previousData: getSetTagData(tagElm), data: tagData }; this.trigger("edit:beforeUpdate", eventData, { @@ -2657,7 +2670,7 @@ this.value.length = 0; [].forEach.call(this.getTagElms(), node => { if (node.classList.contains(this.settings.classNames.tagNotAllowed.split(' ')[0])) return; - this.value.push(this.tagData(node)); + this.value.push(getSetTagData(node)); }); this.update(); }, @@ -2798,19 +2811,6 @@ var lastTag = this.DOM.scope.querySelectorAll(`${this.settings.classNames.tagSelector}:not(.${this.settings.classNames.tagHide}):not([readonly])`); return lastTag[lastTag.length - 1]; }, - /** Setter/Getter - * Each tag DOM node contains a custom property called "__tagifyTagData" which hosts its data - * @param {Node} tagElm - * @param {Object} data - */ - tagData(tagElm, data, override) { - if (!tagElm) { - console.warn("tag element doesn't exist", tagElm, data); - return data; - } - if (data) tagElm.__tagifyTagData = override ? data : extend({}, tagElm.__tagifyTagData || {}, data); - return tagElm.__tagifyTagData; - }, /** * Searches if any tag with a certain value already exis * @param {String/Object} value [text value / tag data object] @@ -3063,7 +3063,7 @@ this.DOM.input.innerHTML = s; this.DOM.input.appendChild(document.createTextNode('')); this.DOM.input.normalize(); - this.getTagElms().forEach((elm, idx) => this.tagData(elm, tagsDataSet[idx])); + this.getTagElms().forEach((elm, idx) => getSetTagData(elm, tagsDataSet[idx])); this.update({ withoutChangeEvent: true }); @@ -3141,7 +3141,7 @@ value: "" }, initialData || {}), tagElm = this.createTagElem(tagData); - this.tagData(tagElm, tagData); + getSetTagData(tagElm, tagData); // add the tag to the component's DOM this.appendTag(tagElm); @@ -3363,7 +3363,7 @@ // while( tagElm.lastChild.nodeType == 3 ) // tagElm.lastChild.parentNode.removeChild(tagElm.lastChild) - this.tagData(tagElm, tagData); + getSetTagData(tagElm, tagData); return tagElm; }, /** @@ -3373,7 +3373,7 @@ reCheckInvalidTags() { var _s = this.settings; this.getTagElms(_s.classNames.tagNotAllowed).forEach((tagElm, i) => { - var tagData = this.tagData(tagElm), + var tagData = getSetTagData(tagElm), hasMaxTags = this.hasMaxTags(), tagValidation = this.validateTag(tagData), isValid = tagValidation === true && !hasMaxTags; @@ -3410,7 +3410,7 @@ // 4. return a collection of Objects tagsToRemove = tagElms.reduce((elms, tagElm) => { if (tagElm && typeof tagElm == 'string') tagElm = this.getTagElmByValue(tagElm); - var tagData = this.tagData(tagElm); + var tagData = getSetTagData(tagElm); if (tagElm && tagData && !tagData.readonly) // make sure it's a tag and not some other node // because the DOM node might be removed by async animation, the state will be updated while @@ -3419,7 +3419,7 @@ node: tagElm, idx: this.getTagIdx(tagData), // this.getNodeIndex(tagElm); // this.getTagIndexByValue(tagElm.textContent) - data: this.tagData(tagElm, { + data: getSetTagData(tagElm, { '__removed': true }) }); @@ -3493,7 +3493,7 @@ removeTagsFromValue(tags) { tags = Array.isArray(tags) ? tags : [tags]; tags.forEach(tag => { - var tagData = this.tagData(tag), + var tagData = getSetTagData(tag), tagIdx = this.getTagIdx(tagData); // delete tagData.__removed @@ -3575,7 +3575,7 @@ function iterateChildren(rootNode) { rootNode.childNodes.forEach(node => { if (node.nodeType == 1) { - const tagData = that.tagData(node); + const tagData = getSetTagData(node); if (node.tagName == 'BR') { result += "\r\n"; } diff --git a/dist/tagify.min.js b/dist/tagify.min.js index 4c32b62f..404abd3a 100644 --- a/dist/tagify.min.js +++ b/dist/tagify.min.js @@ -23,3581 +23,4 @@ * THE SOFTWARE IS NOT PERMISSIBLE TO BE SOLD. */ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Tagify = factory()); -})(this, (function () { 'use strict'; - - function ownKeys(object, enumerableOnly) { - var keys = Object.keys(object); - if (Object.getOwnPropertySymbols) { - var symbols = Object.getOwnPropertySymbols(object); - enumerableOnly && (symbols = symbols.filter(function (sym) { - return Object.getOwnPropertyDescriptor(object, sym).enumerable; - })), keys.push.apply(keys, symbols); - } - return keys; - } - function _objectSpread2(target) { - for (var i = 1; i < arguments.length; i++) { - var source = null != arguments[i] ? arguments[i] : {}; - i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { - _defineProperty(target, key, source[key]); - }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { - Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); - }); - } - return target; - } - 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; - } - - // console.json = console.json || function(argument){ - // for(var arg=0; arg < arguments.length; ++arg) - // console.log( JSON.stringify(arguments[arg], null, 4) ) - // } - - // const isEdge = /Edge/.test(navigator.userAgent) - const sameStr = (s1, s2, caseSensitive, trim) => { - // cast to String - s1 = "" + s1; - s2 = "" + s2; - if (trim) { - s1 = s1.trim(); - s2 = s2.trim(); - } - return caseSensitive ? s1 == s2 : s1.toLowerCase() == s2.toLowerCase(); - }; - - // const getUID = () => (new Date().getTime() + Math.floor((Math.random()*10000)+1)).toString(16) - const removeCollectionProp = (collection, unwantedProps) => collection && Array.isArray(collection) && collection.map(v => omit(v, unwantedProps)); - function omit(obj, props) { - var newObj = {}, - p; - for (p in obj) if (props.indexOf(p) < 0) newObj[p] = obj[p]; - return newObj; - } - function decode(s) { - var el = document.createElement('div'); - return s.replace(/\&#?[0-9a-z]+;/gi, function (enc) { - el.innerHTML = enc; - return el.innerText; - }); - } - - /** - * utility method - * https://stackoverflow.com/a/35385518/104380 - * @param {String} s [HTML string] - * @return {Object} [DOM node] - */ - function parseHTML(s) { - var parser = new DOMParser(), - node = parser.parseFromString(s.trim(), "text/html"); - return node.body.firstElementChild; - } - - /** - * Removed new lines and irrelevant spaces which might affect layout, and are better gone - * @param {string} s [HTML string] - */ - function minify(s) { - return s ? s.replace(/\>[\r\n ]+\<").replace(/(<.*?>)|\s+/g, (m, $1) => $1 ? $1 : ' ') // https://stackoverflow.com/a/44841484/104380 - : ""; - } - function removeTextChildNodes(elm) { - var iter = document.createNodeIterator(elm, NodeFilter.SHOW_TEXT, null, false), - textnode; - - // print all text nodes - while (textnode = iter.nextNode()) { - if (!textnode.textContent.trim()) textnode.parentNode.removeChild(textnode); - } - } - function getfirstTextNode(elm, action) { - action = action || 'previous'; - while (elm = elm[action + 'Sibling']) if (elm.nodeType == 3) return elm; - } - - /** - * utility method - * https://stackoverflow.com/a/6234804/104380 - */ - function escapeHTML(s) { - return typeof s == 'string' ? s.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/`|'/g, "'") : s; - } - - /** - * Checks if an argument is a javascript Object - */ - function isObject(obj) { - var type = Object.prototype.toString.call(obj).split(' ')[1].slice(0, -1); - return obj === Object(obj) && type != 'Array' && type != 'Function' && type != 'RegExp' && type != 'HTMLUnknownElement'; - } - - /** - * merge objects into a single new one - * TEST: extend({}, {a:{foo:1}, b:[]}, {a:{bar:2}, b:[1], c:()=>{}}) - */ - function extend(o, o1, o2) { - if (!(o instanceof Object)) o = {}; - copy(o, o1); - if (o2) copy(o, o2); - function copy(a, b) { - // copy o2 to o - for (var key in b) if (b.hasOwnProperty(key)) { - if (isObject(b[key])) { - if (!isObject(a[key])) a[key] = Object.assign({}, b[key]);else copy(a[key], b[key]); - continue; - } - if (Array.isArray(b[key])) { - a[key] = Object.assign([], b[key]); - continue; - } - a[key] = b[key]; - } - } - return o; - } - - /** - * concatenates N arrays without dups. - * If an array's item is an Object, compare by `value` - */ - function concatWithoutDups() { - const newArr = [], - existingObj = {}; - for (let arr of arguments) { - for (let item of arr) { - // if current item is an object which has yet to be added to the new array - if (isObject(item)) { - if (!existingObj[item.value]) { - newArr.push(item); - existingObj[item.value] = 1; - } - } - - // if current item is not an object and is not in the new array - else if (!newArr.includes(item)) newArr.push(item); - } - } - return newArr; - } - - /** - * Extracted from: https://stackoverflow.com/a/37511463/104380 - * @param {String} s - */ - function unaccent(s) { - // if not supported, do not continue. - // developers should use a polyfill: - // https://github.com/walling/unorm - if (!String.prototype.normalize) return s; - if (typeof s === 'string') return s.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); - } - - /** - * Meassures an element's height, which might yet have been added DOM - * https://stackoverflow.com/q/5944038/104380 - * @param {DOM} node - */ - function getNodeHeight(node) { - var height, - clone = node.cloneNode(true); - clone.style.cssText = "position:fixed; top:-9999px; opacity:0"; - document.body.appendChild(clone); - height = clone.clientHeight; - clone.parentNode.removeChild(clone); - return height; - } - var isChromeAndroidBrowser = () => /(?=.*chrome)(?=.*android)/i.test(navigator.userAgent); - function getUID() { - return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)); - } - function isNodeTag(node) { - return node && node.classList && node.classList.contains(this.settings.classNames.tag); - } - - /** - * Get the caret position relative to the viewport - * https://stackoverflow.com/q/58985076/104380 - * - * @returns {object} left, top distance in pixels - */ - function getCaretGlobalPosition() { - const sel = document.getSelection(); - if (sel.rangeCount) { - const r = sel.getRangeAt(0); - const node = r.startContainer; - const offset = r.startOffset; - let rect, r2; - if (offset > 0) { - r2 = document.createRange(); - r2.setStart(node, offset - 1); - r2.setEnd(node, offset); - rect = r2.getBoundingClientRect(); - return { - left: rect.right, - top: rect.top, - bottom: rect.bottom - }; - } - if (node.getBoundingClientRect) return node.getBoundingClientRect(); - } - return { - left: -9999, - top: -9999 - }; - } - - /** - * Injects content (either string or node) at the current the current (or specificed) caret position - * @param {content} string/node - */ - function injectAtCaret(content, range) { - var selection = window.getSelection(); - range = range || selection.getRangeAt(0); - if (typeof content == 'string') content = document.createTextNode(content); - if (range) { - range.deleteContents(); - range.insertNode(content); - } - return content; - } - - /** Setter/Getter - * Each tag DOM node contains a custom property called "__tagifyTagData" which hosts its data - * @param {Node} tagElm - * @param {Object} data - */ - function getSetTagData(tagElm, data, override) { - if (!tagElm) { - console.warn("tag element doesn't exist", tagElm, data); - return data; - } - if (data) tagElm.__tagifyTagData = override ? data : extend({}, tagElm.__tagifyTagData || {}, data); - return tagElm.__tagifyTagData; - } - - var DEFAULTS = { - delimiters: ",", - // [RegEx] split tags by any of these delimiters ("null" to cancel) Example: ",| |." - pattern: null, - // RegEx pattern to validate input by. Ex: /[1-9]/ - tagTextProp: 'value', - // tag data Object property which will be displayed as the tag's text - maxTags: Infinity, - // Maximum number of tags - callbacks: {}, - // Exposed callbacks object to be triggered on certain events - addTagOnBlur: true, - // automatically adds the text which was inputed as a tag when blur event happens - onChangeAfterBlur: true, - // By default, the native way of inputs' onChange events is kept, and it only fires when the field is blured. - duplicates: false, - // "true" - allow duplicate tags - whitelist: [], - // Array of tags to suggest as the user types (can be used along with "enforceWhitelist" setting) - blacklist: [], - // A list of non-allowed tags - enforceWhitelist: false, - // Only allow tags from the whitelist - userInput: true, - // disable manually typing/pasting/editing tags (tags may only be added from the whitelist) - keepInvalidTags: false, - // if true, do not remove tags which did not pass validation - createInvalidTags: true, - // if false, do not create invalid tags from invalid user input - mixTagsAllowedAfter: /,|\.|\:|\s/, - // RegEx - Define conditions in which mix-tags content allows a tag to be added after - mixTagsInterpolator: ['[[', ']]'], - // Interpolation for mix mode. Everything between these will become a tag, if is a valid Object - backspace: true, - // false / true / "edit" - skipInvalid: false, - // If `true`, do not add invalid, temporary, tags before automatically removing them - pasteAsTags: true, - // automatically converts pasted text into tags. if "false", allows for further text editing - - editTags: { - clicks: 2, - // clicks to enter "edit-mode": 1 for single click. any other value is considered as double-click - keepInvalid: true // keeps invalid edits as-is until `esc` is pressed while in focus - }, - - // 1 or 2 clicks to edit a tag. false/null for not allowing editing - transformTag: () => {}, - // Takes a tag input string as argument and returns a transformed value - trim: true, - // whether or not the value provided should be trimmed, before being added as a tag - a11y: { - focusableTags: false - }, - mixMode: { - insertAfterTag: '\u00A0' // String/Node to inject after a tag has been added (see #588) - }, - - autoComplete: { - enabled: true, - // Tries to suggest the input's value while typing (match from whitelist) by adding the rest of term as grayed-out text - rightKey: false // If `true`, when Right key is pressed, use the suggested value to create a tag, else just auto-completes the input. in mixed-mode this is set to "true" - }, - - classNames: { - namespace: 'tagify', - mixMode: 'tagify--mix', - selectMode: 'tagify--select', - input: 'tagify__input', - focus: 'tagify--focus', - tagNoAnimation: 'tagify--noAnim', - tagInvalid: 'tagify--invalid', - tagNotAllowed: 'tagify--notAllowed', - scopeLoading: 'tagify--loading', - hasMaxTags: 'tagify--hasMaxTags', - hasNoTags: 'tagify--noTags', - empty: 'tagify--empty', - inputInvalid: 'tagify__input--invalid', - dropdown: 'tagify__dropdown', - dropdownWrapper: 'tagify__dropdown__wrapper', - dropdownHeader: 'tagify__dropdown__header', - dropdownFooter: 'tagify__dropdown__footer', - dropdownItem: 'tagify__dropdown__item', - dropdownItemActive: 'tagify__dropdown__item--active', - dropdownItemHidden: 'tagify__dropdown__item--hidden', - dropdownInital: 'tagify__dropdown--initial', - tag: 'tagify__tag', - tagText: 'tagify__tag-text', - tagX: 'tagify__tag__removeBtn', - tagLoading: 'tagify__tag--loading', - tagEditing: 'tagify__tag--editable', - tagFlash: 'tagify__tag--flash', - tagHide: 'tagify__tag--hide' - }, - dropdown: { - classname: '', - enabled: 2, - // minimum input characters to be typed for the suggestions dropdown to show - maxItems: 10, - searchKeys: ["value", "searchBy"], - fuzzySearch: true, - caseSensitive: false, - accentedSearch: true, - includeSelectedTags: false, - // Should the suggestions list Include already-selected tags (after filtering) - highlightFirst: false, - // highlights first-matched item in the list - closeOnSelect: true, - // closes the dropdown after selecting an item, if `enabled:0` (which means always show dropdown) - clearOnSelect: true, - // after selecting a suggetion, should the typed text input remain or be cleared - position: 'all', - // 'manual' / 'text' / 'all' - appendTarget: null // defaults to document.body once DOM has been loaded - }, - - hooks: { - beforeRemoveTag: () => Promise.resolve(), - beforePaste: () => Promise.resolve(), - suggestionClick: () => Promise.resolve() - } - }; - - function initDropdown() { - this.dropdown = {}; - - // auto-bind "this" to all the dropdown methods - for (let p in this._dropdown) this.dropdown[p] = typeof this._dropdown[p] === 'function' ? this._dropdown[p].bind(this) : this._dropdown[p]; - this.dropdown.refs(); - } - var _dropdown = { - refs() { - this.DOM.dropdown = this.parseTemplate('dropdown', [this.settings]); - this.DOM.dropdown.content = this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-wrapper']"); - }, - getHeaderRef() { - return this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-header']"); - }, - getFooterRef() { - return this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-footer']"); - }, - getAllSuggestionsRefs() { - return [...this.DOM.dropdown.content.querySelectorAll(this.settings.classNames.dropdownItemSelector)]; - }, - /** - * shows the suggestions select box - * @param {String} value [optional, filter the whitelist by this value] - */ - show(value) { - var _s = this.settings, - firstListItem, - firstListItemValue, - allowNewTags = _s.mode == 'mix' && !_s.enforceWhitelist, - noWhitelist = !_s.whitelist || !_s.whitelist.length, - noMatchListItem, - isManual = _s.dropdown.position == 'manual'; - - // if text still exists in the input, and `show` method has no argument, then the input's text should be used - value = value === undefined ? this.state.inputText : value; - - // ⚠️ Do not render suggestions list if: - // 1. there's no whitelist (can happen while async loading) AND new tags arn't allowed - // 2. dropdown is disabled - // 3. loader is showing (controlled outside of this code) - if (noWhitelist && !allowNewTags && !_s.templates.dropdownItemNoMatch || _s.dropdown.enable === false || this.state.isLoading || this.settings.readonly) return; - clearTimeout(this.dropdownHide__bindEventsTimeout); - - // if no value was supplied, show all the "whitelist" items in the dropdown - // @type [Array] listItems - // TODO: add a Setting to control items' sort order for "listItems" - this.suggestedListItems = this.dropdown.filterListItems(value); - - // trigger at this exact point to let the developer the chance to manually set "this.suggestedListItems" - if (value && !this.suggestedListItems.length) { - this.trigger('dropdown:noMatch', value); - if (_s.templates.dropdownItemNoMatch) noMatchListItem = _s.templates.dropdownItemNoMatch.call(this, { - value - }); - } - - // if "dropdownItemNoMatch" was no defined, procceed regular flow. - // - if (!noMatchListItem) { - // in mix-mode, if the value isn't included in the whilelist & "enforceWhitelist" setting is "false", - // then add a custom suggestion item to the dropdown - if (this.suggestedListItems.length) { - if (value && allowNewTags && !this.state.editing.scope && !sameStr(this.suggestedListItems[0].value, value)) this.suggestedListItems.unshift({ - value - }); - } else { - if (value && allowNewTags && !this.state.editing.scope) { - this.suggestedListItems = [{ - value - }]; - } - // hide suggestions list if no suggestion matched - else { - this.input.autocomplete.suggest.call(this); - this.dropdown.hide(); - return; - } - } - firstListItem = this.suggestedListItems[0]; - firstListItemValue = "" + (isObject(firstListItem) ? firstListItem.value : firstListItem); - if (_s.autoComplete && firstListItemValue) { - // only fill the sugegstion if the value of the first list item STARTS with the input value (regardless of "fuzzysearch" setting) - if (firstListItemValue.indexOf(value) == 0) this.input.autocomplete.suggest.call(this, firstListItem); - } - } - this.dropdown.fill(noMatchListItem); - if (_s.dropdown.highlightFirst) { - this.dropdown.highlightOption(this.DOM.dropdown.content.querySelector(_s.classNames.dropdownItemSelector)); - } - - // bind events, exactly at this stage of the code. "dropdown.show" method is allowed to be - // called multiple times, regardless if the dropdown is currently visible, but the events-binding - // should only be called if the dropdown wasn't previously visible. - if (!this.state.dropdown.visible) - // timeout is needed for when pressing arrow down to show the dropdown, - // so the key event won't get registered in the dropdown events listeners - setTimeout(this.dropdown.events.binding.bind(this)); - - // set the dropdown visible state to be the same as the searched value. - // MUST be set *before* position() is called - this.state.dropdown.visible = value || true; - this.state.dropdown.query = value; - this.setStateSelection(); - - // try to positioning the dropdown (it might not yet be on the page, doesn't matter, next code handles this) - if (!isManual) { - // a slight delay is needed if the dropdown "position" setting is "text", and nothing was typed in the input, - // so sadly the "getCaretGlobalPosition" method doesn't recognize the caret position without this delay - setTimeout(() => { - this.dropdown.position(); - this.dropdown.render(); - }); - } - - // a delay is needed because of the previous delay reason. - // this event must be fired after the dropdown was rendered & positioned - setTimeout(() => { - this.trigger("dropdown:show", this.DOM.dropdown); - }); - }, - /** - * Hides the dropdown (if it's not managed manually by the developer) - * @param {Boolean} overrideManual - */ - hide(overrideManual) { - var _this$DOM = this.DOM, - scope = _this$DOM.scope, - dropdown = _this$DOM.dropdown, - isManual = this.settings.dropdown.position == 'manual' && !overrideManual; - - // if there's no dropdown, this means the dropdown events aren't binded - if (!dropdown || !document.body.contains(dropdown) || isManual) return; - window.removeEventListener('resize', this.dropdown.position); - this.dropdown.events.binding.call(this, false); // unbind all events - - // if the dropdown is open, and the input (scope) is clicked, - // the dropdown should be now "close", and the next click (on the scope) - // should re-open it, and without a timeout, clicking to close will re-open immediately - // clearTimeout(this.dropdownHide__bindEventsTimeout) - // this.dropdownHide__bindEventsTimeout = setTimeout(this.events.binding.bind(this), 250) // re-bind main events - - scope.setAttribute("aria-expanded", false); - dropdown.parentNode.removeChild(dropdown); - - // scenario: clicking the scope to show the dropdown, clicking again to hide -> calls dropdown.hide() and then re-focuses the input - // which casues another onFocus event, which checked "this.state.dropdown.visible" and see it as "false" and re-open the dropdown - setTimeout(() => { - this.state.dropdown.visible = false; - }, 100); - this.state.dropdown.query = this.state.ddItemData = this.state.ddItemElm = this.state.selection = null; - - // if the user closed the dropdown (in mix-mode) while a potential tag was detected, flag the current tag - // so the dropdown won't be shown on following user input for that "tag" - if (this.state.tag && this.state.tag.value.length) { - this.state.flaggedTags[this.state.tag.baseOffset] = this.state.tag; - } - this.trigger("dropdown:hide", dropdown); - return this; - }, - /** - * Toggles dropdown show/hide - * @param {Boolean} show forces the dropdown to show - */ - toggle(show) { - this.dropdown[this.state.dropdown.visible && !show ? 'hide' : 'show'](); - }, - render() { - // let the element render in the DOM first, to accurately measure it. - // this.DOM.dropdown.style.cssText = "left:-9999px; top:-9999px;"; - var ddHeight = getNodeHeight(this.DOM.dropdown), - _s = this.settings, - enabled = typeof _s.dropdown.enabled == 'number' && _s.dropdown.enabled >= 0; - if (!enabled) return this; - this.DOM.scope.setAttribute("aria-expanded", true); - - // if the dropdown has yet to be appended to the DOM, - // append the dropdown to the body element & handle events - if (!document.body.contains(this.DOM.dropdown)) { - this.DOM.dropdown.classList.add(_s.classNames.dropdownInital); - this.dropdown.position(ddHeight); - _s.dropdown.appendTarget.appendChild(this.DOM.dropdown); - setTimeout(() => this.DOM.dropdown.classList.remove(_s.classNames.dropdownInital)); - } - return this; - }, - /** - * re-renders the dropdown content element (see "dropdownContent" in templates file) - * @param {String/Array} HTMLContent - optional - */ - fill(HTMLContent) { - HTMLContent = typeof HTMLContent == 'string' ? HTMLContent : this.dropdown.createListHTML(HTMLContent || this.suggestedListItems); - var dropdownContent = this.settings.templates.dropdownContent.call(this, HTMLContent); - this.DOM.dropdown.content.innerHTML = minify(dropdownContent); - }, - /** - * Re-renders only the header & footer. - * Used when selecting a suggestion and it is wanted that the suggestions dropdown stays open. - * Since the list of sugegstions is not being re-rendered completely every time a suggestion is selected (the item is transitioned-out) - * then the header & footer should be kept in sync with the suggestions data change - */ - fillHeaderFooter() { - this.settings.templates; - var suggestions = this.dropdown.filterListItems(this.state.dropdown.query), - newHeaderElem = this.parseTemplate('dropdownHeader', [suggestions]), - newFooterElem = this.parseTemplate('dropdownFooter', [suggestions]), - headerRef = this.dropdown.getHeaderRef(), - footerRef = this.dropdown.getFooterRef(); - newHeaderElem && (headerRef === null || headerRef === void 0 ? void 0 : headerRef.parentNode.replaceChild(newHeaderElem, headerRef)); - newFooterElem && (footerRef === null || footerRef === void 0 ? void 0 : footerRef.parentNode.replaceChild(newFooterElem, footerRef)); - }, - /** - * fill data into the suggestions list - * (mainly used to update the list when removing tags while the suggestions dropdown is visible, so they will be re-added to the list. not efficient) - */ - refilter(value) { - value = value || this.state.dropdown.query || ''; - this.suggestedListItems = this.dropdown.filterListItems(value); - this.dropdown.fill(); - if (!this.suggestedListItems.length) this.dropdown.hide(); - this.trigger("dropdown:updated", this.DOM.dropdown); - }, - position(ddHeight) { - var _sd = this.settings.dropdown; - if (_sd.position == 'manual') return; - var rect, - top, - bottom, - left, - width, - parentsPositions, - ddElm = this.DOM.dropdown, - placeAbove = _sd.placeAbove, - isDefaultAppendTarget = _sd.appendTarget === document.body, - appendTargetScrollTop = isDefaultAppendTarget ? window.pageYOffset : _sd.appendTarget.scrollTop, - root = document.fullscreenElement || document.webkitFullscreenElement || document.documentElement, - viewportHeight = root.clientHeight, - viewportWidth = Math.max(root.clientWidth || 0, window.innerWidth || 0), - positionTo = viewportWidth > 480 ? _sd.position : 'all', - ddTarget = this.DOM[positionTo == 'input' ? 'input' : 'scope']; - ddHeight = ddHeight || ddElm.clientHeight; - function getParentsPositions(p) { - var left = 0, - top = 0; - - // when in element-fullscreen mode, do not go above the fullscreened-element - while (p && p != root) { - left += p.offsetLeft || 0; - top += p.offsetTop || 0; - p = p.parentNode; - } - return { - left, - top - }; - } - function getAccumulatedAncestorsScrollTop() { - var scrollTop = 0, - p = _sd.appendTarget.parentNode; - while (p) { - scrollTop += p.scrollTop || 0; - p = p.parentNode; - } - return scrollTop; - } - if (!this.state.dropdown.visible) return; - if (positionTo == 'text') { - rect = getCaretGlobalPosition(); - bottom = rect.bottom; - top = rect.top; - left = rect.left; - width = 'auto'; - } else { - parentsPositions = getParentsPositions(_sd.appendTarget); - rect = ddTarget.getBoundingClientRect(); - top = rect.top - parentsPositions.top; - bottom = rect.bottom - 1 - parentsPositions.top; - left = rect.left - parentsPositions.left; - width = rect.width + 'px'; - } - - // if the "append target" isn't the default, correct the `top` variable by ignoring any scrollTop of the target's Ancestors - if (!isDefaultAppendTarget) { - let accumulatedAncestorsScrollTop = getAccumulatedAncestorsScrollTop(); - top += accumulatedAncestorsScrollTop; - bottom += accumulatedAncestorsScrollTop; - } - top = Math.floor(top); - bottom = Math.ceil(bottom); - placeAbove = placeAbove === undefined ? viewportHeight - rect.bottom < ddHeight : placeAbove; - - // flip vertically if there is no space for the dropdown below the input - ddElm.style.cssText = "left:" + (left + window.pageXOffset) + "px; width:" + width + ";" + (placeAbove ? "top: " + (top + appendTargetScrollTop) + "px" : "top: " + (bottom + appendTargetScrollTop) + "px"); - ddElm.setAttribute('placement', placeAbove ? "top" : "bottom"); - ddElm.setAttribute('position', positionTo); - }, - events: { - /** - * Events should only be binded when the dropdown is rendered and removed when isn't - * because there might be multiple Tagify instances on a certain page - * @param {Boolean} bindUnbind [optional. true when wanting to unbind all the events] - */ - binding() { - let bindUnbind = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; - // references to the ".bind()" methods must be saved so they could be unbinded later - var _CB = this.dropdown.events.callbacks, - // callback-refs - _CBR = this.listeners.dropdown = this.listeners.dropdown || { - position: this.dropdown.position.bind(this, null), - onKeyDown: _CB.onKeyDown.bind(this), - onMouseOver: _CB.onMouseOver.bind(this), - onMouseLeave: _CB.onMouseLeave.bind(this), - onClick: _CB.onClick.bind(this), - onScroll: _CB.onScroll.bind(this) - }, - action = bindUnbind ? 'addEventListener' : 'removeEventListener'; - if (this.settings.dropdown.position != 'manual') { - document[action]('scroll', _CBR.position, true); - window[action]('resize', _CBR.position); - window[action]('keydown', _CBR.onKeyDown); - } - this.DOM.dropdown[action]('mouseover', _CBR.onMouseOver); - this.DOM.dropdown[action]('mouseleave', _CBR.onMouseLeave); - this.DOM.dropdown[action]('mousedown', _CBR.onClick); - this.DOM.dropdown.content[action]('scroll', _CBR.onScroll); - }, - callbacks: { - onKeyDown(e) { - // ignore keys during IME composition - if (!this.state.hasFocus || this.state.composing) return; - - // get the "active" element, and if there was none (yet) active, use first child - var selectedElm = this.DOM.dropdown.querySelector(this.settings.classNames.dropdownItemActiveSelector), - selectedElmData = this.dropdown.getSuggestionDataByNode(selectedElm); - switch (e.key) { - case 'ArrowDown': - case 'ArrowUp': - case 'Down': // >IE11 - case 'Up': - { - // >IE11 - e.preventDefault(); - var dropdownItems = this.dropdown.getAllSuggestionsRefs(), - actionUp = e.key == 'ArrowUp' || e.key == 'Up'; - if (selectedElm) { - selectedElm = this.dropdown.getNextOrPrevOption(selectedElm, !actionUp); - } - - // if no element was found OR current item is not a "real" item, loop - if (!selectedElm || !selectedElm.matches(this.settings.classNames.dropdownItemSelector)) { - selectedElm = dropdownItems[actionUp ? dropdownItems.length - 1 : 0]; - } - selectedElmData = this.dropdown.getSuggestionDataByNode(selectedElm); - this.dropdown.highlightOption(selectedElm, true); - // selectedElm.scrollIntoView({inline: 'nearest', behavior: 'smooth'}) - break; - } - case 'Escape': - case 'Esc': - // IE11 - this.dropdown.hide(); - break; - case 'ArrowRight': - if (this.state.actions.ArrowLeft) return; - case 'Tab': - { - // in mix-mode, treat arrowRight like Enter key, so a tag will be created - if (this.settings.mode != 'mix' && selectedElm && !this.settings.autoComplete.rightKey && !this.state.editing) { - e.preventDefault(); // prevents blur so the autocomplete suggestion will not become a tag - var value = this.dropdown.getMappedValue(selectedElmData); - this.input.autocomplete.set.call(this, value); - return false; - } - return true; - } - case 'Enter': - { - e.preventDefault(); - this.settings.hooks.suggestionClick(e, { - tagify: this, - tagData: selectedElmData, - suggestionElm: selectedElm - }).then(() => { - if (selectedElm) { - this.dropdown.selectOption(selectedElm); - // highlight next option - selectedElm = this.dropdown.getNextOrPrevOption(selectedElm, !actionUp); - this.dropdown.highlightOption(selectedElm); - return; - } else this.dropdown.hide(); - if (this.settings.mode != 'mix') this.addTags(this.state.inputText.trim(), true); - }).catch(err => err); - break; - } - case 'Backspace': - { - if (this.settings.mode == 'mix' || this.state.editing.scope) return; - const value = this.input.raw.call(this); - if (value == "" || value.charCodeAt(0) == 8203) { - if (this.settings.backspace === true) this.removeTags();else if (this.settings.backspace == 'edit') setTimeout(this.editTag.bind(this), 0); - } - } - } - }, - onMouseOver(e) { - var ddItem = e.target.closest(this.settings.classNames.dropdownItemSelector); - // event delegation check - ddItem && this.dropdown.highlightOption(ddItem); - }, - onMouseLeave(e) { - // de-highlight any previously highlighted option - this.dropdown.highlightOption(); - }, - onClick(e) { - if (e.button != 0 || e.target == this.DOM.dropdown || e.target == this.DOM.dropdown.content) return; // allow only mouse left-clicks - - var selectedElm = e.target.closest(this.settings.classNames.dropdownItemSelector), - selectedElmData = this.dropdown.getSuggestionDataByNode(selectedElm); - - // temporary set the "actions" state to indicate to the main "blur" event it shouldn't run - this.state.actions.selectOption = true; - setTimeout(() => this.state.actions.selectOption = false, 50); - this.settings.hooks.suggestionClick(e, { - tagify: this, - tagData: selectedElmData, - suggestionElm: selectedElm - }).then(() => { - if (selectedElm) this.dropdown.selectOption(selectedElm, e);else this.dropdown.hide(); - }).catch(err => console.warn(err)); - }, - onScroll(e) { - var elm = e.target, - pos = elm.scrollTop / (elm.scrollHeight - elm.parentNode.clientHeight) * 100; - this.trigger("dropdown:scroll", { - percentage: Math.round(pos) - }); - } - } - }, - /** - * Given a suggestion-item, return the data associated with it - * @param {HTMLElement} tagElm - * @returns Object - */ - getSuggestionDataByNode(tagElm) { - var value = tagElm && tagElm.getAttribute('value'); - return this.suggestedListItems.find(item => item.value == value) || null; - }, - getNextOrPrevOption(selected) { - let next = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - var dropdownItems = this.dropdown.getAllSuggestionsRefs(), - selectedIdx = dropdownItems.findIndex(item => item === selected); - return next ? dropdownItems[selectedIdx + 1] : dropdownItems[selectedIdx - 1]; - }, - /** - * mark the currently active suggestion option - * @param {Object} elm option DOM node - * @param {Boolean} adjustScroll when navigation with keyboard arrows (up/down), aut-scroll to always show the highlighted element - */ - highlightOption(elm, adjustScroll) { - var className = this.settings.classNames.dropdownItemActive, - itemData; - - // focus casues a bug in Firefox with the placeholder been shown on the input element - // if( this.settings.dropdown.position != 'manual' ) - // elm.focus(); - - if (this.state.ddItemElm) { - this.state.ddItemElm.classList.remove(className); - this.state.ddItemElm.removeAttribute("aria-selected"); - } - if (!elm) { - this.state.ddItemData = null; - this.state.ddItemElm = null; - this.input.autocomplete.suggest.call(this); - return; - } - itemData = this.dropdown.getSuggestionDataByNode(elm); - this.state.ddItemData = itemData; - this.state.ddItemElm = elm; - - // this.DOM.dropdown.querySelectorAll("." + this.settings.classNames.dropdownItemActive).forEach(activeElm => activeElm.classList.remove(className)); - elm.classList.add(className); - elm.setAttribute("aria-selected", true); - if (adjustScroll) elm.parentNode.scrollTop = elm.clientHeight + elm.offsetTop - elm.parentNode.clientHeight; - - // Try to autocomplete the typed value with the currently highlighted dropdown item - if (this.settings.autoComplete) { - this.input.autocomplete.suggest.call(this, itemData); - this.dropdown.position(); // suggestions might alter the height of the tagify wrapper because of unkown suggested term length that could drop to the next line - } - }, - - /** - * Create a tag from the currently active suggestion option - * @param {Object} elm DOM node to select - * @param {Object} event The original Click event, if available (since keyboard ENTER key also triggers this method) - */ - selectOption(elm, event) { - var _this$settings$dropdo = this.settings.dropdown, - clearOnSelect = _this$settings$dropdo.clearOnSelect, - closeOnSelect = _this$settings$dropdo.closeOnSelect; - if (!elm) { - this.addTags(this.state.inputText, true); - closeOnSelect && this.dropdown.hide(); - return; - } - event = event || {}; - - // if in edit-mode, do not continue but instead replace the tag's text. - // the scenario is that "addTags" was called from a dropdown suggested option selected while editing - - var value = elm.getAttribute('value'), - isNoMatch = value == 'noMatch', - tagData = this.suggestedListItems.find(item => (item.value || item) == value); - - // The below event must be triggered, regardless of anything else which might go wrong - this.trigger('dropdown:select', { - data: tagData, - elm, - event - }); - if (!value || !tagData && !isNoMatch) { - closeOnSelect && setTimeout(this.dropdown.hide.bind(this)); - return; - } - if (this.state.editing) { - // normalizing value, because "tagData" might be a string, and therefore will not be able to extend the object - this.onEditTagDone(null, extend({ - __isValid: true - }, this.normalizeTags([tagData])[0])); - } - // Tagify instances should re-focus to the input element once an option was selected, to allow continuous typing - else { - this[this.settings.mode == 'mix' ? "addMixTags" : "addTags"]([tagData || this.input.raw.call(this)], clearOnSelect); - } - - // todo: consider not doing this on mix-mode - if (!this.DOM.input.parentNode) return; - setTimeout(() => { - this.DOM.input.focus(); - this.toggleFocusClass(true); - this.setRangeAtStartEnd(false, this.DOM.input); - }); - closeOnSelect && setTimeout(this.dropdown.hide.bind(this)); - - // hide selected suggestion - elm.addEventListener('transitionend', () => { - this.dropdown.fillHeaderFooter(); - setTimeout(() => elm.remove(), 100); - }, { - once: true - }); - elm.classList.add(this.settings.classNames.dropdownItemHidden); - }, - // adds all the suggested items, including the ones which are not currently rendered, - // unless specified otherwise (by the "onlyRendered" argument) - selectAll(onlyRendered) { - // having suggestedListItems with items messes with "normalizeTags" when wanting - // to add all tags - this.suggestedListItems.length = 0; - this.dropdown.hide(); - this.dropdown.filterListItems(''); - var tagsToAdd = this.dropdown.filterListItems(''); - if (!onlyRendered) tagsToAdd = this.state.dropdown.suggestions; - - // some whitelist items might have already been added as tags so when addings all of them, - // skip adding already-added ones, so best to use "filterListItems" method over "settings.whitelist" - this.addTags(tagsToAdd, true); - return this; - }, - /** - * returns an HTML string of the suggestions' list items - * @param {String} value string to filter the whitelist by - * @param {Object} options "exact" - for exact complete match - * @return {Array} list of filtered whitelist items according to the settings provided and current value - */ - filterListItems(value, options) { - var _s = this.settings, - _sd = _s.dropdown, - options = options || {}, - list = [], - exactMatchesList = [], - whitelist = _s.whitelist, - suggestionsCount = _sd.maxItems >= 0 ? _sd.maxItems : Infinity, - searchKeys = _sd.searchKeys, - whitelistItem, - valueIsInWhitelist, - searchBy, - isDuplicate, - niddle, - i = 0; - value = _s.mode == 'select' && this.value.length && this.value[0][_s.tagTextProp] == value ? '' // do not filter if the tag, which is already selecetd in "select" mode, is the same as the typed text - : value; - if (!value || !searchKeys.length) { - list = _sd.includeSelectedTags ? whitelist : whitelist.filter(item => !this.isTagDuplicate(isObject(item) ? item.value : item)); // don't include tags which have already been added. - - this.state.dropdown.suggestions = list; - return list.slice(0, suggestionsCount); // respect "maxItems" dropdown setting - } - - niddle = _sd.caseSensitive ? "" + value : ("" + value).toLowerCase(); - - // checks if ALL of the words in the search query exists in the current whitelist item, regardless of their order - function stringHasAll(s, query) { - return query.toLowerCase().split(' ').every(q => s.includes(q.toLowerCase())); - } - for (; i < whitelist.length; i++) { - let startsWithMatch, exactMatch; - whitelistItem = whitelist[i] instanceof Object ? whitelist[i] : { - value: whitelist[i] - }; //normalize value as an Object - - let itemWithoutSearchKeys = !Object.keys(whitelistItem).some(k => searchKeys.includes(k)), - _searchKeys = itemWithoutSearchKeys ? ["value"] : searchKeys; - if (_sd.fuzzySearch && !options.exact) { - searchBy = _searchKeys.reduce((values, k) => values + " " + (whitelistItem[k] || ""), "").toLowerCase().trim(); - if (_sd.accentedSearch) { - searchBy = unaccent(searchBy); - niddle = unaccent(niddle); - } - startsWithMatch = searchBy.indexOf(niddle) == 0; - exactMatch = searchBy === niddle; - valueIsInWhitelist = stringHasAll(searchBy, niddle); - } else { - startsWithMatch = true; - valueIsInWhitelist = _searchKeys.some(k => { - var v = '' + (whitelistItem[k] || ''); // if key exists, cast to type String - - if (_sd.accentedSearch) { - v = unaccent(v); - niddle = unaccent(niddle); - } - if (!_sd.caseSensitive) v = v.toLowerCase(); - exactMatch = v === niddle; - return options.exact ? v === niddle : v.indexOf(niddle) == 0; - }); - } - isDuplicate = !_sd.includeSelectedTags && this.isTagDuplicate(isObject(whitelistItem) ? whitelistItem.value : whitelistItem); - - // match for the value within each "whitelist" item - if (valueIsInWhitelist && !isDuplicate) if (exactMatch && startsWithMatch) exactMatchesList.push(whitelistItem);else if (_sd.sortby == 'startsWith' && startsWithMatch) list.unshift(whitelistItem);else list.push(whitelistItem); - } - this.state.dropdown.suggestions = exactMatchesList.concat(list); - - // custom sorting function - return typeof _sd.sortby == 'function' ? _sd.sortby(exactMatchesList.concat(list), niddle) : exactMatchesList.concat(list).slice(0, suggestionsCount); - }, - /** - * Returns the final value of a tag data (object) with regards to the "mapValueTo" dropdown setting - * @param {Object} tagData - * @returns - */ - getMappedValue(tagData) { - var mapValueTo = this.settings.dropdown.mapValueTo, - value = mapValueTo ? typeof mapValueTo == 'function' ? mapValueTo(tagData) : tagData[mapValueTo] || tagData.value : tagData.value; - return value; - }, - /** - * Creates the dropdown items' HTML - * @param {Array} sugegstionsList [Array of Objects] - * @return {String} - */ - createListHTML(sugegstionsList) { - return extend([], sugegstionsList).map((suggestion, idx) => { - if (typeof suggestion == 'string' || typeof suggestion == 'number') suggestion = { - value: suggestion - }; - var mappedValue = this.dropdown.getMappedValue(suggestion); - mappedValue = typeof mappedValue == 'string' ? escapeHTML(mappedValue) : mappedValue; - return this.settings.templates.dropdownItem.apply(this, [_objectSpread2(_objectSpread2({}, suggestion), {}, { - mappedValue - }), this]); - }).join(""); - } - }; - - const VERSION = 1; // current version of persisted data. if code change breaks persisted data, verison number should be bumped. - const STORE_KEY = '@yaireo/tagify/'; - const getPersistedData = id => key => { - // if "persist" is "false", do not save to localstorage - let customKey = '/' + key, - persistedData, - versionMatch = localStorage.getItem(STORE_KEY + id + '/v', VERSION) == VERSION; - if (versionMatch) { - try { - persistedData = JSON.parse(localStorage[STORE_KEY + id + customKey]); - } catch (err) {} - } - return persistedData; - }; - const setPersistedData = id => { - if (!id) return () => {}; - - // for storage invalidation - localStorage.setItem(STORE_KEY + id + '/v', VERSION); - return (data, key) => { - let customKey = '/' + key, - persistedData = JSON.stringify(data); - if (data && key) { - localStorage.setItem(STORE_KEY + id + customKey, persistedData); - dispatchEvent(new Event('storage')); - } - }; - }; - const clearPersistedData = id => key => { - const base = STORE_KEY + '/' + id + '/'; - - // delete specific key in the storage - if (key) localStorage.removeItem(base + key); - - // delete all keys in the storage with a specific tagify id - else { - for (let k in localStorage) if (k.includes(base)) localStorage.removeItem(k); - } - }; - - var TEXTS = { - empty: "empty", - exceed: "number of tags exceeded", - pattern: "pattern mismatch", - duplicate: "already exists", - notAllowed: "not allowed" - }; - - var templates = { - /** - * - * @param {DOM Object} input Original input DOm element - * @param {Object} settings Tagify instance settings Object - */ - wrapper(input, _s) { - return ` - - ​ - `; - }, - tag(tagData, _ref) { - let _s = _ref.settings; - return ` - -
- ${tagData[_s.tagTextProp] || tagData.value} -
-
`; - }, - dropdown(settings) { - var _sd = settings.dropdown, - isManual = _sd.position == 'manual', - className = `${settings.classNames.dropdown}`; - return `
-
-
`; - }, - dropdownContent(HTMLContent) { - var _s = this.settings, - suggestions = this.state.dropdown.suggestions; - return ` - ${_s.templates.dropdownHeader.call(this, suggestions)} - ${HTMLContent} - ${_s.templates.dropdownFooter.call(this, suggestions)} - `; - }, - dropdownItem(item) { - return `
${item.mappedValue || item.value}
`; - }, - /** - * @param {Array} suggestions An array of all the matched suggested items, including those which were sliced away due to the "dropdown.maxItems" setting - */ - dropdownHeader(suggestions) { - return `
`; - }, - dropdownFooter(suggestions) { - var hasMore = suggestions.length - this.settings.dropdown.maxItems; - return hasMore > 0 ? `
- ${hasMore} more items. Refine your search. -
` : ''; - }, - dropdownItemNoMatch: null - }; - - function EventDispatcher(instance) { - // Create a DOM EventTarget object - var target = document.createTextNode(''); - function addRemove(op, events, cb) { - if (cb) events.split(/\s+/g).forEach(name => target[op + 'EventListener'].call(target, name, cb)); - } - - // Pass EventTarget interface calls to DOM EventTarget object - return { - off(events, cb) { - addRemove('remove', events, cb); - return this; - }, - on(events, cb) { - if (cb && typeof cb == 'function') addRemove('add', events, cb); - return this; - }, - trigger(eventName, data, opts) { - var e; - opts = opts || { - cloneData: true - }; - if (!eventName) return; - if (instance.settings.isJQueryPlugin) { - if (eventName == 'remove') eventName = 'removeTag'; // issue #222 - jQuery(instance.DOM.originalInput).triggerHandler(eventName, [data]); - } else { - try { - var eventData = typeof data === 'object' ? data : { - value: data - }; - eventData = opts.cloneData ? extend({}, eventData) : eventData; - eventData.tagify = this; - if (data.event) eventData.event = this.cloneEvent(data.event); - - // TODO: move the below to the "extend" function - if (data instanceof Object) for (var prop in data) if (data[prop] instanceof HTMLElement) eventData[prop] = data[prop]; - e = new CustomEvent(eventName, { - "detail": eventData - }); - } catch (err) { - console.warn(err); - } - target.dispatchEvent(e); - } - } - }; - } - - var deleteBackspaceTimeout; - function triggerChangeEvent() { - if (this.settings.mixMode.integrated) return; - var inputElm = this.DOM.originalInput, - changed = this.state.lastOriginalValueReported !== inputElm.value, - event = new CustomEvent("change", { - bubbles: true - }); // must use "CustomEvent" and not "Event" to support IE - - if (!changed) return; - - // must apply this BEFORE triggering the simulated event - this.state.lastOriginalValueReported = inputElm.value; - - // React hack: https://github.com/facebook/react/issues/11488 - event.simulated = true; - if (inputElm._valueTracker) inputElm._valueTracker.setValue(Math.random()); - inputElm.dispatchEvent(event); - - // also trigger a Tagify event - this.trigger("change", this.state.lastOriginalValueReported); - - // React, for some reason, clears the input's value after "dispatchEvent" is fired - inputElm.value = this.state.lastOriginalValueReported; - } - var events = { - // bind custom events which were passed in the settings - customBinding() { - this.customEventsList.forEach(name => { - this.on(name, this.settings.callbacks[name]); - }); - }, - binding() { - let bindUnbind = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; - var _CB = this.events.callbacks, - _CBR, - action = bindUnbind ? 'addEventListener' : 'removeEventListener'; - - // do not allow the main events to be bound more than once - if (this.state.mainEvents && bindUnbind) return; - - // set the binding state of the main events, so they will not be bound more than once - this.state.mainEvents = bindUnbind; - - // everything inside gets executed only once-per instance - if (bindUnbind && !this.listeners.main) { - this.events.bindGlobal.call(this); - if (this.settings.isJQueryPlugin) jQuery(this.DOM.originalInput).on('tagify.removeAllTags', this.removeAllTags.bind(this)); - } - - // setup callback references so events could be removed later - _CBR = this.listeners.main = this.listeners.main || { - focus: ['input', _CB.onFocusBlur.bind(this)], - keydown: ['input', _CB.onKeydown.bind(this)], - click: ['scope', _CB.onClickScope.bind(this)], - dblclick: ['scope', _CB.onDoubleClickScope.bind(this)], - paste: ['input', _CB.onPaste.bind(this)], - drop: ['input', _CB.onDrop.bind(this)], - compositionstart: ['input', _CB.onCompositionStart.bind(this)], - compositionend: ['input', _CB.onCompositionEnd.bind(this)] - }; - for (var eventName in _CBR) { - this.DOM[_CBR[eventName][0]][action](eventName, _CBR[eventName][1]); - } - - // listen to original input changes (unfortunetly this is the best way...) - // https://stackoverflow.com/a/1949416/104380 - clearInterval(this.listeners.main.originalInputValueObserverInterval); - this.listeners.main.originalInputValueObserverInterval = setInterval(_CB.observeOriginalInputValue.bind(this), 500); - - // observers - var inputMutationObserver = this.listeners.main.inputMutationObserver || new MutationObserver(_CB.onInputDOMChange.bind(this)); - - // cleaup just-in-case - if (inputMutationObserver) inputMutationObserver.disconnect(); - - // observe stuff - if (this.settings.mode == 'mix') inputMutationObserver.observe(this.DOM.input, { - childList: true - }); - }, - bindGlobal(unbind) { - var _CB = this.events.callbacks, - action = unbind ? 'removeEventListener' : 'addEventListener', - e; - if (!unbind && this.listeners.global) return; // do not re-bind - - // these events are global event should never be unbinded, unless the instance is destroyed: - this.listeners.global = this.listeners && this.listeners.global || [{ - type: this.isIE ? 'keydown' : 'input', - // IE cannot register "input" events on contenteditable elements, so the "keydown" should be used instead.. - target: this.DOM.input, - cb: _CB[this.isIE ? 'onInputIE' : 'onInput'].bind(this) - }, { - type: 'keydown', - target: window, - cb: _CB.onWindowKeyDown.bind(this) - }, { - type: 'blur', - target: this.DOM.input, - cb: _CB.onFocusBlur.bind(this) - }]; - for (e of this.listeners.global) e.target[action](e.type, e.cb); - }, - unbindGlobal() { - this.events.bindGlobal.call(this, true); - }, - /** - * DOM events callbacks - */ - callbacks: { - onFocusBlur(e) { - var _this$value, _this$value$; - var _s = this.settings, - text = e.target ? this.trim(e.target.textContent) : '', - // a string - currentDisplayValue = (_this$value = this.value) === null || _this$value === void 0 ? void 0 : (_this$value$ = _this$value[0]) === null || _this$value$ === void 0 ? void 0 : _this$value$[_s.tagTextProp], - type = e.type, - ddEnabled = _s.dropdown.enabled >= 0, - eventData = { - relatedTarget: e.relatedTarget - }, - isTargetSelectOption = this.state.actions.selectOption && (ddEnabled || !_s.dropdown.closeOnSelect), - isTargetAddNewBtn = this.state.actions.addNew && ddEnabled, - isRelatedTargetX = e.relatedTarget && isNodeTag.call(this, e.relatedTarget) && this.DOM.scope.contains(e.relatedTarget), - shouldAddTags; - if (type == 'blur') { - if (e.relatedTarget === this.DOM.scope) { - this.dropdown.hide(); - this.DOM.input.focus(); - return; - } - this.postUpdate(); - _s.onChangeAfterBlur && this.triggerChangeEvent(); - } - if (isTargetSelectOption || isTargetAddNewBtn) return; - this.state.hasFocus = type == "focus" ? +new Date() : false; - this.toggleFocusClass(this.state.hasFocus); - if (_s.mode == 'mix') { - if (type == "focus") { - this.trigger("focus", eventData); - } else if (e.type == "blur") { - this.trigger("blur", eventData); - this.loading(false); - this.dropdown.hide(); - // reset state which needs reseting - this.state.dropdown.visible = undefined; - this.setStateSelection(); - } - return; - } - if (type == "focus") { - this.trigger("focus", eventData); - // e.target.classList.remove('placeholder'); - if (_s.dropdown.enabled === 0 || !_s.userInput) { - // && _s.mode != "select" - this.dropdown.show(this.value.length ? '' : undefined); - } - return; - } else if (type == "blur") { - this.trigger("blur", eventData); - this.loading(false); - - // when clicking the X button of a selected tag, it is unwanted for it to be added back - // again in a few more lines of code (shouldAddTags && addTags) - if (_s.mode == 'select') { - if (isRelatedTargetX) { - this.removeTags(); - text = ''; - } - - // if nothing has changed (same display value), do not add a tag - if (currentDisplayValue === text) text = ''; - } - shouldAddTags = text && !this.state.actions.selectOption && _s.addTagOnBlur; - - // do not add a tag if "selectOption" action was just fired (this means a tag was just added from the dropdown) - shouldAddTags && this.addTags(text, true); - } - this.DOM.input.removeAttribute('style'); - this.dropdown.hide(); - }, - onCompositionStart(e) { - this.state.composing = true; - }, - onCompositionEnd(e) { - this.state.composing = false; - }, - onWindowKeyDown(e) { - var focusedElm = document.activeElement, - isTag = isNodeTag.call(this, focusedElm), - isBelong = isTag && this.DOM.scope.contains(document.activeElement), - nextTag; - if (!isBelong) return; - nextTag = focusedElm.nextElementSibling; - switch (e.key) { - // remove tag if has focus - case 'Backspace': - { - if (!this.settings.readonly) { - this.removeTags(focusedElm); - (nextTag ? nextTag : this.DOM.input).focus(); - } - break; - } - - // edit tag if has focus - case 'Enter': - { - setTimeout(this.editTag.bind(this), 0, focusedElm); - break; - } - } - }, - onKeydown(e) { - var _s = this.settings; - - // ignore keys during IME composition or when user input is not allowed - if (this.state.composing || !_s.userInput) return; - if (_s.mode == 'select' && _s.enforceWhitelist && this.value.length && e.key != 'Tab') { - e.preventDefault(); - } - var s = this.trim(e.target.textContent); - this.trigger("keydown", { - event: e - }); - - /** - * ONLY FOR MIX-MODE: - */ - if (_s.mode == 'mix') { - switch (e.key) { - case 'Left': - case 'ArrowLeft': - { - // when left arrow was pressed, set a flag so when the dropdown is shown, right-arrow will be ignored - // because it seems likely the user wishes to use the arrows to move the caret - this.state.actions.ArrowLeft = true; - break; - } - case 'Delete': - case 'Backspace': - { - if (this.state.editing) return; - var sel = document.getSelection(), - deleteKeyTagDetected = e.key == 'Delete' && sel.anchorOffset == (sel.anchorNode.length || 0), - prevAnchorSibling = sel.anchorNode.previousSibling, - isCaretAfterTag = sel.anchorNode.nodeType == 1 || !sel.anchorOffset && prevAnchorSibling && prevAnchorSibling.nodeType == 1 && sel.anchorNode.previousSibling, - lastInputValue = decode(this.DOM.input.innerHTML), - lastTagElems = this.getTagElms(), - // isCaretInsideTag = sel.anchorNode.parentNode('.' + _s.classNames.tag), - tagBeforeCaret, - tagElmToBeDeleted, - firstTextNodeBeforeTag; - if (_s.backspace == 'edit' && isCaretAfterTag) { - tagBeforeCaret = sel.anchorNode.nodeType == 1 ? null : sel.anchorNode.previousElementSibling; - setTimeout(this.editTag.bind(this), 0, tagBeforeCaret); // timeout is needed to the last cahacrter in the edited tag won't get deleted - e.preventDefault(); // needed so the tag elm won't get deleted - return; - } - if (isChromeAndroidBrowser() && isCaretAfterTag) { - firstTextNodeBeforeTag = getfirstTextNode(isCaretAfterTag); - if (!isCaretAfterTag.hasAttribute('readonly')) isCaretAfterTag.remove(); // since this is Chrome, can safetly use this "new" DOM API - - // Android-Chrome wrongly hides the keyboard, and loses focus, - // so this hack below is needed to regain focus at the correct place: - this.DOM.input.focus(); - setTimeout(() => { - this.placeCaretAfterNode(firstTextNodeBeforeTag); - this.DOM.input.click(); - }); - return; - } - if (sel.anchorNode.nodeName == 'BR') return; - if ((deleteKeyTagDetected || isCaretAfterTag) && sel.anchorNode.nodeType == 1) { - if (sel.anchorOffset == 0) - // caret is at the very begining, before a tag - tagElmToBeDeleted = deleteKeyTagDetected // delete key pressed - ? lastTagElems[0] : null;else tagElmToBeDeleted = lastTagElems[Math.min(lastTagElems.length, sel.anchorOffset) - 1]; - - // find out if a tag *might* be a candidate for deletion, and if so, which - } else if (deleteKeyTagDetected) tagElmToBeDeleted = sel.anchorNode.nextElementSibling;else if (isCaretAfterTag) tagElmToBeDeleted = isCaretAfterTag; - - // tagElm.hasAttribute('readonly') - if (sel.anchorNode.nodeType == 3 && - // node at caret location is a Text node - !sel.anchorNode.nodeValue && - // has some text - sel.anchorNode.previousElementSibling) - // text node has a Tag node before it - e.preventDefault(); - - // if backspace not allowed, do nothing - // TODO: a better way to detect if nodes were deleted is to simply check the "this.value" before & after - if ((isCaretAfterTag || deleteKeyTagDetected) && !_s.backspace) { - e.preventDefault(); - return; - } - if (sel.type != 'Range' && !sel.anchorOffset && sel.anchorNode == this.DOM.input && e.key != 'Delete') { - e.preventDefault(); - return; - } - if (sel.type != 'Range' && tagElmToBeDeleted && tagElmToBeDeleted.hasAttribute('readonly')) { - // allows the continuation of deletion by placing the caret on the first previous textNode. - // since a few readonly-tags might be one after the other, iteration is needed: - - this.placeCaretAfterNode(getfirstTextNode(tagElmToBeDeleted)); - return; - } - - // update regarding https://github.com/yairEO/tagify/issues/762#issuecomment-786464317: - // the bug described is more severe than the fix below, therefore I disable the fix until a solution - // is found which work well for both cases. - // ------- - // nodeType is "1" only when the caret is at the end after last tag (no text after), or before first first (no text before) - /* - if( this.isFirefox && sel.anchorNode.nodeType == 1 && sel.anchorOffset != 0 ){ - this.removeTags() // removes last tag by default if no parameter supplied - // place caret inside last textNode, if exist. it's an annoying bug only in FF, - // if the last tag is removed, and there is a textNode before it, the caret is not placed at its end - this.placeCaretAfterNode( setRangeAtStartEnd(false, this.DOM.input) ) - } - */ - - clearTimeout(deleteBackspaceTimeout); - // a minimum delay is needed before the node actually gets detached from the document (don't know why), - // to know exactly which tag was deleted. This is the easiest way of knowing besides using MutationObserver - deleteBackspaceTimeout = setTimeout(() => { - var sel = document.getSelection(), - currentValue = decode(this.DOM.input.innerHTML), - prevElm = !deleteKeyTagDetected && sel.anchorNode.previousSibling; - - // fixes #384, where the first and only tag will not get removed with backspace - if (currentValue.length >= lastInputValue.length && prevElm) { - if (isNodeTag.call(this, prevElm) && !prevElm.hasAttribute('readonly')) { - this.removeTags(prevElm); - this.fixFirefoxLastTagNoCaret(); - - // the above "removeTag" methods removes the tag with a transition. Chrome adds a
element for some reason at this stage - if (this.DOM.input.children.length == 2 && this.DOM.input.children[1].tagName == "BR") { - this.DOM.input.innerHTML = ""; - this.value.length = 0; - return true; - } - } else prevElm.remove(); - } - - // find out which tag(s) were deleted and trigger "remove" event - // iterate over the list of tags still in the document and then filter only those from the "this.value" collection - this.value = [].map.call(lastTagElems, (node, nodeIdx) => { - var tagData = getSetTagData(node); - - // since readonly cannot be removed (it's technically resurrected if removed somehow) - if (node.parentNode || tagData.readonly) return tagData;else this.trigger('remove', { - tag: node, - index: nodeIdx, - data: tagData - }); - }).filter(n => n); // remove empty items in the mapped array - }, 20); // Firefox needs this higher duration for some reason or things get buggy when deleting text from the end - break; - } - // currently commented to allow new lines in mixed-mode - // case 'Enter' : - // // e.preventDefault(); // solves Chrome bug - http://stackoverflow.com/a/20398191/104380 - } - - return true; - } - switch (e.key) { - case 'Backspace': - if (_s.mode == 'select' && _s.enforceWhitelist && this.value.length) this.removeTags();else if (!this.state.dropdown.visible || _s.dropdown.position == 'manual') { - if (e.target.textContent == "" || s.charCodeAt(0) == 8203) { - // 8203: ZERO WIDTH SPACE unicode - if (_s.backspace === true) this.removeTags();else if (_s.backspace == 'edit') setTimeout(this.editTag.bind(this), 0); // timeout reason: when edited tag gets focused and the caret is placed at the end, the last character gets deletec (because of backspace) - } - } - - break; - case 'Esc': - case 'Escape': - if (this.state.dropdown.visible) return; - e.target.blur(); - break; - case 'Down': - case 'ArrowDown': - // if( _s.mode == 'select' ) // issue #333 - if (!this.state.dropdown.visible) this.dropdown.show(); - break; - case 'ArrowRight': - { - let tagData = this.state.inputSuggestion || this.state.ddItemData; - if (tagData && _s.autoComplete.rightKey) { - this.addTags([tagData], true); - return; - } - break; - } - case 'Tab': - { - let selectMode = _s.mode == 'select'; - if (s && !selectMode) e.preventDefault();else return true; - } - case 'Enter': - // manual suggestion boxes are assumed to always be visible - if (this.state.dropdown.visible && _s.dropdown.position != 'manual') return; - e.preventDefault(); // solves Chrome bug - http://stackoverflow.com/a/20398191/104380 - // because the main "keydown" event is bound before the dropdown events, this will fire first and will not *yet* - // know if an option was just selected from the dropdown menu. If an option was selected, - // the dropdown events should handle adding the tag - setTimeout(() => { - if (this.state.actions.selectOption) return; - this.addTags(s, true); - }); - } - }, - onInput(e) { - this.postUpdate(); // toggles "tagify--empty" class - - var _s = this.settings; - if (_s.mode == 'mix') return this.events.callbacks.onMixTagsInput.call(this, e); - var value = this.input.normalize.call(this), - showSuggestions = value.length >= _s.dropdown.enabled, - eventData = { - value, - inputElm: this.DOM.input - }, - validation = this.validateTag({ - value - }); - if (_s.mode == 'select') { - this.toggleScopeValidation(validation); - } - eventData.isValid = validation; - - // for IE; since IE doesn't have an "input" event so "keyDown" is used instead to trigger the "onInput" callback, - // and so many keys do not change the input, and for those do not continue. - if (this.state.inputText == value) return; - - // save the value on the input's State object - this.input.set.call(this, value, false); // update the input with the normalized value and run validations - // this.setRangeAtStartEnd(false, this.DOM.input); // fix caret position - - // if delimiters detected, add tags - if (value.search(_s.delimiters) != -1) { - if (this.addTags(value)) { - this.input.set.call(this); // clear the input field's value - } - } else if (_s.dropdown.enabled >= 0) { - this.dropdown[showSuggestions ? "show" : "hide"](value); - } - this.trigger('input', eventData); // "input" event must be triggered at this point, before the dropdown is shown - }, - - onMixTagsInput(e) { - var rangeText, - match, - matchedPatternCount, - tag, - showSuggestions, - selection, - _s = this.settings, - lastTagsCount = this.value.length, - matchFlaggedTag, - matchDelimiters, - tagsElems = this.getTagElms(), - fragment = document.createDocumentFragment(), - range = window.getSelection().getRangeAt(0), - remainingTagsValues = [].map.call(tagsElems, node => getSetTagData(node).value); - - // Android Chrome "keydown" event argument does not report the correct "key". - // this workaround is needed to manually call "onKeydown" method with a synthesized event object - if (e.inputType == "deleteContentBackward" && isChromeAndroidBrowser()) { - this.events.callbacks.onKeydown.call(this, { - target: e.target, - key: "Backspace" - }); - } - - // re-add "readonly" tags which might have been removed - this.value.slice().forEach(item => { - if (item.readonly && !remainingTagsValues.includes(item.value)) fragment.appendChild(this.createTagElem(item)); - }); - if (fragment.childNodes.length) { - range.insertNode(fragment); - this.setRangeAtStartEnd(false, fragment.lastChild); - } - - // check if tags were "magically" added/removed (browser redo/undo or CTRL-A -> delete) - if (tagsElems.length != lastTagsCount) { - this.value = [].map.call(this.getTagElms(), node => getSetTagData(node)); - this.update({ - withoutChangeEvent: true - }); - return; - } - if (this.hasMaxTags()) return true; - if (window.getSelection) { - selection = window.getSelection(); - - // only detect tags if selection is inside a textNode (not somehow on already-existing tag) - if (selection.rangeCount > 0 && selection.anchorNode.nodeType == 3) { - range = selection.getRangeAt(0).cloneRange(); - range.collapse(true); - range.setStart(selection.focusNode, 0); - rangeText = range.toString().slice(0, range.endOffset); // slice the range so everything AFTER the caret will be trimmed - // split = range.toString().split(_s.mixTagsAllowedAfter) // ["foo", "bar", "@baz"] - matchedPatternCount = rangeText.split(_s.pattern).length - 1; - match = rangeText.match(_s.pattern); - if (match) - // tag string, example: "@aaa ccc" - tag = rangeText.slice(rangeText.lastIndexOf(match[match.length - 1])); - if (tag) { - this.state.actions.ArrowLeft = false; // start fresh, assuming the user did not (yet) used any arrow to move the caret - this.state.tag = { - prefix: tag.match(_s.pattern)[0], - value: tag.replace(_s.pattern, '') // get rid of the prefix - }; - - this.state.tag.baseOffset = selection.baseOffset - this.state.tag.value.length; - matchDelimiters = this.state.tag.value.match(_s.delimiters); - // if a delimeter exists, add the value as tag (exluding the delimiter) - if (matchDelimiters) { - this.state.tag.value = this.state.tag.value.replace(_s.delimiters, ''); - this.state.tag.delimiters = matchDelimiters[0]; - this.addTags(this.state.tag.value, _s.dropdown.clearOnSelect); - this.dropdown.hide(); - return; - } - showSuggestions = this.state.tag.value.length >= _s.dropdown.enabled; - - // When writeing something that might look like a tag (an email address) but isn't one - it is unwanted - // the suggestions dropdown be shown, so the user closes it (in any way), and while continue typing, - // dropdown should stay closed until another tag is typed. - // if( this.state.tag.value.length && this.state.dropdown.visible === false ) - // showSuggestions = false - - // test for similar flagged tags to the current tag - - try { - matchFlaggedTag = this.state.flaggedTags[this.state.tag.baseOffset]; - matchFlaggedTag = matchFlaggedTag.prefix == this.state.tag.prefix && matchFlaggedTag.value[0] == this.state.tag.value[0]; - - // reset - if (this.state.flaggedTags[this.state.tag.baseOffset] && !this.state.tag.value) delete this.state.flaggedTags[this.state.tag.baseOffset]; - } catch (err) {} - - // scenario: (do not show suggestions of previous matched tag, if more than 1 detected) - // (2 tags exist) " a@a.com and @" - // (second tag is removed by backspace) " a@a.com and " - if (matchFlaggedTag || matchedPatternCount < this.state.mixMode.matchedPatternCount) showSuggestions = false; - } - // no (potential) tag found - else { - this.state.flaggedTags = {}; - } - this.state.mixMode.matchedPatternCount = matchedPatternCount; - } - } - - // wait until the "this.value" has been updated (see "onKeydown" method for "mix-mode") - // the dropdown must be shown only after this event has been driggered, so an implementer could - // dynamically change the whitelist. - setTimeout(() => { - this.update({ - withoutChangeEvent: true - }); - this.trigger("input", extend({}, this.state.tag, { - textContent: this.DOM.input.textContent - })); - if (this.state.tag) this.dropdown[showSuggestions ? "show" : "hide"](this.state.tag.value); - }, 10); - }, - onInputIE(e) { - var _this = this; - // for the "e.target.textContent" to be changed, the browser requires a small delay - setTimeout(function () { - _this.events.callbacks.onInput.call(_this, e); - }); - }, - observeOriginalInputValue() { - // if, for some reason, the Tagified element is no longer in the DOM, - // call the "destroy" method to kill all references to timeouts/intervals - if (!this.DOM.originalInput.parentNode) this.destroy(); - - // if original input value changed for some reason (for exmaple a form reset) - if (this.DOM.originalInput.value != this.DOM.originalInput.tagifyValue) this.loadOriginalValues(); - }, - onClickScope(e) { - var _s = this.settings, - tagElm = e.target.closest('.' + _s.classNames.tag), - timeDiffFocus = +new Date() - this.state.hasFocus; - if (e.target == this.DOM.scope) { - if (!this.state.hasFocus) this.DOM.input.focus(); - return; - } else if (e.target.classList.contains(_s.classNames.tagX)) { - this.removeTags(e.target.parentNode); - return; - } else if (tagElm) { - this.trigger("click", { - tag: tagElm, - index: this.getNodeIndex(tagElm), - data: getSetTagData(tagElm), - event: e - }); - if (_s.editTags === 1 || _s.editTags.clicks === 1) this.events.callbacks.onDoubleClickScope.call(this, e); - return; - } - - // when clicking on the input itself - else if (e.target == this.DOM.input) { - if (_s.mode == 'mix') { - // firefox won't show caret if last element is a tag (and not a textNode), - // so an empty textnode should be added - this.fixFirefoxLastTagNoCaret(); - } - if (timeDiffFocus > 500) { - if (this.state.dropdown.visible) this.dropdown.hide();else if (_s.dropdown.enabled === 0 && _s.mode != 'mix') this.dropdown.show(this.value.length ? '' : undefined); - return; - } - } - if (_s.mode == 'select' && _s.dropdown.enabled === 0 && !this.state.dropdown.visible) this.dropdown.show(); - }, - // special proccess is needed for pasted content in order to "clean" it - onPaste(e) { - e.preventDefault(); - var _s = this.settings, - selectModeWithoutInput = _s.mode == 'select' && _s.enforceWhitelist; - if (selectModeWithoutInput || !_s.userInput) { - return false; - } - var clipboardData, pastedText; - if (_s.readonly) return; - - // Get pasted data via clipboard API - clipboardData = e.clipboardData || window.clipboardData; - pastedText = clipboardData.getData('Text'); - _s.hooks.beforePaste(e, { - tagify: this, - pastedText, - clipboardData - }).then(result => { - if (result === undefined) result = pastedText; - if (result) { - this.injectAtCaret(result, window.getSelection().getRangeAt(0)); - if (this.settings.mode == 'mix') { - this.events.callbacks.onMixTagsInput.call(this, e); - } else if (this.settings.pasteAsTags) { - this.addTags(this.state.inputText + result, true); - } else this.state.inputText = result; - } - }).catch(err => err); - }, - onDrop(e) { - e.preventDefault(); - }, - onEditTagInput(editableElm, e) { - var tagElm = editableElm.closest('.' + this.settings.classNames.tag), - tagElmIdx = this.getNodeIndex(tagElm), - tagData = getSetTagData(tagElm), - textValue = this.input.normalize.call(this, editableElm), - dataForChangedProp = { - [this.settings.tagTextProp]: textValue, - __tagId: tagData.__tagId - }, - // "__tagId" is needed so validation will skip current tag when checking for dups - isValid = this.validateTag(dataForChangedProp), - // the value could have been invalid in the first-place so make sure to re-validate it (via "addEmptyTag" method) - hasChanged = this.editTagChangeDetected(extend(tagData, dataForChangedProp)); - - // if the value is same as before-editing and the tag was valid before as well, ignore the current "isValid" result, which is false-positive - if (!hasChanged && editableElm.originalIsValid === true) isValid = true; - tagElm.classList.toggle(this.settings.classNames.tagInvalid, isValid !== true); - tagData.__isValid = isValid; - tagElm.title = isValid === true ? tagData.title || tagData.value : isValid; // change the tag's title to indicate why is the tag invalid (if it's so) - - // show dropdown if typed text is equal or more than the "enabled" dropdown setting - if (textValue.length >= this.settings.dropdown.enabled) { - // this check is needed apparently because doing browser "undo" will fire - // "onEditTagInput" but "this.state.editing" will be "false" - if (this.state.editing) this.state.editing.value = textValue; - this.dropdown.show(textValue); - } - this.trigger("edit:input", { - tag: tagElm, - index: tagElmIdx, - data: extend({}, this.value[tagElmIdx], { - newValue: textValue - }), - event: e - }); - }, - onEditTagPaste(tagElm, e) { - // Get pasted data via clipboard API - var clipboardData = e.clipboardData || window.clipboardData, - pastedText = clipboardData.getData('Text'); - e.preventDefault(); - var newNode = injectAtCaret(pastedText); - this.setRangeAtStartEnd(false, newNode); - }, - onEditTagFocus(tagElm) { - this.state.editing = { - scope: tagElm, - input: tagElm.querySelector("[contenteditable]") - }; - }, - onEditTagBlur(editableElm) { - if (!this.state.hasFocus) this.toggleFocusClass(); - - // one scenario is when selecting a suggestion from the dropdown, when editing, and by selecting it - // the "onEditTagDone" is called directly, already replacing the tag, so the argument "editableElm" - // node isn't in the DOM anynmore because it has been replaced. - if (!this.DOM.scope.contains(editableElm)) return; - var _s = this.settings, - tagElm = editableElm.closest('.' + _s.classNames.tag), - textValue = this.input.normalize.call(this, editableElm), - tagData = getSetTagData(tagElm), - originalData = tagData.__originalData, - // pre-edit data - hasChanged = this.editTagChangeDetected(tagData), - isValid = this.validateTag({ - [_s.tagTextProp]: textValue, - __tagId: tagData.__tagId - }), - // "__tagId" is needed so validation will skip current tag when checking for dups - hasMaxTags, - newTagData; - if (!textValue) { - this.onEditTagDone(tagElm); - return; - } - - // if nothing changed revert back to how it was before editing - if (!hasChanged) { - this.onEditTagDone(tagElm, originalData); - return; - } - - // need to know this because if "keepInvalidTags" setting is "true" and an invalid tag is edited as a valid one, - // but the maximum number of tags have alreay been reached, so it should not allow saving the new valid value. - // only if the tag was already valid before editing, ignore this check (see a few lines below) - hasMaxTags = this.hasMaxTags(); - newTagData = extend({}, originalData, { - [_s.tagTextProp]: this.trim(textValue), - __isValid: isValid - }); - - // pass through optional transformer defined in settings - _s.transformTag.call(this, newTagData, originalData); - - // MUST re-validate after tag transformation - // only validate the "tagTextProp" because is the only thing that metters for validating an edited tag. - // -- Scenarios: -- - // 1. max 3 tags allowd. there are 4 tags, one has invalid input and is edited to a valid one, and now should be marked as "not allowed" because limit of tags has reached - // 2. max 3 tags allowed. there are 3 tags, one is edited, and so max-tags vaildation should be OK - isValid = (!hasMaxTags || originalData.__isValid === true) && this.validateTag(newTagData); - if (isValid !== true) { - this.trigger("invalid", { - data: newTagData, - tag: tagElm, - message: isValid - }); - - // do nothing if invalid, stay in edit-mode until corrected or reverted by presssing esc - if (_s.editTags.keepInvalid) return; - if (_s.keepInvalidTags) newTagData.__isValid = isValid;else - // revert back if not specified to keep - newTagData = originalData; - } else if (_s.keepInvalidTags) { - // cleaup any previous leftovers if the tag was invalid - delete newTagData.title; - delete newTagData["aria-invalid"]; - delete newTagData.class; - } - - // tagElm.classList.toggle(_s.classNames.tagInvalid, true) - - this.onEditTagDone(tagElm, newTagData); - }, - onEditTagkeydown(e, tagElm) { - // ignore keys during IME composition - if (this.state.composing) return; - this.trigger("edit:keydown", { - event: e - }); - switch (e.key) { - case 'Esc': - case 'Escape': - { - // revert the tag to how it was before editing - // replace current tag with original one (pre-edited one) - tagElm.parentNode.replaceChild(tagElm.__tagifyTagData.__originalHTML, tagElm); - this.state.editing = false; - } - case 'Enter': - case 'Tab': - e.preventDefault(); - e.target.blur(); - } - }, - onDoubleClickScope(e) { - var tagElm = e.target.closest('.' + this.settings.classNames.tag), - tagData = getSetTagData(tagElm), - _s = this.settings, - isEditingTag, - isReadyOnlyTag; - if (!tagElm || !_s.userInput || tagData.editable === false) return; - isEditingTag = tagElm.classList.contains(this.settings.classNames.tagEditing); - isReadyOnlyTag = tagElm.hasAttribute('readonly'); - if (_s.mode != 'select' && !_s.readonly && !isEditingTag && !isReadyOnlyTag && this.settings.editTags) this.editTag(tagElm); - this.toggleFocusClass(true); - this.trigger('dblclick', { - tag: tagElm, - index: this.getNodeIndex(tagElm), - data: getSetTagData(tagElm) - }); - }, - /** - * - * @param {Object} m an object representing the observed DOM changes - */ - onInputDOMChange(m) { - // iterate all DOm mutation - m.forEach(record => { - // only the ADDED nodes - record.addedNodes.forEach(addedNode => { - // fix chrome's placing '

' everytime ENTER key is pressed, and replace with just `
') { - addedNode.replaceWith(document.createElement('br')); - } - - // if the added element is a div containing a tag within it (chrome does this when pressing ENTER before a tag) - else if (addedNode.nodeType == 1 && addedNode.querySelector(this.settings.classNames.tagSelector)) { - let newlineText = document.createTextNode(''); - if (addedNode.childNodes[0].nodeType == 3 && addedNode.previousSibling.nodeName != 'BR') newlineText = document.createTextNode('\n'); - - // unwrap the useless div - // chrome adds a BR at the end which should be removed - addedNode.replaceWith(...[newlineText, ...[...addedNode.childNodes].slice(0, -1)]); - this.placeCaretAfterNode(newlineText); - } - - // if this is a tag - else if (isNodeTag.call(this, addedNode)) { - var _addedNode$previousSi; - if (((_addedNode$previousSi = addedNode.previousSibling) === null || _addedNode$previousSi === void 0 ? void 0 : _addedNode$previousSi.nodeType) == 3 && !addedNode.previousSibling.textContent) addedNode.previousSibling.remove(); - // and it is the first node in a new line - if (addedNode.previousSibling && addedNode.previousSibling.nodeName == 'BR') { - // allows placing the caret just before the tag, when the tag is the first node in that line - addedNode.previousSibling.replaceWith('\n\u200B'); - let nextNode = addedNode.nextSibling, - anythingAfterNode = ''; - while (nextNode) { - anythingAfterNode += nextNode.textContent; - nextNode = nextNode.nextSibling; - } - - // when hitting ENTER for new line just before an existing tag, but skip below logic when a tag has been addded - anythingAfterNode.trim() && this.placeCaretAfterNode(addedNode.previousSibling); - } - } - }); - record.removedNodes.forEach(removedNode => { - // when trying to delete a tag which is in a new line and there's nothing else there (caret is after the tag) - if (removedNode && removedNode.nodeName == 'BR' && isNodeTag.call(this, lastInputChild)) { - this.removeTags(lastInputChild); - this.fixFirefoxLastTagNoCaret(); - } - }); - }); - - // get the last child only after the above DOM modifications - // check these scenarios: - // 1. after a single line, press ENTER once - should add only 1 BR - // 2. presss ENTER right before a tag - // 3. press enter within a text node before a tag - var lastInputChild = this.DOM.input.lastChild; - if (lastInputChild && lastInputChild.nodeValue == '') lastInputChild.remove(); - - // make sure the last element is always a BR - if (!lastInputChild || lastInputChild.nodeName != 'BR') { - this.DOM.input.appendChild(document.createElement('br')); - } - } - } - }; - - /** - * @constructor - * @param {Object} input DOM element - * @param {Object} settings settings object - */ - function Tagify(input, settings) { - if (!input) { - console.warn('Tagify:', 'input element not found', input); - // return an empty mock of all methods, so the code using tagify will not break - // because it might be calling methods even though the input element does not exist - const mockInstance = new Proxy(this, { - get() { - return () => mockInstance; - } - }); - return mockInstance; - } - if (input.__tagify) { - console.warn('Tagify: ', 'input element is already Tagified - Same instance is returned.', input); - return input.__tagify; - } - extend(this, EventDispatcher(this)); - this.isFirefox = /firefox|fxios/i.test(navigator.userAgent) && !/seamonkey/i.test(navigator.userAgent); - this.isIE = window.document.documentMode; // https://developer.mozilla.org/en-US/docs/Web/API/Document/compatMode#Browser_compatibility - - settings = settings || {}; - this.getPersistedData = getPersistedData(settings.id); - this.setPersistedData = setPersistedData(settings.id); - this.clearPersistedData = clearPersistedData(settings.id); - this.applySettings(input, settings); - this.state = { - inputText: '', - editing: false, - composing: false, - actions: {}, - // UI actions for state-locking - mixMode: {}, - dropdown: {}, - flaggedTags: {} // in mix-mode, when a string is detetced as potential tag, and the user has chocen to close the suggestions dropdown, keep the record of the tasg here - }; - - this.value = []; // tags' data - - // events' callbacks references will be stores here, so events could be unbinded - this.listeners = {}; - this.DOM = {}; // Store all relevant DOM elements in an Object - - this.build(input); - initDropdown.call(this); - this.getCSSVars(); - this.loadOriginalValues(); - this.events.customBinding.call(this); - this.events.binding.call(this); - input.autofocus && this.DOM.input.focus(); - input.__tagify = this; - } - Tagify.prototype = { - _dropdown, - helpers: { - sameStr, - removeCollectionProp, - omit, - isObject, - parseHTML, - escapeHTML, - extend, - concatWithoutDups, - getUID, - isNodeTag - }, - customEventsList: ['change', 'add', 'remove', 'invalid', 'input', 'click', 'keydown', 'focus', 'blur', 'edit:input', 'edit:beforeUpdate', 'edit:updated', 'edit:start', 'edit:keydown', 'dropdown:show', 'dropdown:hide', 'dropdown:select', 'dropdown:updated', 'dropdown:noMatch', 'dropdown:scroll'], - dataProps: ['__isValid', '__removed', '__originalData', '__originalHTML', '__tagId'], - // internal-uasge props - - trim(text) { - return this.settings.trim && text && typeof text == "string" ? text.trim() : text; - }, - // expose this handy utility function - parseHTML, - templates, - parseTemplate(template, data) { - template = this.settings.templates[template] || template; - return parseHTML(template.apply(this, data)); - }, - set whitelist(arr) { - const isArray = arr && Array.isArray(arr); - this.settings.whitelist = isArray ? arr : []; - this.setPersistedData(isArray ? arr : [], 'whitelist'); - }, - get whitelist() { - return this.settings.whitelist; - }, - generateClassSelectors(classNames) { - for (let name in classNames) { - let currentName = name; - Object.defineProperty(classNames, currentName + "Selector", { - get() { - return "." + this[currentName].split(" ")[0]; - } - }); - } - }, - applySettings(input, settings) { - DEFAULTS.templates = this.templates; - var _s = this.settings = extend({}, DEFAULTS, settings); - _s.disabled = input.hasAttribute('disabled'); - _s.readonly = _s.readonly || input.hasAttribute('readonly'); - _s.placeholder = escapeHTML(input.getAttribute('placeholder') || _s.placeholder || ""); - _s.required = input.hasAttribute('required'); - this.generateClassSelectors(_s.classNames); - if (_s.dropdown.includeSelectedTags === undefined) _s.dropdown.includeSelectedTags = _s.duplicates; - if (this.isIE) _s.autoComplete = false; // IE goes crazy if this isn't false - - ["whitelist", "blacklist"].forEach(name => { - var attrVal = input.getAttribute('data-' + name); - if (attrVal) { - attrVal = attrVal.split(_s.delimiters); - if (attrVal instanceof Array) _s[name] = attrVal; - } - }); - - // backward-compatibility for old version of "autoComplete" setting: - if ("autoComplete" in settings && !isObject(settings.autoComplete)) { - _s.autoComplete = DEFAULTS.autoComplete; - _s.autoComplete.enabled = settings.autoComplete; - } - if (_s.mode == 'mix') { - _s.autoComplete.rightKey = true; - _s.delimiters = settings.delimiters || null; // default dlimiters in mix-mode must be NULL - - // needed for "filterListItems". This assumes the user might have forgotten to manually - // define the same term in "dropdown.searchKeys" as defined in "tagTextProp" setting, so - // by automatically adding it, tagify is "helping" out, guessing the intesntions of the developer. - if (_s.tagTextProp && !_s.dropdown.searchKeys.includes(_s.tagTextProp)) _s.dropdown.searchKeys.push(_s.tagTextProp); - } - if (input.pattern) try { - _s.pattern = new RegExp(input.pattern); - } catch (e) {} - - // Convert the "delimiters" setting into a REGEX object - if (_s.delimiters) { - _s._delimiters = _s.delimiters; - try { - _s.delimiters = new RegExp(this.settings.delimiters, "g"); - } catch (e) {} - } - if (_s.disabled) _s.userInput = false; - this.TEXTS = _objectSpread2(_objectSpread2({}, TEXTS), _s.texts || {}); - - // make sure the dropdown will be shown on "focus" and not only after typing something (in "select" mode) - if (_s.mode == 'select' && !settings.dropdown.enabled || !_s.userInput) { - _s.dropdown.enabled = 0; - } - _s.dropdown.appendTarget = settings.dropdown && settings.dropdown.appendTarget ? settings.dropdown.appendTarget : document.body; - - // get & merge persisted data with current data - let persistedWhitelist = this.getPersistedData('whitelist'); - if (Array.isArray(persistedWhitelist)) this.whitelist = Array.isArray(_s.whitelist) ? concatWithoutDups(_s.whitelist, persistedWhitelist) : persistedWhitelist; - }, - /** - * Returns a string of HTML element attributes - * @param {Object} data [Tag data] - */ - getAttributes(data) { - var attrs = this.getCustomAttributes(data), - s = '', - k; - for (k in attrs) s += " " + k + (data[k] !== undefined ? `="${attrs[k]}"` : ""); - return s; - }, - /** - * Returns an object of attributes to be used for the templates - */ - getCustomAttributes(data) { - // only items which are objects have properties which can be used as attributes - if (!isObject(data)) return ''; - var output = {}, - propName; - for (propName in data) { - if (propName.slice(0, 2) != '__' && propName != 'class' && data.hasOwnProperty(propName) && data[propName] !== undefined) output[propName] = escapeHTML(data[propName]); - } - return output; - }, - setStateSelection() { - var selection = window.getSelection(); - - // save last selection place to be able to inject anything from outside to that specific place - var sel = { - anchorOffset: selection.anchorOffset, - anchorNode: selection.anchorNode, - range: selection.getRangeAt && selection.rangeCount && selection.getRangeAt(0) - }; - this.state.selection = sel; - return sel; - }, - /** - * Get specific CSS variables which are relevant to this script and parse them as needed. - * The result is saved on the instance in "this.CSSVars" - */ - getCSSVars() { - var compStyle = getComputedStyle(this.DOM.scope, null); - const getProp = name => compStyle.getPropertyValue('--' + name); - function seprateUnitFromValue(a) { - if (!a) return {}; - a = a.trim().split(' ')[0]; - var unit = a.split(/\d+/g).filter(n => n).pop().trim(), - value = +a.split(unit).filter(n => n)[0].trim(); - return { - value, - unit - }; - } - this.CSSVars = { - tagHideTransition: (_ref => { - let value = _ref.value, - unit = _ref.unit; - return unit == 's' ? value * 1000 : value; - })(seprateUnitFromValue(getProp('tag-hide-transition'))) - }; - }, - /** - * builds the HTML of this component - * @param {Object} input [DOM element which would be "transformed" into "Tags"] - */ - build(input) { - var DOM = this.DOM; - if (this.settings.mixMode.integrated) { - DOM.originalInput = null; - DOM.scope = input; - DOM.input = input; - } else { - DOM.originalInput = input; - DOM.originalInput_tabIndex = input.tabIndex; - DOM.scope = this.parseTemplate('wrapper', [input, this.settings]); - DOM.input = DOM.scope.querySelector(this.settings.classNames.inputSelector); - input.parentNode.insertBefore(DOM.scope, input); - input.tabIndex = -1; // do not allow focus or typing directly, once tagified - } - }, - - /** - * revert any changes made by this component - */ - destroy() { - this.events.unbindGlobal.call(this); - this.DOM.scope.parentNode.removeChild(this.DOM.scope); - this.DOM.originalInput.tabIndex = this.DOM.originalInput_tabIndex; - delete this.DOM.originalInput.__tagify; - this.dropdown.hide(true); - clearTimeout(this.dropdownHide__bindEventsTimeout); - clearInterval(this.listeners.main.originalInputValueObserverInterval); - }, - /** - * if the original input has any values, add them as tags - */ - loadOriginalValues(value) { - var lastChild, - _s = this.settings; - - // temporarily block firign the "change" event on the original input unil - // this method finish removing current value and adding the new one - this.state.blockChangeEvent = true; - if (value === undefined) { - const persistedOriginalValue = this.getPersistedData('value'); - - // if the field already has a field, trust its the desired - // one to be rendered and do not use the persisted one - if (persistedOriginalValue && !this.DOM.originalInput.value) value = persistedOriginalValue;else value = _s.mixMode.integrated ? this.DOM.input.textContent : this.DOM.originalInput.value; - } - this.removeAllTags(); - if (value) { - if (_s.mode == 'mix') { - this.parseMixTags(value); - lastChild = this.DOM.input.lastChild; - - // fixes a Chrome bug, when the last node in `mix-mode` is a tag, the caret appears at the far-top-top, outside the field - if (!lastChild || lastChild.tagName != 'BR') this.DOM.input.insertAdjacentHTML('beforeend', '
'); - } else { - try { - if (JSON.parse(value) instanceof Array) value = JSON.parse(value); - } catch (err) {} - this.addTags(value, true).forEach(tag => tag && tag.classList.add(_s.classNames.tagNoAnimation)); - } - } else this.postUpdate(); - this.state.lastOriginalValueReported = _s.mixMode.integrated ? '' : this.DOM.originalInput.value; - this.state.blockChangeEvent = false; - }, - cloneEvent(e) { - var clonedEvent = {}; - for (var v in e) if (v != 'path') clonedEvent[v] = e[v]; - return clonedEvent; - }, - /** - * Toogle global loading state on/off - * Useful when fetching async whitelist while user is typing - * @param {Boolean} isLoading - */ - loading(isLoading) { - this.state.isLoading = isLoading; - // IE11 doesn't support toggle with second parameter - this.DOM.scope.classList[isLoading ? "add" : "remove"](this.settings.classNames.scopeLoading); - return this; - }, - /** - * Toogle a tag loading state on/off - * @param {Boolean} isLoading - */ - tagLoading(tagElm, isLoading) { - if (tagElm) - // IE11 doesn't support toggle with second parameter - tagElm.classList[isLoading ? "add" : "remove"](this.settings.classNames.tagLoading); - return this; - }, - /** - * Toggles class on the main tagify container ("scope") - * @param {String} className - * @param {Boolean} force - */ - toggleClass(className, force) { - if (typeof className == 'string') this.DOM.scope.classList.toggle(className, force); - }, - toggleScopeValidation(validation) { - var isValid = validation === true || validation === undefined; // initially it is undefined - - if (!this.settings.required && validation && validation === this.TEXTS.empty) isValid = true; - this.toggleClass(this.settings.classNames.tagInvalid, !isValid); - this.DOM.scope.title = isValid ? '' : validation; - }, - toggleFocusClass(force) { - this.toggleClass(this.settings.classNames.focus, !!force); - }, - triggerChangeEvent, - events, - fixFirefoxLastTagNoCaret() { - return; // seems to be fixed in newer version of FF, so retiring below code (for now) - }, - /** https://stackoverflow.com/a/59156872/104380 - * @param {Boolean} start indicating where to place it (start or end of the node) - * @param {Object} node DOM node to place the caret at - */ - setRangeAtStartEnd(start, node) { - start = typeof start == 'number' ? start : !!start; - node = node.lastChild || node; - var sel = document.getSelection(); - - // do not force caret placement if the current selection (focus) is on another element (not this tagify instance) - if (sel.focusNode && sel.focusNode !== this.DOM.input) { - return true; - } - try { - if (sel.rangeCount >= 1) { - ['Start', 'End'].forEach(pos => sel.getRangeAt(0)["set" + pos](node, start ? start : node.length)); - } - } catch (err) { - // console.warn("Tagify: ", err) - } - }, - placeCaretAfterNode(node) { - if (!node || !node.parentNode) return; - var nextSibling = node, - sel = window.getSelection(), - range = sel.getRangeAt(0); - if (sel.rangeCount) { - range.setStartAfter(nextSibling || node); - range.collapse(true); - // range.setEndBefore(nextSibling || node); - sel.removeAllRanges(); - sel.addRange(range); - } - }, - insertAfterTag(tagElm, newNode) { - newNode = newNode || this.settings.mixMode.insertAfterTag; - if (!tagElm || !tagElm.parentNode || !newNode) return; - newNode = typeof newNode == 'string' ? document.createTextNode(newNode) : newNode; - tagElm.parentNode.insertBefore(newNode, tagElm.nextSibling); - return newNode; - }, - // compares all "__originalData" property values with the current "tagData" properties - // and returns "true" if something changed. - editTagChangeDetected(tagData) { - var originalData = tagData.__originalData; - for (var prop in originalData) if (!this.dataProps.includes(prop) && tagData[prop] != originalData[prop]) return true; - return false; // not changed - }, - - // returns the node which has the actual tag's content - getTagTextNode(tagElm) { - return tagElm.querySelector(this.settings.classNames.tagTextSelector); - }, - // sets the text of a tag - setTagTextNode(tagElm, HTML) { - this.getTagTextNode(tagElm).innerHTML = escapeHTML(HTML); - }, - /** - * Enters a tag into "edit" mode - * @param {Node} tagElm the tag element to edit. if nothing specified, use last last - */ - editTag(tagElm, opts) { - tagElm = tagElm || this.getLastTag(); - opts = opts || {}; - this.dropdown.hide(); - var _s = this.settings, - editableElm = this.getTagTextNode(tagElm), - tagIdx = this.getNodeIndex(tagElm), - tagData = getSetTagData(tagElm), - _CB = this.events.callbacks, - that = this, - isValid = true, - delayed_onEditTagBlur = function () { - setTimeout(() => _CB.onEditTagBlur.call(that, that.getTagTextNode(tagElm))); - }; - if (!editableElm) { - console.warn('Cannot find element in Tag template: .', _s.classNames.tagTextSelector); - return; - } - if (tagData instanceof Object && "editable" in tagData && !tagData.editable) return; - - // cache the original data, on the DOM node, before any modification ocurs, for possible revert - tagData = getSetTagData(tagElm, { - __originalData: extend({}, tagData), - __originalHTML: tagElm.cloneNode(true) - }); - // re-set the tagify custom-prop on the clones element (because cloning removed it) - getSetTagData(tagData.__originalHTML, tagData.__originalData); - editableElm.setAttribute('contenteditable', true); - tagElm.classList.add(_s.classNames.tagEditing); - editableElm.addEventListener('focus', _CB.onEditTagFocus.bind(this, tagElm)); - editableElm.addEventListener('blur', delayed_onEditTagBlur); - editableElm.addEventListener('input', _CB.onEditTagInput.bind(this, editableElm)); - editableElm.addEventListener('paste', _CB.onEditTagPaste.bind(this, editableElm)); - editableElm.addEventListener('keydown', e => _CB.onEditTagkeydown.call(this, e, tagElm)); - editableElm.addEventListener('compositionstart', _CB.onCompositionStart.bind(this)); - editableElm.addEventListener('compositionend', _CB.onCompositionEnd.bind(this)); - if (!opts.skipValidation) isValid = this.editTagToggleValidity(tagElm); - editableElm.originalIsValid = isValid; - this.trigger("edit:start", { - tag: tagElm, - index: tagIdx, - data: tagData, - isValid - }); - editableElm.focus(); - this.setRangeAtStartEnd(false, editableElm); // place the caret at the END of the editable tag text - - return this; - }, - /** - * If a tag is invalid, for any reason, set its class to "not allowed" (see defaults file) - * @param {Node} tagElm required - * @param {Object} tagData optional - * @returns true if valid, a string (reason) if not - */ - editTagToggleValidity(tagElm, tagData) { - var tagData = tagData || getSetTagData(tagElm), - isValid; - if (!tagData) { - console.warn("tag has no data: ", tagElm, tagData); - return; - } - isValid = !("__isValid" in tagData) || tagData.__isValid === true; - if (!isValid) { - this.removeTagsFromValue(tagElm); - } - this.update(); - - //this.validateTag(tagData); - - tagElm.classList.toggle(this.settings.classNames.tagNotAllowed, !isValid); - return tagData.__isValid; - }, - onEditTagDone(tagElm, tagData) { - tagElm = tagElm || this.state.editing.scope; - tagData = tagData || {}; - var eventData = { - tag: tagElm, - index: this.getNodeIndex(tagElm), - previousData: getSetTagData(tagElm), - data: tagData - }; - this.trigger("edit:beforeUpdate", eventData, { - cloneData: false - }); - this.state.editing = false; - delete tagData.__originalData; - delete tagData.__originalHTML; - if (tagElm && tagData[this.settings.tagTextProp]) { - tagElm = this.replaceTag(tagElm, tagData); - this.editTagToggleValidity(tagElm, tagData); - if (this.settings.a11y.focusableTags) tagElm.focus();else - // place caret after edited tag - this.placeCaretAfterNode(tagElm); - } else if (tagElm) this.removeTags(tagElm); - this.trigger("edit:updated", eventData); - this.dropdown.hide(); - - // check if any of the current tags which might have been marked as "duplicate" should be now un-marked - if (this.settings.keepInvalidTags) this.reCheckInvalidTags(); - }, - /** - * Replaces an exisitng tag with a new one. Used for updating a tag's data - * @param {Object} tagElm [DOM node to replace] - * @param {Object} tagData [data to create new tag from] - */ - replaceTag(tagElm, tagData) { - if (!tagData || !tagData.value) tagData = tagElm.__tagifyTagData; - - // if tag is invalid, make the according changes in the newly created element - if (tagData.__isValid && tagData.__isValid != true) extend(tagData, this.getInvalidTagAttrs(tagData, tagData.__isValid)); - var newTagElm = this.createTagElem(tagData); - - // update DOM - tagElm.parentNode.replaceChild(newTagElm, tagElm); - this.updateValueByDOMTags(); - return newTagElm; - }, - /** - * update "value" (Array of Objects) by traversing all valid tags - */ - updateValueByDOMTags() { - this.value.length = 0; - [].forEach.call(this.getTagElms(), node => { - if (node.classList.contains(this.settings.classNames.tagNotAllowed.split(' ')[0])) return; - this.value.push(getSetTagData(node)); - }); - this.update(); - }, - /** - * injects nodes/text at caret position, which is saved on the "state" when "blur" event gets triggered - * @param {Node} injectedNode [the node to inject at the caret position] - * @param {Object} selection [optional range Object. must have "anchorNode" & "anchorOffset"] - */ - injectAtCaret(injectedNode, range) { - var _this$state$selection; - range = range || ((_this$state$selection = this.state.selection) === null || _this$state$selection === void 0 ? void 0 : _this$state$selection.range); - if (!range && injectedNode) { - this.appendMixTags(injectedNode); - return this; - } - injectAtCaret(injectedNode, range); - this.setRangeAtStartEnd(false, content); - this.updateValueByDOMTags(); // updates internal "this.value" - this.update(); // updates original input/textarea - - return this; - }, - /** - * input bridge for accessing & setting - * @type {Object} - */ - input: { - set() { - let s = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; - let updateDOM = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - var hideDropdown = this.settings.dropdown.closeOnSelect; - this.state.inputText = s; - if (updateDOM) this.DOM.input.innerHTML = escapeHTML("" + s); - if (!s && hideDropdown) this.dropdown.hide.bind(this); - this.input.autocomplete.suggest.call(this); - this.input.validate.call(this); - }, - raw() { - return this.DOM.input.textContent; - }, - /** - * Marks the tagify's input as "invalid" if the value did not pass "validateTag()" - */ - validate() { - var isValid = !this.state.inputText || this.validateTag({ - value: this.state.inputText - }) === true; - this.DOM.input.classList.toggle(this.settings.classNames.inputInvalid, !isValid); - return isValid; - }, - // remove any child DOM elements that aren't of type TEXT (like
) - normalize(node) { - var clone = node || this.DOM.input, - //.cloneNode(true), - v = []; - - // when a text was pasted in FF, the "this.DOM.input" element will have
but no newline symbols (\n), and this will - // result in tags not being properly created if one wishes to create a separate tag per newline. - clone.childNodes.forEach(n => n.nodeType == 3 && v.push(n.nodeValue)); - v = v.join("\n"); - try { - // "delimiters" might be of a non-regex value, where this will fail ("Tags With Properties" example in demo page): - v = v.replace(/(?:\r\n|\r|\n)/g, this.settings.delimiters.source.charAt(0)); - } catch (err) {} - v = v.replace(/\s/g, ' '); // replace NBSPs with spaces characters - - return this.trim(v); - }, - /** - * suggest the rest of the input's value (via CSS "::after" using "content:attr(...)") - * @param {String} s [description] - */ - autocomplete: { - suggest(data) { - if (!this.settings.autoComplete.enabled) return; - data = data || {}; - if (typeof data == 'string') data = { - value: data - }; - var suggestedText = data.value ? '' + data.value : '', - suggestionStart = suggestedText.substr(0, this.state.inputText.length).toLowerCase(), - suggestionTrimmed = suggestedText.substring(this.state.inputText.length); - if (!suggestedText || !this.state.inputText || suggestionStart != this.state.inputText.toLowerCase()) { - this.DOM.input.removeAttribute("data-suggest"); - delete this.state.inputSuggestion; - } else { - this.DOM.input.setAttribute("data-suggest", suggestionTrimmed); - this.state.inputSuggestion = data; - } - }, - /** - * sets the suggested text as the input's value & cleanup the suggestion autocomplete. - * @param {String} s [text] - */ - set(s) { - var dataSuggest = this.DOM.input.getAttribute('data-suggest'), - suggestion = s || (dataSuggest ? this.state.inputText + dataSuggest : null); - if (suggestion) { - if (this.settings.mode == 'mix') { - this.replaceTextWithNode(document.createTextNode(this.state.tag.prefix + suggestion)); - } else { - this.input.set.call(this, suggestion); - this.setRangeAtStartEnd(false, this.DOM.input); - } - this.input.autocomplete.suggest.call(this); - this.dropdown.hide(); - return true; - } - return false; - } - } - }, - /** - * returns the index of the the tagData within the "this.value" array collection. - * since values should be unique, it is suffice to only search by "value" property - * @param {Object} tagData - */ - getTagIdx(tagData) { - return this.value.findIndex(item => item.__tagId == (tagData || {}).__tagId); - }, - getNodeIndex(node) { - var index = 0; - if (node) while (node = node.previousElementSibling) index++; - return index; - }, - getTagElms() { - for (var _len = arguments.length, classess = new Array(_len), _key = 0; _key < _len; _key++) { - classess[_key] = arguments[_key]; - } - var classname = '.' + [...this.settings.classNames.tag.split(' '), ...classess].join('.'); - return [].slice.call(this.DOM.scope.querySelectorAll(classname)); // convert nodeList to Array - https://stackoverflow.com/a/3199627/104380 - }, - - /** - * gets the last non-readonly, not-in-the-proccess-of-removal tag - */ - getLastTag() { - var lastTag = this.DOM.scope.querySelectorAll(`${this.settings.classNames.tagSelector}:not(.${this.settings.classNames.tagHide}):not([readonly])`); - return lastTag[lastTag.length - 1]; - }, - /** - * Searches if any tag with a certain value already exis - * @param {String/Object} value [text value / tag data object] - * @param {Boolean} caseSensitive - * @return {Number} - */ - isTagDuplicate(value, caseSensitive, tagId) { - var dupsCount = 0, - _s = this.settings; - - // duplications are irrelevant for this scenario - if (_s.mode == 'select') return false; - for (let item of this.value) { - let isSameStr = sameStr(this.trim("" + value), item.value, caseSensitive); - if (isSameStr && tagId != item.__tagId) dupsCount++; - } - return dupsCount; - }, - getTagIndexByValue(value) { - var indices = []; - this.getTagElms().forEach((tagElm, i) => { - if (sameStr(this.trim(tagElm.textContent), value, this.settings.dropdown.caseSensitive)) indices.push(i); - }); - return indices; - }, - getTagElmByValue(value) { - var tagIdx = this.getTagIndexByValue(value)[0]; - return this.getTagElms()[tagIdx]; - }, - /** - * Temporarily marks a tag element (by value or Node argument) - * @param {Object} tagElm [a specific "tag" element to compare to the other tag elements siblings] - */ - flashTag(tagElm) { - if (tagElm) { - tagElm.classList.add(this.settings.classNames.tagFlash); - setTimeout(() => { - tagElm.classList.remove(this.settings.classNames.tagFlash); - }, 100); - } - }, - /** - * checks if text is in the blacklist - */ - isTagBlacklisted(v) { - v = this.trim(v.toLowerCase()); - return this.settings.blacklist.filter(x => ("" + x).toLowerCase() == v).length; - }, - /** - * checks if text is in the whitelist - */ - isTagWhitelisted(v) { - return !!this.getWhitelistItem(v); - /* - return this.settings.whitelist.some(item => - typeof v == 'string' - ? sameStr(this.trim(v), (item.value || item)) - : sameStr(JSON.stringify(item), JSON.stringify(v)) - ) - */ - }, - - /** - * Returns the first whitelist item matched, by value (if match found) - * @param {String} value [text to match by] - */ - getWhitelistItem(value, prop, whitelist) { - var result, - prop = prop || 'value', - _s = this.settings, - whitelist = whitelist || _s.whitelist; - whitelist.some(_wi => { - var _wiv = typeof _wi == 'string' ? _wi : _wi[prop] || _wi.value, - isSameStr = sameStr(_wiv, value, _s.dropdown.caseSensitive, _s.trim); - if (isSameStr) { - result = typeof _wi == 'string' ? { - value: _wi - } : _wi; - return true; - } - }); - - // first iterate the whitelist, try find matches by "value" and if that fails - // and a "tagTextProp" is set to be other than "value", try that also - if (!result && prop == 'value' && _s.tagTextProp != 'value') { - // if found, adds the first which matches - result = this.getWhitelistItem(value, _s.tagTextProp, whitelist); - } - return result; - }, - /** - * validate a tag object BEFORE the actual tag will be created & appeneded - * @param {String} s - * @param {String} uid [unique ID, to not inclue own tag when cheking for duplicates] - * @return {Boolean/String} ["true" if validation has passed, String for a fail] - */ - validateTag(tagData) { - var _s = this.settings, - // when validating a tag in edit-mode, need to take "tagTextProp" into consideration - prop = "value" in tagData ? "value" : _s.tagTextProp, - v = this.trim(tagData[prop] + ""); - - // check for definitive empty value - if (!(tagData[prop] + "").trim()) return this.TEXTS.empty; - - // check if pattern should be used and if so, use it to test the value - if (_s.pattern && _s.pattern instanceof RegExp && !_s.pattern.test(v)) return this.TEXTS.pattern; - - // check for duplicates - if (!_s.duplicates && this.isTagDuplicate(v, _s.dropdown.caseSensitive, tagData.__tagId)) return this.TEXTS.duplicate; - if (this.isTagBlacklisted(v) || _s.enforceWhitelist && !this.isTagWhitelisted(v)) return this.TEXTS.notAllowed; - if (_s.validate) return _s.validate(tagData); - return true; - }, - getInvalidTagAttrs(tagData, validation) { - return { - "aria-invalid": true, - "class": `${tagData.class || ''} ${this.settings.classNames.tagNotAllowed}`.trim(), - "title": validation - }; - }, - hasMaxTags() { - return this.value.length >= this.settings.maxTags ? this.TEXTS.exceed : false; - }, - setReadonly(toggle, attrribute) { - var _s = this.settings; - document.activeElement.blur(); // exit possible edit-mode - _s[attrribute || 'readonly'] = toggle; - this.DOM.scope[(toggle ? 'set' : 'remove') + 'Attribute'](attrribute || 'readonly', true); - this.setContentEditable(!toggle); - }, - setContentEditable(state) { - if (!this.settings.userInput) return; - this.DOM.input.contentEditable = state; - this.DOM.input.tabIndex = !!state ? 0 : -1; - }, - setDisabled(isDisabled) { - this.setReadonly(isDisabled, 'disabled'); - }, - /** - * pre-proccess the tagsItems, which can be a complex tagsItems like an Array of Objects or a string comprised of multiple words - * so each item should be iterated on and a tag created for. - * @return {Array} [Array of Objects] - */ - normalizeTags(tagsItems) { - var _this$settings = this.settings, - whitelist = _this$settings.whitelist, - delimiters = _this$settings.delimiters, - mode = _this$settings.mode, - tagTextProp = _this$settings.tagTextProp; - _this$settings.enforceWhitelist; - var whitelistMatches = [], - whitelistWithProps = whitelist ? whitelist[0] instanceof Object : false, - isArray = Array.isArray(tagsItems), - isCollection = isArray && tagsItems[0].value, - mapStringToCollection = s => (s + "").split(delimiters).filter(n => n).map(v => ({ - [tagTextProp]: this.trim(v), - value: this.trim(v) - })); - if (typeof tagsItems == 'number') tagsItems = tagsItems.toString(); - - // if the argument is a "simple" String, ex: "aaa, bbb, ccc" - if (typeof tagsItems == 'string') { - if (!tagsItems.trim()) return []; - - // go over each tag and add it (if there were multiple ones) - tagsItems = mapStringToCollection(tagsItems); - } - - // if is an Array of Strings, convert to an Array of Objects - else if (isArray) { - // flatten the 2D array - tagsItems = [].concat(...tagsItems.map(item => item.value ? item // mapStringToCollection(item.value).map(newItem => ({...item,...newItem})) - : mapStringToCollection(item))); - } - - // search if the tag exists in the whitelist as an Object (has props), - // to be able to use its properties. - // skip matching collections with whitelist items as they are considered "whole" - if (whitelistWithProps && !isCollection) { - tagsItems.forEach(item => { - var whitelistMatchesValues = whitelistMatches.map(a => a.value); - - // if suggestions are shown, they are already filtered, so it's easier to use them, - // because the whitelist might also include items which have already been added - var filteredList = this.dropdown.filterListItems.call(this, item[tagTextProp], { - exact: true - }); - if (!this.settings.duplicates) - // also filter out items which have already been matched in previous iterations - filteredList = filteredList.filter(filteredItem => !whitelistMatchesValues.includes(filteredItem.value)); - - // get the best match out of list of possible matches. - // if there was a single item in the filtered list, use that one - var matchObj = filteredList.length > 1 ? this.getWhitelistItem(item[tagTextProp], tagTextProp, filteredList) : filteredList[0]; - if (matchObj && matchObj instanceof Object) { - whitelistMatches.push(matchObj); // set the Array (with the found Object) as the new value - } else if (mode != 'mix') { - if (item.value == undefined) item.value = item[tagTextProp]; - whitelistMatches.push(item); - } - }); - if (whitelistMatches.length) tagsItems = whitelistMatches; - } - return tagsItems; - }, - /** - * Parse the initial value of a textarea (or input) element and generate mixed text w/ tags - * https://stackoverflow.com/a/57598892/104380 - * @param {String} s - */ - parseMixTags(s) { - var _this$settings2 = this.settings, - mixTagsInterpolator = _this$settings2.mixTagsInterpolator, - duplicates = _this$settings2.duplicates, - transformTag = _this$settings2.transformTag, - enforceWhitelist = _this$settings2.enforceWhitelist, - maxTags = _this$settings2.maxTags, - tagTextProp = _this$settings2.tagTextProp, - tagsDataSet = []; - s = s.split(mixTagsInterpolator[0]).map((s1, i) => { - var s2 = s1.split(mixTagsInterpolator[1]), - preInterpolated = s2[0], - maxTagsReached = tagsDataSet.length == maxTags, - textProp, - tagData, - tagElm; - try { - // skip numbers and go straight to the "catch" statement - if (preInterpolated == +preInterpolated) throw Error; - tagData = JSON.parse(preInterpolated); - } catch (err) { - tagData = this.normalizeTags(preInterpolated)[0] || { - value: preInterpolated - }; - } - transformTag.call(this, tagData); - if (!maxTagsReached && s2.length > 1 && (!enforceWhitelist || this.isTagWhitelisted(tagData.value)) && !(!duplicates && this.isTagDuplicate(tagData.value))) { - // in case "tagTextProp" setting is set to other than "value" and this tag does not have this prop - textProp = tagData[tagTextProp] ? tagTextProp : 'value'; - tagData[textProp] = this.trim(tagData[textProp]); - tagElm = this.createTagElem(tagData); - tagsDataSet.push(tagData); - tagElm.classList.add(this.settings.classNames.tagNoAnimation); - s2[0] = tagElm.outerHTML; //+ "⁠" // put a zero-space at the end so the caret won't jump back to the start (when the last input's child element is a tag) - this.value.push(tagData); - } else if (s1) return i ? mixTagsInterpolator[0] + s1 : s1; - return s2.join(''); - }).join(''); - this.DOM.input.innerHTML = s; - this.DOM.input.appendChild(document.createTextNode('')); - this.DOM.input.normalize(); - this.getTagElms().forEach((elm, idx) => getSetTagData(elm, tagsDataSet[idx])); - this.update({ - withoutChangeEvent: true - }); - return s; - }, - /** - * For mixed-mode: replaces a text starting with a prefix with a wrapper element (tag or something) - * First there *has* to be a "this.state.tag" which is a string that was just typed and is staring with a prefix - */ - replaceTextWithNode(newWrapperNode, strToReplace) { - if (!this.state.tag && !strToReplace) return; - strToReplace = strToReplace || this.state.tag.prefix + this.state.tag.value; - var idx, - nodeToReplace, - selection = this.state.selection || window.getSelection(), - nodeAtCaret = selection.anchorNode, - firstSplitOffset = this.state.tag.delimiters ? this.state.tag.delimiters.length : 0; - - // STEP 1: ex. replace #ba with the tag "bart" where "|" is where the caret is: - // CURRENT STATE: "foo #ba #ba| #ba" - - // split the text node at the index of the caret - nodeAtCaret.splitText(selection.anchorOffset - firstSplitOffset); - - // node 0: "foo #ba #ba|" - // node 1: " #ba" - - // get index of LAST occurence of "#ba" - idx = nodeAtCaret.nodeValue.lastIndexOf(strToReplace); - if (idx == -1) return true; - nodeToReplace = nodeAtCaret.splitText(idx); - - // node 0: "foo #ba " - // node 1: "#ba" <- nodeToReplace - - newWrapperNode && nodeAtCaret.parentNode.replaceChild(newWrapperNode, nodeToReplace); - - // must NOT normalize contenteditable or it will cause unwanted issues: - // https://monosnap.com/file/ZDVmRvq5upYkidiFedvrwzSswegWk7 - // nodeAtCaret.parentNode.normalize() - - return true; - }, - /** - * For selecting a single option (not used for multiple tags, but for "mode:select" only) - * @param {Object} tagElm Tag DOM node - * @param {Object} tagData Tag data - */ - selectTag(tagElm, tagData) { - var _s = this.settings; - if (_s.enforceWhitelist && !this.isTagWhitelisted(tagData.value)) return; - this.input.set.call(this, tagData[_s.tagTextProp] || tagData.value, true); - - // place the caret at the end of the input, only if a dropdown option was selected (and not by manually typing another value and clicking "TAB") - if (this.state.actions.selectOption) setTimeout(() => this.setRangeAtStartEnd(false, this.DOM.input)); - var lastTagElm = this.getLastTag(); - if (lastTagElm) this.replaceTag(lastTagElm, tagData);else this.appendTag(tagElm); - - // if( _s.enforceWhitelist ) - // this.setContentEditable(false); - - this.value[0] = tagData; - this.update(); - this.trigger('add', { - tag: tagElm, - data: tagData - }); - return [tagElm]; - }, - /** - * add an empty "tag" element in an editable state - */ - addEmptyTag(initialData) { - var tagData = extend({ - value: "" - }, initialData || {}), - tagElm = this.createTagElem(tagData); - getSetTagData(tagElm, tagData); - - // add the tag to the component's DOM - this.appendTag(tagElm); - this.editTag(tagElm, { - skipValidation: true - }); - }, - /** - * add a "tag" element to the "tags" component - * @param {String/Array} tagsItems [A string (single or multiple values with a delimiter), or an Array of Objects or just Array of Strings] - * @param {Boolean} clearInput [flag if the input's value should be cleared after adding tags] - * @param {Boolean} skipInvalid [do not add, mark & remove invalid tags] - * @return {Array} Array of DOM elements (tags) - */ - addTags(tagsItems, clearInput, skipInvalid) { - var tagElems = [], - _s = this.settings, - aggregatedinvalidInput = [], - frag = document.createDocumentFragment(); - skipInvalid = skipInvalid || _s.skipInvalid; - if (!tagsItems || tagsItems.length == 0) { - return tagElems; - } - - // converts Array/String/Object to an Array of Objects - tagsItems = this.normalizeTags(tagsItems); - switch (_s.mode) { - case 'mix': - return this.addMixTags(tagsItems); - case 'select': - { - clearInput = false; - this.removeAllTags(); - } - } - this.DOM.input.removeAttribute('style'); - tagsItems.forEach(tagData => { - var tagElm, - tagElmParams = {}, - originalData = Object.assign({}, tagData, { - value: tagData.value + "" - }); - - // shallow-clone tagData so later modifications will not apply to the source - tagData = Object.assign({}, originalData); - _s.transformTag.call(this, tagData); - tagData.__isValid = this.hasMaxTags() || this.validateTag(tagData); - if (tagData.__isValid !== true) { - if (skipInvalid) return; - - // originalData is kept because it might be that this tag is invalid because it is a duplicate of another, - // and if that other tags is edited/deleted, this one should be re-validated and if is no more a duplicate - restored - extend(tagElmParams, this.getInvalidTagAttrs(tagData, tagData.__isValid), { - __preInvalidData: originalData - }); - if (tagData.__isValid == this.TEXTS.duplicate) - // mark, for a brief moment, the tag (this this one) which THIS CURRENT tag is a duplcate of - this.flashTag(this.getTagElmByValue(tagData.value)); - if (!_s.createInvalidTags) { - aggregatedinvalidInput.push(tagData.value); - return; - } - } - if ('readonly' in tagData) { - if (tagData.readonly) tagElmParams["aria-readonly"] = true; - // if "readonly" is "false", remove it from the tagData so it won't be added as an attribute in the template - else delete tagData.readonly; - } - - // Create tag HTML element - tagElm = this.createTagElem(tagData, tagElmParams); - tagElems.push(tagElm); - - // mode-select overrides - if (_s.mode == 'select') { - return this.selectTag(tagElm, tagData); - } - - // add the tag to the component's DOM - // this.appendTag(tagElm) - frag.appendChild(tagElm); - if (tagData.__isValid && tagData.__isValid === true) { - // update state - this.value.push(tagData); - this.trigger('add', { - tag: tagElm, - index: this.value.length - 1, - data: tagData - }); - } else { - this.trigger("invalid", { - data: tagData, - index: this.value.length, - tag: tagElm, - message: tagData.__isValid - }); - if (!_s.keepInvalidTags) - // remove invalid tags (if "keepInvalidTags" is set to "false") - setTimeout(() => this.removeTags(tagElm, true), 1000); - } - this.dropdown.position(); // reposition the dropdown because the just-added tag might cause a new-line - }); - - this.appendTag(frag); - this.update(); - if (tagsItems.length && clearInput) { - this.input.set.call(this, _s.createInvalidTags ? '' : aggregatedinvalidInput.join(_s._delimiters)); - this.setRangeAtStartEnd(false, this.DOM.input); - } - _s.dropdown.enabled && this.dropdown.refilter(); - return tagElems; - }, - /** - * Adds a mix-content tag - * @param {String/Array} tagData A string (single or multiple values with a delimiter), or an Array of Objects or just Array of Strings - */ - addMixTags(tagsData) { - tagsData = this.normalizeTags(tagsData); - if (tagsData[0].prefix || this.state.tag) { - return this.prefixedTextToTag(tagsData[0]); - } - if (typeof tagsData == 'string') tagsData = [{ - value: tagsData - }]; - !!this.state.selection; - var // must be cast, not to use the reference which is changing - frag = document.createDocumentFragment(); - tagsData.forEach(tagData => { - var tagElm = this.createTagElem(tagData); - frag.appendChild(tagElm); - this.insertAfterTag(tagElm); - }); - this.appendMixTags(frag); - return frag; - }, - appendMixTags(node) { - var selection = !!this.state.selection; - - // if "selection" exists, assumes intention of inecting the new tag at the last - // saved location of the caret inside "this.DOM.input" - if (selection) { - this.injectAtCaret(node); - } - // else, create a range and inject the new tag as the last child of "this.DOM.input" - else { - this.DOM.input.focus(); - selection = this.setStateSelection(); - selection.range.setStart(this.DOM.input, selection.range.endOffset); - selection.range.setEnd(this.DOM.input, selection.range.endOffset); - this.DOM.input.appendChild(node); - this.updateValueByDOMTags(); // updates internal "this.value" - this.update(); // updates original input/textarea - } - }, - - /** - * Adds a tag which was activly typed by the user - * @param {String/Array} tagItem [A string (single or multiple values with a delimiter), or an Array of Objects or just Array of Strings] - */ - prefixedTextToTag(tagItem) { - var _s = this.settings, - tagElm, - createdFromDelimiters = this.state.tag.delimiters; - _s.transformTag.call(this, tagItem); - tagItem.prefix = tagItem.prefix || this.state.tag ? this.state.tag.prefix : (_s.pattern.source || _s.pattern)[0]; - - // TODO: should check if the tag is valid - tagElm = this.createTagElem(tagItem); - - // tries to replace a taged textNode with a tagElm, and if not able, - // insert the new tag to the END if "addTags" was called from outside - if (!this.replaceTextWithNode(tagElm)) { - this.DOM.input.appendChild(tagElm); - } - setTimeout(() => tagElm.classList.add(this.settings.classNames.tagNoAnimation), 300); - this.value.push(tagItem); - this.update(); - if (!createdFromDelimiters) { - var elm = this.insertAfterTag(tagElm) || tagElm; - this.placeCaretAfterNode(elm); - } - this.state.tag = null; - this.trigger('add', extend({}, { - tag: tagElm - }, { - data: tagItem - })); - return tagElm; - }, - /** - * appened (validated) tag to the component's DOM scope - */ - appendTag(tagElm) { - var DOM = this.DOM, - insertBeforeNode = DOM.input; - if (insertBeforeNode === DOM.input) DOM.scope.insertBefore(tagElm, insertBeforeNode);else DOM.scope.appendChild(tagElm); - }, - /** - * creates a DOM tag element and injects it into the component (this.DOM.scope) - * @param {Object} tagData [text value & properties for the created tag] - * @param {Object} extraData [properties which are for the HTML template only] - * @return {Object} [DOM element] - */ - createTagElem(tagData, extraData) { - tagData.__tagId = getUID(); - var tagElm, - templateData = extend({}, tagData, _objectSpread2({ - value: escapeHTML(tagData.value + "") - }, extraData)); - - // if( this.settings.readonly ) - // tagData.readonly = true - - tagElm = this.parseTemplate('tag', [templateData, this]); - - // crucial for proper caret placement when deleting content. if textNodes are allowed as children of a tag element, - // a browser bug casues the caret to be misplaced inside the tag element (especcially affects "readonly" tags) - removeTextChildNodes(tagElm); - // while( tagElm.lastChild.nodeType == 3 ) - // tagElm.lastChild.parentNode.removeChild(tagElm.lastChild) - - getSetTagData(tagElm, tagData); - return tagElm; - }, - /** - * re-check all invalid tags. - * called after a tag was edited or removed - */ - reCheckInvalidTags() { - var _s = this.settings; - this.getTagElms(_s.classNames.tagNotAllowed).forEach((tagElm, i) => { - var tagData = getSetTagData(tagElm), - hasMaxTags = this.hasMaxTags(), - tagValidation = this.validateTag(tagData), - isValid = tagValidation === true && !hasMaxTags; - if (_s.mode == 'select') this.toggleScopeValidation(tagValidation); - - // if the tag has become valid - if (isValid) { - tagData = tagData.__preInvalidData ? tagData.__preInvalidData : { - value: tagData.value - }; - return this.replaceTag(tagElm, tagData); - } - - // if the tag is still invaild, set its title as such (reson of invalid might have changed) - tagElm.title = hasMaxTags || tagValidation; - }); - }, - /** - * Removes a tag - * @param {Array|Node|String} tagElms [DOM element(s) or a String value. if undefined or null, remove last added tag] - * @param {Boolean} silent [A flag, which when turned on, does not remove any value and does not update the original input value but simply removes the tag from tagify] - * @param {Number} tranDuration [Transition duration in MS] - * TODO: Allow multiple tags to be removed at-once - */ - removeTags(tagElms, silent, tranDuration) { - var tagsToRemove, - _s = this.settings; - tagElms = tagElms && tagElms instanceof HTMLElement ? [tagElms] : tagElms instanceof Array ? tagElms : tagElms ? [tagElms] : [this.getLastTag()]; - - // normalize tagElms array values: - // 1. removing invalid items - // 2, if an item is String try to get the matching Tag HTML node - // 3. get the tag data - // 4. return a collection of Objects - tagsToRemove = tagElms.reduce((elms, tagElm) => { - if (tagElm && typeof tagElm == 'string') tagElm = this.getTagElmByValue(tagElm); - var tagData = getSetTagData(tagElm); - if (tagElm && tagData && !tagData.readonly) - // make sure it's a tag and not some other node - // because the DOM node might be removed by async animation, the state will be updated while - // the node might still be in the DOM, so the "update" method should know which nodes to ignore - elms.push({ - node: tagElm, - idx: this.getTagIdx(tagData), - // this.getNodeIndex(tagElm); // this.getTagIndexByValue(tagElm.textContent) - data: getSetTagData(tagElm, { - '__removed': true - }) - }); - return elms; - }, []); - tranDuration = typeof tranDuration == "number" ? tranDuration : this.CSSVars.tagHideTransition; - if (_s.mode == 'select') { - tranDuration = 0; - this.input.set.call(this); - } - - // if only a single tag is to be removed. - // skip "select" mode because invalid tags are actually set to `this.value` - if (tagsToRemove.length == 1 && _s.mode != 'select') { - if (tagsToRemove[0].node.classList.contains(_s.classNames.tagNotAllowed)) silent = true; - } - if (!tagsToRemove.length) return; - return _s.hooks.beforeRemoveTag(tagsToRemove, { - tagify: this - }).then(() => { - function removeNode(tag) { - if (!tag.node.parentNode) return; - tag.node.parentNode.removeChild(tag.node); - if (!silent) { - // this.removeValueById(tagData.__uid) - this.trigger('remove', { - tag: tag.node, - index: tag.idx, - data: tag.data - }); - this.dropdown.refilter(); - this.dropdown.position(); - this.DOM.input.normalize(); // best-practice when in mix-mode (safe to do always anyways) - - // check if any of the current tags which might have been marked as "duplicate" should be un-marked - if (_s.keepInvalidTags) this.reCheckInvalidTags(); - - // below code is unfinished. it should iterate all currently invalid edited tags, which their edits have not - // changed the value yet, and should re-trigger the check, but since nothing has changed, it does not work... - // this.getTagElms(_s.classNames.tagEditing).forEach( this.events.callbacks.onEditTagBlur.bind ) - } else if (_s.keepInvalidTags) this.trigger('remove', { - tag: tag.node, - index: tag.idx - }); - } - function animation(tag) { - tag.node.style.width = parseFloat(window.getComputedStyle(tag.node).width) + 'px'; - document.body.clientTop; // force repaint for the width to take affect before the "hide" class below - tag.node.classList.add(_s.classNames.tagHide); - - // manual timeout (hack, since transitionend cannot be used because of hover) - setTimeout(removeNode.bind(this), tranDuration, tag); - } - if (tranDuration && tranDuration > 10 && tagsToRemove.length == 1) animation.call(this, tagsToRemove[0]);else tagsToRemove.forEach(removeNode.bind(this)); - - // update state regardless of animation - if (!silent) { - this.removeTagsFromValue(tagsToRemove.map(tag => tag.node)); - this.update(); // update the original input with the current value - - if (_s.mode == 'select') this.setContentEditable(true); - } - }).catch(reason => {}); - }, - removeTagsFromDOM() { - [].slice.call(this.getTagElms()).forEach(elm => elm.parentNode.removeChild(elm)); - }, - /** - * @param {Array/Node} tags to be removed from the this.value array - */ - removeTagsFromValue(tags) { - tags = Array.isArray(tags) ? tags : [tags]; - tags.forEach(tag => { - var tagData = getSetTagData(tag), - tagIdx = this.getTagIdx(tagData); - - // delete tagData.__removed - - if (tagIdx > -1) this.value.splice(tagIdx, 1); - }); - }, - removeAllTags(opts) { - opts = opts || {}; - this.value = []; - if (this.settings.mode == 'mix') this.DOM.input.innerHTML = '';else this.removeTagsFromDOM(); - this.dropdown.refilter(); - this.dropdown.position(); - if (this.state.dropdown.visible) setTimeout(() => { - this.DOM.input.focus(); - }); - if (this.settings.mode == 'select') { - this.input.set.call(this); - this.setContentEditable(true); - } - - // technically for now only "withoutChangeEvent" exists in the opts. - // if more properties will be added later, only pass what's needed to "update" - this.update(opts); - }, - postUpdate() { - var _s = this.settings, - classNames = _s.classNames, - hasValue = _s.mode == 'mix' ? _s.mixMode.integrated ? this.DOM.input.textContent : this.DOM.originalInput.value.trim() : this.value.length + this.input.raw.call(this).length; - this.toggleClass(classNames.hasMaxTags, this.value.length >= _s.maxTags); - this.toggleClass(classNames.hasNoTags, !this.value.length); - this.toggleClass(classNames.empty, !hasValue); - - // specifically the "select mode" might have the "invalid" classname set when the field is changed, so it must be toggled on add/remove/edit - if (_s.mode == 'select') { - var _this$value, _this$value$; - this.toggleScopeValidation((_this$value = this.value) === null || _this$value === void 0 ? void 0 : (_this$value$ = _this$value[0]) === null || _this$value$ === void 0 ? void 0 : _this$value$.__isValid); - } - }, - setOriginalInputValue(v) { - var inputElm = this.DOM.originalInput; - if (!this.settings.mixMode.integrated) { - inputElm.value = v; - inputElm.tagifyValue = inputElm.value; // must set to "inputElm.value" and not again to "inputValue" because for some reason the browser changes the string afterwards a bit. - this.setPersistedData(v, 'value'); - } - }, - /** - * update the origianl (hidden) input field's value - * see - https://stackoverflow.com/q/50957841/104380 - */ - update(args) { - const UPDATE_DELAY = 100; - clearTimeout(this.debouncedUpdateTimeout); - this.debouncedUpdateTimeout = setTimeout(reallyUpdate.bind(this), UPDATE_DELAY); - function reallyUpdate() { - var inputValue = this.getInputValue(); - this.setOriginalInputValue(inputValue); - this.postUpdate(); - if ((!this.settings.onChangeAfterBlur || !(args || {}).withoutChangeEvent) && !this.state.blockChangeEvent) this.triggerChangeEvent(); - } - }, - getInputValue() { - var value = this.getCleanValue(); - return this.settings.mode == 'mix' ? this.getMixedTagsAsString(value) : value.length ? this.settings.originalInputValueFormat ? this.settings.originalInputValueFormat(value) : JSON.stringify(value) : ""; - }, - /** - * removes properties from `this.value` which are only used internally - */ - getCleanValue(v) { - return removeCollectionProp(v || this.value, this.dataProps); - }, - getMixedTagsAsString() { - var result = "", - that = this, - _s = this.settings, - originalInputValueFormat = _s.originalInputValueFormat || JSON.stringify, - _interpolator = _s.mixTagsInterpolator; - function iterateChildren(rootNode) { - rootNode.childNodes.forEach(node => { - if (node.nodeType == 1) { - const tagData = getSetTagData(node); - if (node.tagName == 'BR') { - result += "\r\n"; - } - if (tagData && isNodeTag.call(that, node)) { - if (tagData.__removed) return;else result += _interpolator[0] + originalInputValueFormat(omit(tagData, that.dataProps)) + _interpolator[1]; - } else if (node.getAttribute('style') || ['B', 'I', 'U'].includes(node.tagName)) result += node.textContent;else if (node.tagName == 'DIV' || node.tagName == 'P') { - result += "\r\n"; - // if( !node.children.length && node.textContent ) - // result += node.textContent; - iterateChildren(node); - } - } else result += node.textContent; - }); - } - iterateChildren(this.DOM.input); - return result; - } - }; - - // legacy support for changed methods names - Tagify.prototype.removeTag = Tagify.prototype.removeTags; - - return Tagify; - -})); +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Tagify=e()}(this,(function(){"use strict";function t(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(t);e&&(s=s.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,s)}return i}function e(e){for(var s=1;s(t=""+t,e=""+e,s&&(t=t.trim(),e=e.trim()),i?t==e:t.toLowerCase()==e.toLowerCase()),a=(t,e)=>t&&Array.isArray(t)&&t.map((t=>n(t,e)));function n(t,e){var i,s={};for(i in t)e.indexOf(i)<0&&(s[i]=t[i]);return s}function o(t){var e=document.createElement("div");return t.replace(/\&#?[0-9a-z]+;/gi,(function(t){return e.innerHTML=t,e.innerText}))}function r(t){return(new DOMParser).parseFromString(t.trim(),"text/html").body.firstElementChild}function l(t,e){for(e=e||"previous";t=t[e+"Sibling"];)if(3==t.nodeType)return t}function d(t){return"string"==typeof t?t.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/`|'/g,"'"):t}function h(t){var e=Object.prototype.toString.call(t).split(" ")[1].slice(0,-1);return t===Object(t)&&"Array"!=e&&"Function"!=e&&"RegExp"!=e&&"HTMLUnknownElement"!=e}function g(t,e,i){function s(t,e){for(var i in e)if(e.hasOwnProperty(i)){if(h(e[i])){h(t[i])?s(t[i],e[i]):t[i]=Object.assign({},e[i]);continue}if(Array.isArray(e[i])){t[i]=Object.assign([],e[i]);continue}t[i]=e[i]}}return t instanceof Object||(t={}),s(t,e),i&&s(t,i),t}function p(){const t=[],e={};for(let i of arguments)for(let s of i)h(s)?e[s.value]||(t.push(s),e[s.value]=1):t.includes(s)||t.push(s);return t}function c(t){return String.prototype.normalize?"string"==typeof t?t.normalize("NFD").replace(/[\u0300-\u036f]/g,""):void 0:t}var u=()=>/(?=.*chrome)(?=.*android)/i.test(navigator.userAgent);function m(){return([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,(t=>(t^crypto.getRandomValues(new Uint8Array(1))[0]&15>>t/4).toString(16)))}function v(t){return t&&t.classList&&t.classList.contains(this.settings.classNames.tag)}function f(t,e){var i=window.getSelection();return e=e||i.getRangeAt(0),"string"==typeof t&&(t=document.createTextNode(t)),e&&(e.deleteContents(),e.insertNode(t)),t}function T(t,e,i){return t?(e&&(t.__tagifyTagData=i?e:g({},t.__tagifyTagData||{},e)),t.__tagifyTagData):(console.warn("tag element doesn't exist",t,e),e)}var w={delimiters:",",pattern:null,tagTextProp:"value",maxTags:1/0,callbacks:{},addTagOnBlur:!0,onChangeAfterBlur:!0,duplicates:!1,whitelist:[],blacklist:[],enforceWhitelist:!1,userInput:!0,keepInvalidTags:!1,createInvalidTags:!0,mixTagsAllowedAfter:/,|\.|\:|\s/,mixTagsInterpolator:["[[","]]"],backspace:!0,skipInvalid:!1,pasteAsTags:!0,editTags:{clicks:2,keepInvalid:!0},transformTag:()=>{},trim:!0,a11y:{focusableTags:!1},mixMode:{insertAfterTag:" "},autoComplete:{enabled:!0,rightKey:!1},classNames:{namespace:"tagify",mixMode:"tagify--mix",selectMode:"tagify--select",input:"tagify__input",focus:"tagify--focus",tagNoAnimation:"tagify--noAnim",tagInvalid:"tagify--invalid",tagNotAllowed:"tagify--notAllowed",scopeLoading:"tagify--loading",hasMaxTags:"tagify--hasMaxTags",hasNoTags:"tagify--noTags",empty:"tagify--empty",inputInvalid:"tagify__input--invalid",dropdown:"tagify__dropdown",dropdownWrapper:"tagify__dropdown__wrapper",dropdownHeader:"tagify__dropdown__header",dropdownFooter:"tagify__dropdown__footer",dropdownItem:"tagify__dropdown__item",dropdownItemActive:"tagify__dropdown__item--active",dropdownItemHidden:"tagify__dropdown__item--hidden",dropdownInital:"tagify__dropdown--initial",tag:"tagify__tag",tagText:"tagify__tag-text",tagX:"tagify__tag__removeBtn",tagLoading:"tagify__tag--loading",tagEditing:"tagify__tag--editable",tagFlash:"tagify__tag--flash",tagHide:"tagify__tag--hide"},dropdown:{classname:"",enabled:2,maxItems:10,searchKeys:["value","searchBy"],fuzzySearch:!0,caseSensitive:!1,accentedSearch:!0,includeSelectedTags:!1,highlightFirst:!1,closeOnSelect:!0,clearOnSelect:!0,position:"all",appendTarget:null},hooks:{beforeRemoveTag:()=>Promise.resolve(),beforePaste:()=>Promise.resolve(),suggestionClick:()=>Promise.resolve()}};function b(){this.dropdown={};for(let t in this._dropdown)this.dropdown[t]="function"==typeof this._dropdown[t]?this._dropdown[t].bind(this):this._dropdown[t];this.dropdown.refs()}var y={refs(){this.DOM.dropdown=this.parseTemplate("dropdown",[this.settings]),this.DOM.dropdown.content=this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-wrapper']")},getHeaderRef(){return this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-header']")},getFooterRef(){return this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-footer']")},getAllSuggestionsRefs(){return[...this.DOM.dropdown.content.querySelectorAll(this.settings.classNames.dropdownItemSelector)]},show(t){var e,i,a,n=this.settings,o="mix"==n.mode&&!n.enforceWhitelist,r=!n.whitelist||!n.whitelist.length,l="manual"==n.dropdown.position;if(t=void 0===t?this.state.inputText:t,!(r&&!o&&!n.templates.dropdownItemNoMatch||!1===n.dropdown.enable||this.state.isLoading||this.settings.readonly)){if(clearTimeout(this.dropdownHide__bindEventsTimeout),this.suggestedListItems=this.dropdown.filterListItems(t),t&&!this.suggestedListItems.length&&(this.trigger("dropdown:noMatch",t),n.templates.dropdownItemNoMatch&&(a=n.templates.dropdownItemNoMatch.call(this,{value:t}))),!a){if(this.suggestedListItems.length)t&&o&&!this.state.editing.scope&&!s(this.suggestedListItems[0].value,t)&&this.suggestedListItems.unshift({value:t});else{if(!t||!o||this.state.editing.scope)return this.input.autocomplete.suggest.call(this),void this.dropdown.hide();this.suggestedListItems=[{value:t}]}i=""+(h(e=this.suggestedListItems[0])?e.value:e),n.autoComplete&&i&&0==i.indexOf(t)&&this.input.autocomplete.suggest.call(this,e)}this.dropdown.fill(a),n.dropdown.highlightFirst&&this.dropdown.highlightOption(this.DOM.dropdown.content.querySelector(n.classNames.dropdownItemSelector)),this.state.dropdown.visible||setTimeout(this.dropdown.events.binding.bind(this)),this.state.dropdown.visible=t||!0,this.state.dropdown.query=t,this.setStateSelection(),l||setTimeout((()=>{this.dropdown.position(),this.dropdown.render()})),setTimeout((()=>{this.trigger("dropdown:show",this.DOM.dropdown)}))}},hide(t){var e=this.DOM,i=e.scope,s=e.dropdown,a="manual"==this.settings.dropdown.position&&!t;if(s&&document.body.contains(s)&&!a)return window.removeEventListener("resize",this.dropdown.position),this.dropdown.events.binding.call(this,!1),i.setAttribute("aria-expanded",!1),s.parentNode.removeChild(s),setTimeout((()=>{this.state.dropdown.visible=!1}),100),this.state.dropdown.query=this.state.ddItemData=this.state.ddItemElm=this.state.selection=null,this.state.tag&&this.state.tag.value.length&&(this.state.flaggedTags[this.state.tag.baseOffset]=this.state.tag),this.trigger("dropdown:hide",s),this},toggle(t){this.dropdown[this.state.dropdown.visible&&!t?"hide":"show"]()},render(){var t,e,i,s=(t=this.DOM.dropdown,(i=t.cloneNode(!0)).style.cssText="position:fixed; top:-9999px; opacity:0",document.body.appendChild(i),e=i.clientHeight,i.parentNode.removeChild(i),e),a=this.settings;return"number"==typeof a.dropdown.enabled&&a.dropdown.enabled>=0?(this.DOM.scope.setAttribute("aria-expanded",!0),document.body.contains(this.DOM.dropdown)||(this.DOM.dropdown.classList.add(a.classNames.dropdownInital),this.dropdown.position(s),a.dropdown.appendTarget.appendChild(this.DOM.dropdown),setTimeout((()=>this.DOM.dropdown.classList.remove(a.classNames.dropdownInital)))),this):this},fill(t){t="string"==typeof t?t:this.dropdown.createListHTML(t||this.suggestedListItems);var e,i=this.settings.templates.dropdownContent.call(this,t);this.DOM.dropdown.content.innerHTML=(e=i)?e.replace(/\>[\r\n ]+\<").replace(/(<.*?>)|\s+/g,((t,e)=>e||" ")):""},fillHeaderFooter(){this.settings.templates;var t=this.dropdown.filterListItems(this.state.dropdown.query),e=this.parseTemplate("dropdownHeader",[t]),i=this.parseTemplate("dropdownFooter",[t]),s=this.dropdown.getHeaderRef(),a=this.dropdown.getFooterRef();e&&(null==s||s.parentNode.replaceChild(e,s)),i&&(null==a||a.parentNode.replaceChild(i,a))},refilter(t){t=t||this.state.dropdown.query||"",this.suggestedListItems=this.dropdown.filterListItems(t),this.dropdown.fill(),this.suggestedListItems.length||this.dropdown.hide(),this.trigger("dropdown:updated",this.DOM.dropdown)},position(t){var e=this.settings.dropdown;if("manual"!=e.position){var i,s,a,n,o,r,l=this.DOM.dropdown,d=e.placeAbove,h=e.appendTarget===document.body,g=h?window.pageYOffset:e.appendTarget.scrollTop,p=document.fullscreenElement||document.webkitFullscreenElement||document.documentElement,c=p.clientHeight,u=Math.max(p.clientWidth||0,window.innerWidth||0)>480?e.position:"all",m=this.DOM["input"==u?"input":"scope"];if(t=t||l.clientHeight,this.state.dropdown.visible){if("text"==u?(a=(i=function(){const t=document.getSelection();if(t.rangeCount){const e=t.getRangeAt(0),i=e.startContainer,s=e.startOffset;let a,n;if(s>0)return n=document.createRange(),n.setStart(i,s-1),n.setEnd(i,s),a=n.getBoundingClientRect(),{left:a.right,top:a.top,bottom:a.bottom};if(i.getBoundingClientRect)return i.getBoundingClientRect()}return{left:-9999,top:-9999}}()).bottom,s=i.top,n=i.left,o="auto"):(r=function(t){for(var e=0,i=0;t&&t!=p;)e+=t.offsetLeft||0,i+=t.offsetTop||0,t=t.parentNode;return{left:e,top:i}}(e.appendTarget),s=(i=m.getBoundingClientRect()).top-r.top,a=i.bottom-1-r.top,n=i.left-r.left,o=i.width+"px"),!h){let t=function(){for(var t=0,i=e.appendTarget.parentNode;i;)t+=i.scrollTop||0,i=i.parentNode;return t}();s+=t,a+=t}s=Math.floor(s),a=Math.ceil(a),d=void 0===d?c-i.bottom0&&void 0!==arguments[0])||arguments[0];var e=this.dropdown.events.callbacks,i=this.listeners.dropdown=this.listeners.dropdown||{position:this.dropdown.position.bind(this,null),onKeyDown:e.onKeyDown.bind(this),onMouseOver:e.onMouseOver.bind(this),onMouseLeave:e.onMouseLeave.bind(this),onClick:e.onClick.bind(this),onScroll:e.onScroll.bind(this)},s=t?"addEventListener":"removeEventListener";"manual"!=this.settings.dropdown.position&&(document[s]("scroll",i.position,!0),window[s]("resize",i.position),window[s]("keydown",i.onKeyDown)),this.DOM.dropdown[s]("mouseover",i.onMouseOver),this.DOM.dropdown[s]("mouseleave",i.onMouseLeave),this.DOM.dropdown[s]("mousedown",i.onClick),this.DOM.dropdown.content[s]("scroll",i.onScroll)},callbacks:{onKeyDown(t){if(this.state.hasFocus&&!this.state.composing){var e=this.DOM.dropdown.querySelector(this.settings.classNames.dropdownItemActiveSelector),i=this.dropdown.getSuggestionDataByNode(e);switch(t.key){case"ArrowDown":case"ArrowUp":case"Down":case"Up":t.preventDefault();var s=this.dropdown.getAllSuggestionsRefs(),a="ArrowUp"==t.key||"Up"==t.key;e&&(e=this.dropdown.getNextOrPrevOption(e,!a)),e&&e.matches(this.settings.classNames.dropdownItemSelector)||(e=s[a?s.length-1:0]),i=this.dropdown.getSuggestionDataByNode(e),this.dropdown.highlightOption(e,!0);break;case"Escape":case"Esc":this.dropdown.hide();break;case"ArrowRight":if(this.state.actions.ArrowLeft)return;case"Tab":if("mix"!=this.settings.mode&&e&&!this.settings.autoComplete.rightKey&&!this.state.editing){t.preventDefault();var n=this.dropdown.getMappedValue(i);return this.input.autocomplete.set.call(this,n),!1}return!0;case"Enter":t.preventDefault(),this.settings.hooks.suggestionClick(t,{tagify:this,tagData:i,suggestionElm:e}).then((()=>{if(e)return this.dropdown.selectOption(e),e=this.dropdown.getNextOrPrevOption(e,!a),void this.dropdown.highlightOption(e);this.dropdown.hide(),"mix"!=this.settings.mode&&this.addTags(this.state.inputText.trim(),!0)})).catch((t=>t));break;case"Backspace":{if("mix"==this.settings.mode||this.state.editing.scope)return;const t=this.input.raw.call(this);""!=t&&8203!=t.charCodeAt(0)||(!0===this.settings.backspace?this.removeTags():"edit"==this.settings.backspace&&setTimeout(this.editTag.bind(this),0))}}}},onMouseOver(t){var e=t.target.closest(this.settings.classNames.dropdownItemSelector);e&&this.dropdown.highlightOption(e)},onMouseLeave(t){this.dropdown.highlightOption()},onClick(t){if(0==t.button&&t.target!=this.DOM.dropdown&&t.target!=this.DOM.dropdown.content){var e=t.target.closest(this.settings.classNames.dropdownItemSelector),i=this.dropdown.getSuggestionDataByNode(e);this.state.actions.selectOption=!0,setTimeout((()=>this.state.actions.selectOption=!1),50),this.settings.hooks.suggestionClick(t,{tagify:this,tagData:i,suggestionElm:e}).then((()=>{e?this.dropdown.selectOption(e,t):this.dropdown.hide()})).catch((t=>console.warn(t)))}},onScroll(t){var e=t.target,i=e.scrollTop/(e.scrollHeight-e.parentNode.clientHeight)*100;this.trigger("dropdown:scroll",{percentage:Math.round(i)})}}},getSuggestionDataByNode(t){var e=t&&t.getAttribute("value");return this.suggestedListItems.find((t=>t.value==e))||null},getNextOrPrevOption(t){let e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];var i=this.dropdown.getAllSuggestionsRefs(),s=i.findIndex((e=>e===t));return e?i[s+1]:i[s-1]},highlightOption(t,e){var i,s=this.settings.classNames.dropdownItemActive;if(this.state.ddItemElm&&(this.state.ddItemElm.classList.remove(s),this.state.ddItemElm.removeAttribute("aria-selected")),!t)return this.state.ddItemData=null,this.state.ddItemElm=null,void this.input.autocomplete.suggest.call(this);i=this.dropdown.getSuggestionDataByNode(t),this.state.ddItemData=i,this.state.ddItemElm=t,t.classList.add(s),t.setAttribute("aria-selected",!0),e&&(t.parentNode.scrollTop=t.clientHeight+t.offsetTop-t.parentNode.clientHeight),this.settings.autoComplete&&(this.input.autocomplete.suggest.call(this,i),this.dropdown.position())},selectOption(t,e){var i=this.settings.dropdown,s=i.clearOnSelect,a=i.closeOnSelect;if(!t)return this.addTags(this.state.inputText,!0),void(a&&this.dropdown.hide());e=e||{};var n=t.getAttribute("value"),o="noMatch"==n,r=this.suggestedListItems.find((t=>(t.value||t)==n));this.trigger("dropdown:select",{data:r,elm:t,event:e}),n&&(r||o)?(this.state.editing?this.onEditTagDone(null,g({__isValid:!0},this.normalizeTags([r])[0])):this["mix"==this.settings.mode?"addMixTags":"addTags"]([r||this.input.raw.call(this)],s),this.DOM.input.parentNode&&(setTimeout((()=>{this.DOM.input.focus(),this.toggleFocusClass(!0),this.setRangeAtStartEnd(!1,this.DOM.input)})),a&&setTimeout(this.dropdown.hide.bind(this)),t.addEventListener("transitionend",(()=>{this.dropdown.fillHeaderFooter(),setTimeout((()=>t.remove()),100)}),{once:!0}),t.classList.add(this.settings.classNames.dropdownItemHidden))):a&&setTimeout(this.dropdown.hide.bind(this))},selectAll(t){this.suggestedListItems.length=0,this.dropdown.hide(),this.dropdown.filterListItems("");var e=this.dropdown.filterListItems("");return t||(e=this.state.dropdown.suggestions),this.addTags(e,!0),this},filterListItems(t,e){var i,s,a,n,o,r=this.settings,l=r.dropdown,d=(e=e||{},[]),g=[],p=r.whitelist,u=l.maxItems>=0?l.maxItems:1/0,m=l.searchKeys,v=0;if(!(t="select"==r.mode&&this.value.length&&this.value[0][r.tagTextProp]==t?"":t)||!m.length)return d=l.includeSelectedTags?p:p.filter((t=>!this.isTagDuplicate(h(t)?t.value:t))),this.state.dropdown.suggestions=d,d.slice(0,u);function f(t,e){return e.toLowerCase().split(" ").every((e=>t.includes(e.toLowerCase())))}for(o=l.caseSensitive?""+t:(""+t).toLowerCase();vm.includes(t)))?["value"]:m;l.fuzzySearch&&!e.exact?(a=u.reduce(((t,e)=>t+" "+(i[e]||"")),"").toLowerCase().trim(),l.accentedSearch&&(a=c(a),o=c(o)),t=0==a.indexOf(o),r=a===o,s=f(a,o)):(t=!0,s=u.some((t=>{var s=""+(i[t]||"");return l.accentedSearch&&(s=c(s),o=c(o)),l.caseSensitive||(s=s.toLowerCase()),r=s===o,e.exact?s===o:0==s.indexOf(o)}))),n=!l.includeSelectedTags&&this.isTagDuplicate(h(i)?i.value:i),s&&!n&&(r&&t?g.push(i):"startsWith"==l.sortby&&t?d.unshift(i):d.push(i))}return this.state.dropdown.suggestions=g.concat(d),"function"==typeof l.sortby?l.sortby(g.concat(d),o):g.concat(d).slice(0,u)},getMappedValue(t){var e=this.settings.dropdown.mapValueTo;return e?"function"==typeof e?e(t):t[e]||t.value:t.value},createListHTML(t){return g([],t).map(((t,i)=>{"string"!=typeof t&&"number"!=typeof t||(t={value:t});var s=this.dropdown.getMappedValue(t);return s="string"==typeof s?d(s):s,this.settings.templates.dropdownItem.apply(this,[e(e({},t),{},{mappedValue:s}),this])})).join("")}};const x="@yaireo/tagify/";var O,D={empty:"empty",exceed:"number of tags exceeded",pattern:"pattern mismatch",duplicate:"already exists",notAllowed:"not allowed"},M={wrapper:(t,e)=>`\n \n ​\n `,tag(t,e){let i=e.settings;return`\n \n
\n ${t[i.tagTextProp]||t.value}\n
\n
`},dropdown(t){var e=t.dropdown,i="manual"==e.position,s=`${t.classNames.dropdown}`;return`
\n
\n
`},dropdownContent(t){var e=this.settings,i=this.state.dropdown.suggestions;return`\n ${e.templates.dropdownHeader.call(this,i)}\n ${t}\n ${e.templates.dropdownFooter.call(this,i)}\n `},dropdownItem(t){return`
${t.mappedValue||t.value}
`},dropdownHeader(t){return`
`},dropdownFooter(t){var e=t.length-this.settings.dropdown.maxItems;return e>0?`
\n ${e} more items. Refine your search.\n
`:""},dropdownItemNoMatch:null};var I={customBinding(){this.customEventsList.forEach((t=>{this.on(t,this.settings.callbacks[t])}))},binding(){let t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];var e,i=this.events.callbacks,s=t?"addEventListener":"removeEventListener";if(!this.state.mainEvents||!t){for(var a in this.state.mainEvents=t,t&&!this.listeners.main&&(this.events.bindGlobal.call(this),this.settings.isJQueryPlugin&&jQuery(this.DOM.originalInput).on("tagify.removeAllTags",this.removeAllTags.bind(this))),e=this.listeners.main=this.listeners.main||{focus:["input",i.onFocusBlur.bind(this)],keydown:["input",i.onKeydown.bind(this)],click:["scope",i.onClickScope.bind(this)],dblclick:["scope",i.onDoubleClickScope.bind(this)],paste:["input",i.onPaste.bind(this)],drop:["input",i.onDrop.bind(this)],compositionstart:["input",i.onCompositionStart.bind(this)],compositionend:["input",i.onCompositionEnd.bind(this)]})this.DOM[e[a][0]][s](a,e[a][1]);clearInterval(this.listeners.main.originalInputValueObserverInterval),this.listeners.main.originalInputValueObserverInterval=setInterval(i.observeOriginalInputValue.bind(this),500);var n=this.listeners.main.inputMutationObserver||new MutationObserver(i.onInputDOMChange.bind(this));n&&n.disconnect(),"mix"==this.settings.mode&&n.observe(this.DOM.input,{childList:!0})}},bindGlobal(t){var e,i=this.events.callbacks,s=t?"removeEventListener":"addEventListener";if(t||!this.listeners.global)for(e of(this.listeners.global=this.listeners&&this.listeners.global||[{type:this.isIE?"keydown":"input",target:this.DOM.input,cb:i[this.isIE?"onInputIE":"onInput"].bind(this)},{type:"keydown",target:window,cb:i.onWindowKeyDown.bind(this)},{type:"blur",target:this.DOM.input,cb:i.onFocusBlur.bind(this)}],this.listeners.global))e.target[s](e.type,e.cb)},unbindGlobal(){this.events.bindGlobal.call(this,!0)},callbacks:{onFocusBlur(t){var e,i,s=this.settings,a=t.target?this.trim(t.target.textContent):"",n=null===(e=this.value)||void 0===e||null===(i=e[0])||void 0===i?void 0:i[s.tagTextProp],o=t.type,r=s.dropdown.enabled>=0,l={relatedTarget:t.relatedTarget},d=this.state.actions.selectOption&&(r||!s.dropdown.closeOnSelect),h=this.state.actions.addNew&&r,g=t.relatedTarget&&v.call(this,t.relatedTarget)&&this.DOM.scope.contains(t.relatedTarget);if("blur"==o){if(t.relatedTarget===this.DOM.scope)return this.dropdown.hide(),void this.DOM.input.focus();this.postUpdate(),s.onChangeAfterBlur&&this.triggerChangeEvent()}if(!d&&!h)if(this.state.hasFocus="focus"==o&&+new Date,this.toggleFocusClass(this.state.hasFocus),"mix"!=s.mode){if("focus"==o)return this.trigger("focus",l),void(0!==s.dropdown.enabled&&s.userInput||this.dropdown.show(this.value.length?"":void 0));"blur"==o&&(this.trigger("blur",l),this.loading(!1),"select"==s.mode&&(g&&(this.removeTags(),a=""),n===a&&(a="")),a&&!this.state.actions.selectOption&&s.addTagOnBlur&&this.addTags(a,!0)),this.DOM.input.removeAttribute("style"),this.dropdown.hide()}else"focus"==o?this.trigger("focus",l):"blur"==t.type&&(this.trigger("blur",l),this.loading(!1),this.dropdown.hide(),this.state.dropdown.visible=void 0,this.setStateSelection())},onCompositionStart(t){this.state.composing=!0},onCompositionEnd(t){this.state.composing=!1},onWindowKeyDown(t){var e,i=document.activeElement;if(v.call(this,i)&&this.DOM.scope.contains(document.activeElement))switch(e=i.nextElementSibling,t.key){case"Backspace":this.settings.readonly||(this.removeTags(i),(e||this.DOM.input).focus());break;case"Enter":setTimeout(this.editTag.bind(this),0,i)}},onKeydown(t){var e=this.settings;if(!this.state.composing&&e.userInput){"select"==e.mode&&e.enforceWhitelist&&this.value.length&&"Tab"!=t.key&&t.preventDefault();var i=this.trim(t.target.textContent);if(this.trigger("keydown",{event:t}),"mix"==e.mode){switch(t.key){case"Left":case"ArrowLeft":this.state.actions.ArrowLeft=!0;break;case"Delete":case"Backspace":if(this.state.editing)return;var s,a,n,r=document.getSelection(),d="Delete"==t.key&&r.anchorOffset==(r.anchorNode.length||0),h=r.anchorNode.previousSibling,g=1==r.anchorNode.nodeType||!r.anchorOffset&&h&&1==h.nodeType&&r.anchorNode.previousSibling,p=o(this.DOM.input.innerHTML),c=this.getTagElms();if("edit"==e.backspace&&g)return s=1==r.anchorNode.nodeType?null:r.anchorNode.previousElementSibling,setTimeout(this.editTag.bind(this),0,s),void t.preventDefault();if(u()&&g)return n=l(g),g.hasAttribute("readonly")||g.remove(),this.DOM.input.focus(),void setTimeout((()=>{this.placeCaretAfterNode(n),this.DOM.input.click()}));if("BR"==r.anchorNode.nodeName)return;if((d||g)&&1==r.anchorNode.nodeType?a=0==r.anchorOffset?d?c[0]:null:c[Math.min(c.length,r.anchorOffset)-1]:d?a=r.anchorNode.nextElementSibling:g&&(a=g),3==r.anchorNode.nodeType&&!r.anchorNode.nodeValue&&r.anchorNode.previousElementSibling&&t.preventDefault(),(g||d)&&!e.backspace)return void t.preventDefault();if("Range"!=r.type&&!r.anchorOffset&&r.anchorNode==this.DOM.input&&"Delete"!=t.key)return void t.preventDefault();if("Range"!=r.type&&a&&a.hasAttribute("readonly"))return void this.placeCaretAfterNode(l(a));clearTimeout(O),O=setTimeout((()=>{var t=document.getSelection(),e=o(this.DOM.input.innerHTML),i=!d&&t.anchorNode.previousSibling;if(e.length>=p.length&&i)if(v.call(this,i)&&!i.hasAttribute("readonly")){if(this.removeTags(i),this.fixFirefoxLastTagNoCaret(),2==this.DOM.input.children.length&&"BR"==this.DOM.input.children[1].tagName)return this.DOM.input.innerHTML="",this.value.length=0,!0}else i.remove();this.value=[].map.call(c,((t,e)=>{var i=T(t);if(t.parentNode||i.readonly)return i;this.trigger("remove",{tag:t,index:e,data:i})})).filter((t=>t))}),20)}return!0}switch(t.key){case"Backspace":"select"==e.mode&&e.enforceWhitelist&&this.value.length?this.removeTags():this.state.dropdown.visible&&"manual"!=e.dropdown.position||""!=t.target.textContent&&8203!=i.charCodeAt(0)||(!0===e.backspace?this.removeTags():"edit"==e.backspace&&setTimeout(this.editTag.bind(this),0));break;case"Esc":case"Escape":if(this.state.dropdown.visible)return;t.target.blur();break;case"Down":case"ArrowDown":this.state.dropdown.visible||this.dropdown.show();break;case"ArrowRight":{let t=this.state.inputSuggestion||this.state.ddItemData;if(t&&e.autoComplete.rightKey)return void this.addTags([t],!0);break}case"Tab":{let s="select"==e.mode;if(!i||s)return!0;t.preventDefault()}case"Enter":if(this.state.dropdown.visible&&"manual"!=e.dropdown.position)return;t.preventDefault(),setTimeout((()=>{this.state.actions.selectOption||this.addTags(i,!0)}))}}},onInput(t){this.postUpdate();var e=this.settings;if("mix"==e.mode)return this.events.callbacks.onMixTagsInput.call(this,t);var i=this.input.normalize.call(this),s=i.length>=e.dropdown.enabled,a={value:i,inputElm:this.DOM.input},n=this.validateTag({value:i});"select"==e.mode&&this.toggleScopeValidation(n),a.isValid=n,this.state.inputText!=i&&(this.input.set.call(this,i,!1),-1!=i.search(e.delimiters)?this.addTags(i)&&this.input.set.call(this):e.dropdown.enabled>=0&&this.dropdown[s?"show":"hide"](i),this.trigger("input",a))},onMixTagsInput(t){var e,i,s,a,n,o,r,l,d=this.settings,h=this.value.length,p=this.getTagElms(),c=document.createDocumentFragment(),m=window.getSelection().getRangeAt(0),v=[].map.call(p,(t=>T(t).value));if("deleteContentBackward"==t.inputType&&u()&&this.events.callbacks.onKeydown.call(this,{target:t.target,key:"Backspace"}),this.value.slice().forEach((t=>{t.readonly&&!v.includes(t.value)&&c.appendChild(this.createTagElem(t))})),c.childNodes.length&&(m.insertNode(c),this.setRangeAtStartEnd(!1,c.lastChild)),p.length!=h)return this.value=[].map.call(this.getTagElms(),(t=>T(t))),void this.update({withoutChangeEvent:!0});if(this.hasMaxTags())return!0;if(window.getSelection&&(o=window.getSelection()).rangeCount>0&&3==o.anchorNode.nodeType){if((m=o.getRangeAt(0).cloneRange()).collapse(!0),m.setStart(o.focusNode,0),s=(e=m.toString().slice(0,m.endOffset)).split(d.pattern).length-1,(i=e.match(d.pattern))&&(a=e.slice(e.lastIndexOf(i[i.length-1]))),a){if(this.state.actions.ArrowLeft=!1,this.state.tag={prefix:a.match(d.pattern)[0],value:a.replace(d.pattern,"")},this.state.tag.baseOffset=o.baseOffset-this.state.tag.value.length,l=this.state.tag.value.match(d.delimiters))return this.state.tag.value=this.state.tag.value.replace(d.delimiters,""),this.state.tag.delimiters=l[0],this.addTags(this.state.tag.value,d.dropdown.clearOnSelect),void this.dropdown.hide();n=this.state.tag.value.length>=d.dropdown.enabled;try{r=(r=this.state.flaggedTags[this.state.tag.baseOffset]).prefix==this.state.tag.prefix&&r.value[0]==this.state.tag.value[0],this.state.flaggedTags[this.state.tag.baseOffset]&&!this.state.tag.value&&delete this.state.flaggedTags[this.state.tag.baseOffset]}catch(t){}(r||s{this.update({withoutChangeEvent:!0}),this.trigger("input",g({},this.state.tag,{textContent:this.DOM.input.textContent})),this.state.tag&&this.dropdown[n?"show":"hide"](this.state.tag.value)}),10)},onInputIE(t){var e=this;setTimeout((function(){e.events.callbacks.onInput.call(e,t)}))},observeOriginalInputValue(){this.DOM.originalInput.parentNode||this.destroy(),this.DOM.originalInput.value!=this.DOM.originalInput.tagifyValue&&this.loadOriginalValues()},onClickScope(t){var e=this.settings,i=t.target.closest("."+e.classNames.tag),s=+new Date-this.state.hasFocus;if(t.target!=this.DOM.scope){if(!t.target.classList.contains(e.classNames.tagX))return i?(this.trigger("click",{tag:i,index:this.getNodeIndex(i),data:T(i),event:t}),void(1!==e.editTags&&1!==e.editTags.clicks||this.events.callbacks.onDoubleClickScope.call(this,t))):void(t.target==this.DOM.input&&("mix"==e.mode&&this.fixFirefoxLastTagNoCaret(),s>500)?this.state.dropdown.visible?this.dropdown.hide():0===e.dropdown.enabled&&"mix"!=e.mode&&this.dropdown.show(this.value.length?"":void 0):"select"!=e.mode||0!==e.dropdown.enabled||this.state.dropdown.visible||this.dropdown.show());this.removeTags(t.target.parentNode)}else this.state.hasFocus||this.DOM.input.focus()},onPaste(t){t.preventDefault();var e,i,s=this.settings;if("select"==s.mode&&s.enforceWhitelist||!s.userInput)return!1;s.readonly||(e=t.clipboardData||window.clipboardData,i=e.getData("Text"),s.hooks.beforePaste(t,{tagify:this,pastedText:i,clipboardData:e}).then((e=>{void 0===e&&(e=i),e&&(this.injectAtCaret(e,window.getSelection().getRangeAt(0)),"mix"==this.settings.mode?this.events.callbacks.onMixTagsInput.call(this,t):this.settings.pasteAsTags?this.addTags(this.state.inputText+e,!0):this.state.inputText=e)})).catch((t=>t)))},onDrop(t){t.preventDefault()},onEditTagInput(t,e){var i=t.closest("."+this.settings.classNames.tag),s=this.getNodeIndex(i),a=T(i),n=this.input.normalize.call(this,t),o={[this.settings.tagTextProp]:n,__tagId:a.__tagId},r=this.validateTag(o);this.editTagChangeDetected(g(a,o))||!0!==t.originalIsValid||(r=!0),i.classList.toggle(this.settings.classNames.tagInvalid,!0!==r),a.__isValid=r,i.title=!0===r?a.title||a.value:r,n.length>=this.settings.dropdown.enabled&&(this.state.editing&&(this.state.editing.value=n),this.dropdown.show(n)),this.trigger("edit:input",{tag:i,index:s,data:g({},this.value[s],{newValue:n}),event:e})},onEditTagPaste(t,e){var i=(e.clipboardData||window.clipboardData).getData("Text");e.preventDefault();var s=f(i);this.setRangeAtStartEnd(!1,s)},onEditTagFocus(t){this.state.editing={scope:t,input:t.querySelector("[contenteditable]")}},onEditTagBlur(t){if(this.state.hasFocus||this.toggleFocusClass(),this.DOM.scope.contains(t)){var e,i,s=this.settings,a=t.closest("."+s.classNames.tag),n=this.input.normalize.call(this,t),o=T(a),r=o.__originalData,l=this.editTagChangeDetected(o),d=this.validateTag({[s.tagTextProp]:n,__tagId:o.__tagId});if(n)if(l){if(e=this.hasMaxTags(),i=g({},r,{[s.tagTextProp]:this.trim(n),__isValid:d}),s.transformTag.call(this,i,r),!0!==(d=(!e||!0===r.__isValid)&&this.validateTag(i))){if(this.trigger("invalid",{data:i,tag:a,message:d}),s.editTags.keepInvalid)return;s.keepInvalidTags?i.__isValid=d:i=r}else s.keepInvalidTags&&(delete i.title,delete i["aria-invalid"],delete i.class);this.onEditTagDone(a,i)}else this.onEditTagDone(a,r);else this.onEditTagDone(a)}},onEditTagkeydown(t,e){if(!this.state.composing)switch(this.trigger("edit:keydown",{event:t}),t.key){case"Esc":case"Escape":e.parentNode.replaceChild(e.__tagifyTagData.__originalHTML,e),this.state.editing=!1;case"Enter":case"Tab":t.preventDefault(),t.target.blur()}},onDoubleClickScope(t){var e,i,s=t.target.closest("."+this.settings.classNames.tag),a=T(s),n=this.settings;s&&n.userInput&&!1!==a.editable&&(e=s.classList.contains(this.settings.classNames.tagEditing),i=s.hasAttribute("readonly"),"select"==n.mode||n.readonly||e||i||!this.settings.editTags||this.editTag(s),this.toggleFocusClass(!0),this.trigger("dblclick",{tag:s,index:this.getNodeIndex(s),data:T(s)}))},onInputDOMChange(t){t.forEach((t=>{t.addedNodes.forEach((t=>{if("

"==t.outerHTML)t.replaceWith(document.createElement("br"));else if(1==t.nodeType&&t.querySelector(this.settings.classNames.tagSelector)){let e=document.createTextNode("");3==t.childNodes[0].nodeType&&"BR"!=t.previousSibling.nodeName&&(e=document.createTextNode("\n")),t.replaceWith(e,...[...t.childNodes].slice(0,-1)),this.placeCaretAfterNode(e)}else if(v.call(this,t)){var e;if(3!=(null===(e=t.previousSibling)||void 0===e?void 0:e.nodeType)||t.previousSibling.textContent||t.previousSibling.remove(),t.previousSibling&&"BR"==t.previousSibling.nodeName){t.previousSibling.replaceWith("\n​");let e=t.nextSibling,i="";for(;e;)i+=e.textContent,e=e.nextSibling;i.trim()&&this.placeCaretAfterNode(t.previousSibling)}}})),t.removedNodes.forEach((t=>{t&&"BR"==t.nodeName&&v.call(this,e)&&(this.removeTags(e),this.fixFirefoxLastTagNoCaret())}))}));var e=this.DOM.input.lastChild;e&&""==e.nodeValue&&e.remove(),e&&"BR"==e.nodeName||this.DOM.input.appendChild(document.createElement("br"))}}};function N(t,e){if(!t){console.warn("Tagify:","input element not found",t);const e=new Proxy(this,{get:()=>()=>e});return e}if(t.__tagify)return console.warn("Tagify: ","input element is already Tagified - Same instance is returned.",t),t.__tagify;var i;g(this,function(t){var e=document.createTextNode("");function i(t,i,s){s&&i.split(/\s+/g).forEach((i=>e[t+"EventListener"].call(e,i,s)))}return{off(t,e){return i("remove",t,e),this},on(t,e){return e&&"function"==typeof e&&i("add",t,e),this},trigger(i,s,a){var n;if(a=a||{cloneData:!0},i)if(t.settings.isJQueryPlugin)"remove"==i&&(i="removeTag"),jQuery(t.DOM.originalInput).triggerHandler(i,[s]);else{try{var o="object"==typeof s?s:{value:s};if((o=a.cloneData?g({},o):o).tagify=this,s.event&&(o.event=this.cloneEvent(s.event)),s instanceof Object)for(var r in s)s[r]instanceof HTMLElement&&(o[r]=s[r]);n=new CustomEvent(i,{detail:o})}catch(t){console.warn(t)}e.dispatchEvent(n)}}}}(this)),this.isFirefox=/firefox|fxios/i.test(navigator.userAgent)&&!/seamonkey/i.test(navigator.userAgent),this.isIE=window.document.documentMode,e=e||{},this.getPersistedData=(i=e.id,t=>{let e,s="/"+t;if(1==localStorage.getItem(x+i+"/v",1))try{e=JSON.parse(localStorage[x+i+s])}catch(t){}return e}),this.setPersistedData=(t=>t?(localStorage.setItem(x+t+"/v",1),(e,i)=>{let s="/"+i,a=JSON.stringify(e);e&&i&&(localStorage.setItem(x+t+s,a),dispatchEvent(new Event("storage")))}):()=>{})(e.id),this.clearPersistedData=(t=>e=>{const i=x+"/"+t+"/";if(e)localStorage.removeItem(i+e);else for(let t in localStorage)t.includes(i)&&localStorage.removeItem(t)})(e.id),this.applySettings(t,e),this.state={inputText:"",editing:!1,composing:!1,actions:{},mixMode:{},dropdown:{},flaggedTags:{}},this.value=[],this.listeners={},this.DOM={},this.build(t),b.call(this),this.getCSSVars(),this.loadOriginalValues(),this.events.customBinding.call(this),this.events.binding.call(this),t.autofocus&&this.DOM.input.focus(),t.__tagify=this}return N.prototype={_dropdown:y,helpers:{sameStr:s,removeCollectionProp:a,omit:n,isObject:h,parseHTML:r,escapeHTML:d,extend:g,concatWithoutDups:p,getUID:m,isNodeTag:v},customEventsList:["change","add","remove","invalid","input","click","keydown","focus","blur","edit:input","edit:beforeUpdate","edit:updated","edit:start","edit:keydown","dropdown:show","dropdown:hide","dropdown:select","dropdown:updated","dropdown:noMatch","dropdown:scroll"],dataProps:["__isValid","__removed","__originalData","__originalHTML","__tagId"],trim(t){return this.settings.trim&&t&&"string"==typeof t?t.trim():t},parseHTML:r,templates:M,parseTemplate(t,e){return r((t=this.settings.templates[t]||t).apply(this,e))},set whitelist(t){const e=t&&Array.isArray(t);this.settings.whitelist=e?t:[],this.setPersistedData(e?t:[],"whitelist")},get whitelist(){return this.settings.whitelist},generateClassSelectors(t){for(let e in t){let i=e;Object.defineProperty(t,i+"Selector",{get(){return"."+this[i].split(" ")[0]}})}},applySettings(t,i){w.templates=this.templates;var s=this.settings=g({},w,i);if(s.disabled=t.hasAttribute("disabled"),s.readonly=s.readonly||t.hasAttribute("readonly"),s.placeholder=d(t.getAttribute("placeholder")||s.placeholder||""),s.required=t.hasAttribute("required"),this.generateClassSelectors(s.classNames),void 0===s.dropdown.includeSelectedTags&&(s.dropdown.includeSelectedTags=s.duplicates),this.isIE&&(s.autoComplete=!1),["whitelist","blacklist"].forEach((e=>{var i=t.getAttribute("data-"+e);i&&(i=i.split(s.delimiters))instanceof Array&&(s[e]=i)})),"autoComplete"in i&&!h(i.autoComplete)&&(s.autoComplete=w.autoComplete,s.autoComplete.enabled=i.autoComplete),"mix"==s.mode&&(s.autoComplete.rightKey=!0,s.delimiters=i.delimiters||null,s.tagTextProp&&!s.dropdown.searchKeys.includes(s.tagTextProp)&&s.dropdown.searchKeys.push(s.tagTextProp)),t.pattern)try{s.pattern=new RegExp(t.pattern)}catch(t){}if(s.delimiters){s._delimiters=s.delimiters;try{s.delimiters=new RegExp(this.settings.delimiters,"g")}catch(t){}}s.disabled&&(s.userInput=!1),this.TEXTS=e(e({},D),s.texts||{}),("select"!=s.mode||i.dropdown.enabled)&&s.userInput||(s.dropdown.enabled=0),s.dropdown.appendTarget=i.dropdown&&i.dropdown.appendTarget?i.dropdown.appendTarget:document.body;let a=this.getPersistedData("whitelist");Array.isArray(a)&&(this.whitelist=Array.isArray(s.whitelist)?p(s.whitelist,a):a)},getAttributes(t){var e,i=this.getCustomAttributes(t),s="";for(e in i)s+=" "+e+(void 0!==t[e]?`="${i[e]}"`:"");return s},getCustomAttributes(t){if(!h(t))return"";var e,i={};for(e in t)"__"!=e.slice(0,2)&&"class"!=e&&t.hasOwnProperty(e)&&void 0!==t[e]&&(i[e]=d(t[e]));return i},setStateSelection(){var t=window.getSelection(),e={anchorOffset:t.anchorOffset,anchorNode:t.anchorNode,range:t.getRangeAt&&t.rangeCount&&t.getRangeAt(0)};return this.state.selection=e,e},getCSSVars(){var t=getComputedStyle(this.DOM.scope,null);var e;this.CSSVars={tagHideTransition:(t=>{let e=t.value;return"s"==t.unit?1e3*e:e})(function(t){if(!t)return{};var e=(t=t.trim().split(" ")[0]).split(/\d+/g).filter((t=>t)).pop().trim();return{value:+t.split(e).filter((t=>t))[0].trim(),unit:e}}((e="tag-hide-transition",t.getPropertyValue("--"+e))))}},build(t){var e=this.DOM;this.settings.mixMode.integrated?(e.originalInput=null,e.scope=t,e.input=t):(e.originalInput=t,e.originalInput_tabIndex=t.tabIndex,e.scope=this.parseTemplate("wrapper",[t,this.settings]),e.input=e.scope.querySelector(this.settings.classNames.inputSelector),t.parentNode.insertBefore(e.scope,t),t.tabIndex=-1)},destroy(){this.events.unbindGlobal.call(this),this.DOM.scope.parentNode.removeChild(this.DOM.scope),this.DOM.originalInput.tabIndex=this.DOM.originalInput_tabIndex,delete this.DOM.originalInput.__tagify,this.dropdown.hide(!0),clearTimeout(this.dropdownHide__bindEventsTimeout),clearInterval(this.listeners.main.originalInputValueObserverInterval)},loadOriginalValues(t){var e,i=this.settings;if(this.state.blockChangeEvent=!0,void 0===t){const e=this.getPersistedData("value");t=e&&!this.DOM.originalInput.value?e:i.mixMode.integrated?this.DOM.input.textContent:this.DOM.originalInput.value}if(this.removeAllTags(),t)if("mix"==i.mode)this.parseMixTags(t),(e=this.DOM.input.lastChild)&&"BR"==e.tagName||this.DOM.input.insertAdjacentHTML("beforeend","
");else{try{JSON.parse(t)instanceof Array&&(t=JSON.parse(t))}catch(t){}this.addTags(t,!0).forEach((t=>t&&t.classList.add(i.classNames.tagNoAnimation)))}else this.postUpdate();this.state.lastOriginalValueReported=i.mixMode.integrated?"":this.DOM.originalInput.value,this.state.blockChangeEvent=!1},cloneEvent(t){var e={};for(var i in t)"path"!=i&&(e[i]=t[i]);return e},loading(t){return this.state.isLoading=t,this.DOM.scope.classList[t?"add":"remove"](this.settings.classNames.scopeLoading),this},tagLoading(t,e){return t&&t.classList[e?"add":"remove"](this.settings.classNames.tagLoading),this},toggleClass(t,e){"string"==typeof t&&this.DOM.scope.classList.toggle(t,e)},toggleScopeValidation(t){var e=!0===t||void 0===t;!this.settings.required&&t&&t===this.TEXTS.empty&&(e=!0),this.toggleClass(this.settings.classNames.tagInvalid,!e),this.DOM.scope.title=e?"":t},toggleFocusClass(t){this.toggleClass(this.settings.classNames.focus,!!t)},triggerChangeEvent:function(){if(!this.settings.mixMode.integrated){var t=this.DOM.originalInput,e=this.state.lastOriginalValueReported!==t.value,i=new CustomEvent("change",{bubbles:!0});e&&(this.state.lastOriginalValueReported=t.value,i.simulated=!0,t._valueTracker&&t._valueTracker.setValue(Math.random()),t.dispatchEvent(i),this.trigger("change",this.state.lastOriginalValueReported),t.value=this.state.lastOriginalValueReported)}},events:I,fixFirefoxLastTagNoCaret(){},setRangeAtStartEnd(t,e){t="number"==typeof t?t:!!t,e=e.lastChild||e;var i=document.getSelection();if(i.focusNode&&i.focusNode!==this.DOM.input)return!0;try{i.rangeCount>=1&&["Start","End"].forEach((s=>i.getRangeAt(0)["set"+s](e,t||e.length)))}catch(t){}},placeCaretAfterNode(t){if(t&&t.parentNode){var e=t,i=window.getSelection(),s=i.getRangeAt(0);i.rangeCount&&(s.setStartAfter(e||t),s.collapse(!0),i.removeAllRanges(),i.addRange(s))}},insertAfterTag(t,e){if(e=e||this.settings.mixMode.insertAfterTag,t&&t.parentNode&&e)return e="string"==typeof e?document.createTextNode(e):e,t.parentNode.insertBefore(e,t.nextSibling),e},editTagChangeDetected(t){var e=t.__originalData;for(var i in e)if(!this.dataProps.includes(i)&&t[i]!=e[i])return!0;return!1},getTagTextNode(t){return t.querySelector(this.settings.classNames.tagTextSelector)},setTagTextNode(t,e){this.getTagTextNode(t).innerHTML=d(e)},editTag(t,e){t=t||this.getLastTag(),e=e||{},this.dropdown.hide();var i=this.settings,s=this.getTagTextNode(t),a=this.getNodeIndex(t),n=T(t),o=this.events.callbacks,r=this,l=!0;if(s){if(!(n instanceof Object&&"editable"in n)||n.editable)return n=T(t,{__originalData:g({},n),__originalHTML:t.cloneNode(!0)}),T(n.__originalHTML,n.__originalData),s.setAttribute("contenteditable",!0),t.classList.add(i.classNames.tagEditing),s.addEventListener("focus",o.onEditTagFocus.bind(this,t)),s.addEventListener("blur",(function(){setTimeout((()=>o.onEditTagBlur.call(r,r.getTagTextNode(t))))})),s.addEventListener("input",o.onEditTagInput.bind(this,s)),s.addEventListener("paste",o.onEditTagPaste.bind(this,s)),s.addEventListener("keydown",(e=>o.onEditTagkeydown.call(this,e,t))),s.addEventListener("compositionstart",o.onCompositionStart.bind(this)),s.addEventListener("compositionend",o.onCompositionEnd.bind(this)),e.skipValidation||(l=this.editTagToggleValidity(t)),s.originalIsValid=l,this.trigger("edit:start",{tag:t,index:a,data:n,isValid:l}),s.focus(),this.setRangeAtStartEnd(!1,s),this}else console.warn("Cannot find element in Tag template: .",i.classNames.tagTextSelector)},editTagToggleValidity(t,e){var i;if(e=e||T(t))return(i=!("__isValid"in e)||!0===e.__isValid)||this.removeTagsFromValue(t),this.update(),t.classList.toggle(this.settings.classNames.tagNotAllowed,!i),e.__isValid;console.warn("tag has no data: ",t,e)},onEditTagDone(t,e){e=e||{};var i={tag:t=t||this.state.editing.scope,index:this.getNodeIndex(t),previousData:T(t),data:e};this.trigger("edit:beforeUpdate",i,{cloneData:!1}),this.state.editing=!1,delete e.__originalData,delete e.__originalHTML,t&&e[this.settings.tagTextProp]?(t=this.replaceTag(t,e),this.editTagToggleValidity(t,e),this.settings.a11y.focusableTags?t.focus():this.placeCaretAfterNode(t)):t&&this.removeTags(t),this.trigger("edit:updated",i),this.dropdown.hide(),this.settings.keepInvalidTags&&this.reCheckInvalidTags()},replaceTag(t,e){e&&e.value||(e=t.__tagifyTagData),e.__isValid&&1!=e.__isValid&&g(e,this.getInvalidTagAttrs(e,e.__isValid));var i=this.createTagElem(e);return t.parentNode.replaceChild(i,t),this.updateValueByDOMTags(),i},updateValueByDOMTags(){this.value.length=0,[].forEach.call(this.getTagElms(),(t=>{t.classList.contains(this.settings.classNames.tagNotAllowed.split(" ")[0])||this.value.push(T(t))})),this.update()},injectAtCaret(t,e){var i;return!(e=e||(null===(i=this.state.selection)||void 0===i?void 0:i.range))&&t?(this.appendMixTags(t),this):(f(t,e),this.setRangeAtStartEnd(!1,content),this.updateValueByDOMTags(),this.update(),this)},input:{set(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];var i=this.settings.dropdown.closeOnSelect;this.state.inputText=t,e&&(this.DOM.input.innerHTML=d(""+t)),!t&&i&&this.dropdown.hide.bind(this),this.input.autocomplete.suggest.call(this),this.input.validate.call(this)},raw(){return this.DOM.input.textContent},validate(){var t=!this.state.inputText||!0===this.validateTag({value:this.state.inputText});return this.DOM.input.classList.toggle(this.settings.classNames.inputInvalid,!t),t},normalize(t){var e=t||this.DOM.input,i=[];e.childNodes.forEach((t=>3==t.nodeType&&i.push(t.nodeValue))),i=i.join("\n");try{i=i.replace(/(?:\r\n|\r|\n)/g,this.settings.delimiters.source.charAt(0))}catch(t){}return i=i.replace(/\s/g," "),this.trim(i)},autocomplete:{suggest(t){if(this.settings.autoComplete.enabled){"string"==typeof(t=t||{})&&(t={value:t});var e=t.value?""+t.value:"",i=e.substr(0,this.state.inputText.length).toLowerCase(),s=e.substring(this.state.inputText.length);e&&this.state.inputText&&i==this.state.inputText.toLowerCase()?(this.DOM.input.setAttribute("data-suggest",s),this.state.inputSuggestion=t):(this.DOM.input.removeAttribute("data-suggest"),delete this.state.inputSuggestion)}},set(t){var e=this.DOM.input.getAttribute("data-suggest"),i=t||(e?this.state.inputText+e:null);return!!i&&("mix"==this.settings.mode?this.replaceTextWithNode(document.createTextNode(this.state.tag.prefix+i)):(this.input.set.call(this,i),this.setRangeAtStartEnd(!1,this.DOM.input)),this.input.autocomplete.suggest.call(this),this.dropdown.hide(),!0)}}},getTagIdx(t){return this.value.findIndex((e=>e.__tagId==(t||{}).__tagId))},getNodeIndex(t){var e=0;if(t)for(;t=t.previousElementSibling;)e++;return e},getTagElms(){for(var t=arguments.length,e=new Array(t),i=0;i{s(this.trim(i.textContent),t,this.settings.dropdown.caseSensitive)&&e.push(a)})),e},getTagElmByValue(t){var e=this.getTagIndexByValue(t)[0];return this.getTagElms()[e]},flashTag(t){t&&(t.classList.add(this.settings.classNames.tagFlash),setTimeout((()=>{t.classList.remove(this.settings.classNames.tagFlash)}),100))},isTagBlacklisted(t){return t=this.trim(t.toLowerCase()),this.settings.blacklist.filter((e=>(""+e).toLowerCase()==t)).length},isTagWhitelisted(t){return!!this.getWhitelistItem(t)},getWhitelistItem(t,e,i){e=e||"value";var a,n=this.settings;return(i=i||n.whitelist).some((i=>{var o="string"==typeof i?i:i[e]||i.value;if(s(o,t,n.dropdown.caseSensitive,n.trim))return a="string"==typeof i?{value:i}:i,!0})),a||"value"!=e||"value"==n.tagTextProp||(a=this.getWhitelistItem(t,n.tagTextProp,i)),a},validateTag(t){var e=this.settings,i="value"in t?"value":e.tagTextProp,s=this.trim(t[i]+"");return(t[i]+"").trim()?e.pattern&&e.pattern instanceof RegExp&&!e.pattern.test(s)?this.TEXTS.pattern:!e.duplicates&&this.isTagDuplicate(s,e.dropdown.caseSensitive,t.__tagId)?this.TEXTS.duplicate:this.isTagBlacklisted(s)||e.enforceWhitelist&&!this.isTagWhitelisted(s)?this.TEXTS.notAllowed:!e.validate||e.validate(t):this.TEXTS.empty},getInvalidTagAttrs(t,e){return{"aria-invalid":!0,class:`${t.class||""} ${this.settings.classNames.tagNotAllowed}`.trim(),title:e}},hasMaxTags(){return this.value.length>=this.settings.maxTags&&this.TEXTS.exceed},setReadonly(t,e){var i=this.settings;document.activeElement.blur(),i[e||"readonly"]=t,this.DOM.scope[(t?"set":"remove")+"Attribute"](e||"readonly",!0),this.setContentEditable(!t)},setContentEditable(t){this.settings.userInput&&(this.DOM.input.contentEditable=t,this.DOM.input.tabIndex=t?0:-1)},setDisabled(t){this.setReadonly(t,"disabled")},normalizeTags(t){var e=this.settings,i=e.whitelist,s=e.delimiters,a=e.mode,n=e.tagTextProp;e.enforceWhitelist;var o=[],r=!!i&&i[0]instanceof Object,l=Array.isArray(t),d=l&&t[0].value,h=t=>(t+"").split(s).filter((t=>t)).map((t=>({[n]:this.trim(t),value:this.trim(t)})));if("number"==typeof t&&(t=t.toString()),"string"==typeof t){if(!t.trim())return[];t=h(t)}else l&&(t=[].concat(...t.map((t=>t.value?t:h(t)))));return r&&!d&&(t.forEach((t=>{var e=o.map((t=>t.value)),i=this.dropdown.filterListItems.call(this,t[n],{exact:!0});this.settings.duplicates||(i=i.filter((t=>!e.includes(t.value))));var s=i.length>1?this.getWhitelistItem(t[n],n,i):i[0];s&&s instanceof Object?o.push(s):"mix"!=a&&(null==t.value&&(t.value=t[n]),o.push(t))})),o.length&&(t=o)),t},parseMixTags(t){var e=this.settings,i=e.mixTagsInterpolator,s=e.duplicates,a=e.transformTag,n=e.enforceWhitelist,o=e.maxTags,r=e.tagTextProp,l=[];return t=t.split(i[0]).map(((t,e)=>{var d,h,g,p=t.split(i[1]),c=p[0],u=l.length==o;try{if(c==+c)throw Error;h=JSON.parse(c)}catch(t){h=this.normalizeTags(c)[0]||{value:c}}if(a.call(this,h),u||!(p.length>1)||n&&!this.isTagWhitelisted(h.value)||!s&&this.isTagDuplicate(h.value)){if(t)return e?i[0]+t:t}else h[d=h[r]?r:"value"]=this.trim(h[d]),g=this.createTagElem(h),l.push(h),g.classList.add(this.settings.classNames.tagNoAnimation),p[0]=g.outerHTML,this.value.push(h);return p.join("")})).join(""),this.DOM.input.innerHTML=t,this.DOM.input.appendChild(document.createTextNode("")),this.DOM.input.normalize(),this.getTagElms().forEach(((t,e)=>T(t,l[e]))),this.update({withoutChangeEvent:!0}),t},replaceTextWithNode(t,e){if(this.state.tag||e){e=e||this.state.tag.prefix+this.state.tag.value;var i,s,a=this.state.selection||window.getSelection(),n=a.anchorNode,o=this.state.tag.delimiters?this.state.tag.delimiters.length:0;return n.splitText(a.anchorOffset-o),-1==(i=n.nodeValue.lastIndexOf(e))?!0:(s=n.splitText(i),t&&n.parentNode.replaceChild(t,s),!0)}},selectTag(t,e){var i=this.settings;if(!i.enforceWhitelist||this.isTagWhitelisted(e.value)){this.input.set.call(this,e[i.tagTextProp]||e.value,!0),this.state.actions.selectOption&&setTimeout((()=>this.setRangeAtStartEnd(!1,this.DOM.input)));var s=this.getLastTag();return s?this.replaceTag(s,e):this.appendTag(t),this.value[0]=e,this.update(),this.trigger("add",{tag:t,data:e}),[t]}},addEmptyTag(t){var e=g({value:""},t||{}),i=this.createTagElem(e);T(i,e),this.appendTag(i),this.editTag(i,{skipValidation:!0})},addTags(t,e,i){var s=[],a=this.settings,n=[],o=document.createDocumentFragment();if(i=i||a.skipInvalid,!t||0==t.length)return s;switch(t=this.normalizeTags(t),a.mode){case"mix":return this.addMixTags(t);case"select":e=!1,this.removeAllTags()}return this.DOM.input.removeAttribute("style"),t.forEach((t=>{var e,r={},l=Object.assign({},t,{value:t.value+""});if(t=Object.assign({},l),a.transformTag.call(this,t),t.__isValid=this.hasMaxTags()||this.validateTag(t),!0!==t.__isValid){if(i)return;if(g(r,this.getInvalidTagAttrs(t,t.__isValid),{__preInvalidData:l}),t.__isValid==this.TEXTS.duplicate&&this.flashTag(this.getTagElmByValue(t.value)),!a.createInvalidTags)return void n.push(t.value)}if("readonly"in t&&(t.readonly?r["aria-readonly"]=!0:delete t.readonly),e=this.createTagElem(t,r),s.push(e),"select"==a.mode)return this.selectTag(e,t);o.appendChild(e),t.__isValid&&!0===t.__isValid?(this.value.push(t),this.trigger("add",{tag:e,index:this.value.length-1,data:t})):(this.trigger("invalid",{data:t,index:this.value.length,tag:e,message:t.__isValid}),a.keepInvalidTags||setTimeout((()=>this.removeTags(e,!0)),1e3)),this.dropdown.position()})),this.appendTag(o),this.update(),t.length&&e&&(this.input.set.call(this,a.createInvalidTags?"":n.join(a._delimiters)),this.setRangeAtStartEnd(!1,this.DOM.input)),a.dropdown.enabled&&this.dropdown.refilter(),s},addMixTags(t){if((t=this.normalizeTags(t))[0].prefix||this.state.tag)return this.prefixedTextToTag(t[0]);"string"==typeof t&&(t=[{value:t}]),this.state.selection;var e=document.createDocumentFragment();return t.forEach((t=>{var i=this.createTagElem(t);e.appendChild(i),this.insertAfterTag(i)})),this.appendMixTags(e),e},appendMixTags(t){var e=!!this.state.selection;e?this.injectAtCaret(t):(this.DOM.input.focus(),(e=this.setStateSelection()).range.setStart(this.DOM.input,e.range.endOffset),e.range.setEnd(this.DOM.input,e.range.endOffset),this.DOM.input.appendChild(t),this.updateValueByDOMTags(),this.update())},prefixedTextToTag(t){var e,i=this.settings,s=this.state.tag.delimiters;if(i.transformTag.call(this,t),t.prefix=t.prefix||this.state.tag?this.state.tag.prefix:(i.pattern.source||i.pattern)[0],e=this.createTagElem(t),this.replaceTextWithNode(e)||this.DOM.input.appendChild(e),setTimeout((()=>e.classList.add(this.settings.classNames.tagNoAnimation)),300),this.value.push(t),this.update(),!s){var a=this.insertAfterTag(e)||e;this.placeCaretAfterNode(a)}return this.state.tag=null,this.trigger("add",g({},{tag:e},{data:t})),e},appendTag(t){var e=this.DOM,i=e.input;i===e.input?e.scope.insertBefore(t,i):e.scope.appendChild(t)},createTagElem(t,i){t.__tagId=m();var s,a=g({},t,e({value:d(t.value+"")},i));return function(t){for(var e,i=document.createNodeIterator(t,NodeFilter.SHOW_TEXT,null,!1);e=i.nextNode();)e.textContent.trim()||e.parentNode.removeChild(e)}(s=this.parseTemplate("tag",[a,this])),T(s,t),s},reCheckInvalidTags(){var t=this.settings;this.getTagElms(t.classNames.tagNotAllowed).forEach(((e,i)=>{var s=T(e),a=this.hasMaxTags(),n=this.validateTag(s),o=!0===n&&!a;if("select"==t.mode&&this.toggleScopeValidation(n),o)return s=s.__preInvalidData?s.__preInvalidData:{value:s.value},this.replaceTag(e,s);e.title=a||n}))},removeTags(t,e,i){var s,a=this.settings;if(t=t&&t instanceof HTMLElement?[t]:t instanceof Array?t:t?[t]:[this.getLastTag()],s=t.reduce(((t,e)=>{e&&"string"==typeof e&&(e=this.getTagElmByValue(e));var i=T(e);return e&&i&&!i.readonly&&t.push({node:e,idx:this.getTagIdx(i),data:T(e,{__removed:!0})}),t}),[]),i="number"==typeof i?i:this.CSSVars.tagHideTransition,"select"==a.mode&&(i=0,this.input.set.call(this)),1==s.length&&"select"!=a.mode&&s[0].node.classList.contains(a.classNames.tagNotAllowed)&&(e=!0),s.length)return a.hooks.beforeRemoveTag(s,{tagify:this}).then((()=>{function t(t){t.node.parentNode&&(t.node.parentNode.removeChild(t.node),e?a.keepInvalidTags&&this.trigger("remove",{tag:t.node,index:t.idx}):(this.trigger("remove",{tag:t.node,index:t.idx,data:t.data}),this.dropdown.refilter(),this.dropdown.position(),this.DOM.input.normalize(),a.keepInvalidTags&&this.reCheckInvalidTags()))}i&&i>10&&1==s.length?function(e){e.node.style.width=parseFloat(window.getComputedStyle(e.node).width)+"px",document.body.clientTop,e.node.classList.add(a.classNames.tagHide),setTimeout(t.bind(this),i,e)}.call(this,s[0]):s.forEach(t.bind(this)),e||(this.removeTagsFromValue(s.map((t=>t.node))),this.update(),"select"==a.mode&&this.setContentEditable(!0))})).catch((t=>{}))},removeTagsFromDOM(){[].slice.call(this.getTagElms()).forEach((t=>t.parentNode.removeChild(t)))},removeTagsFromValue(t){(t=Array.isArray(t)?t:[t]).forEach((t=>{var e=T(t),i=this.getTagIdx(e);i>-1&&this.value.splice(i,1)}))},removeAllTags(t){t=t||{},this.value=[],"mix"==this.settings.mode?this.DOM.input.innerHTML="":this.removeTagsFromDOM(),this.dropdown.refilter(),this.dropdown.position(),this.state.dropdown.visible&&setTimeout((()=>{this.DOM.input.focus()})),"select"==this.settings.mode&&(this.input.set.call(this),this.setContentEditable(!0)),this.update(t)},postUpdate(){var t,e,i=this.settings,s=i.classNames,a="mix"==i.mode?i.mixMode.integrated?this.DOM.input.textContent:this.DOM.originalInput.value.trim():this.value.length+this.input.raw.call(this).length;(this.toggleClass(s.hasMaxTags,this.value.length>=i.maxTags),this.toggleClass(s.hasNoTags,!this.value.length),this.toggleClass(s.empty,!a),"select"==i.mode)&&this.toggleScopeValidation(null===(t=this.value)||void 0===t||null===(e=t[0])||void 0===e?void 0:e.__isValid)},setOriginalInputValue(t){var e=this.DOM.originalInput;this.settings.mixMode.integrated||(e.value=t,e.tagifyValue=e.value,this.setPersistedData(t,"value"))},update(t){clearTimeout(this.debouncedUpdateTimeout),this.debouncedUpdateTimeout=setTimeout(function(){var e=this.getInputValue();this.setOriginalInputValue(e),this.postUpdate(),this.settings.onChangeAfterBlur&&(t||{}).withoutChangeEvent||this.state.blockChangeEvent||this.triggerChangeEvent()}.bind(this),100)},getInputValue(){var t=this.getCleanValue();return"mix"==this.settings.mode?this.getMixedTagsAsString(t):t.length?this.settings.originalInputValueFormat?this.settings.originalInputValueFormat(t):JSON.stringify(t):""},getCleanValue(t){return a(t||this.value,this.dataProps)},getMixedTagsAsString(){var t="",e=this,i=this.settings,s=i.originalInputValueFormat||JSON.stringify,a=i.mixTagsInterpolator;return function i(o){o.childNodes.forEach((o=>{if(1==o.nodeType){const r=T(o);if("BR"==o.tagName&&(t+="\r\n"),r&&v.call(e,o)){if(r.__removed)return;t+=a[0]+s(n(r,e.dataProps))+a[1]}else o.getAttribute("style")||["B","I","U"].includes(o.tagName)?t+=o.textContent:"DIV"!=o.tagName&&"P"!=o.tagName||(t+="\r\n",i(o))}else t+=o.textContent}))}(this.DOM.input),t}},N.prototype.removeTag=N.prototype.removeTags,N}));