diff --git a/dist/jQuery.tagify.min.js b/dist/jQuery.tagify.min.js index ac64ea7e..58c7dfb2 100644 --- a/dist/jQuery.tagify.min.js +++ b/dist/jQuery.tagify.min.js @@ -14,5 +14,5 @@ }); } -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).Tagify=e()}(this,(function(){"use strict";function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}function e(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function i(t){return function(t){if(Array.isArray(t)){for(var e=0,i=new Array(t.length);e/g,">").replace(/"/g,""").replace(/`|'/g,"'")}function r(t){return t instanceof Array}function l(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 d(t,e,i){function s(t,e){for(var i in e)if(e.hasOwnProperty(i)){if(l(e[i])){l(t[i])?s(t[i],e[i]):t[i]=Object.assign({},e[i]);continue}if(r(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 c(t){return String.prototype.normalize?"string"==typeof t?t.normalize("NFD").replace(/[\u0300-\u036f]/g,""):void 0:t}var h=/(?=.*chrome)(?=.*android)/i.test(navigator.userAgent),g={init:function(){this.DOM.dropdown=this.parseTemplate("dropdown",[this.settings]),this.DOM.dropdown.content=this.DOM.dropdown.querySelector("."+this.settings.classNames.dropdownWrapper)},show:function(t){var e,i,a,n=this,o=this.settings,r=window.getSelection(),d="mix"==o.mode&&!o.enforceWhitelist,c=!o.whitelist||!o.whitelist.length,h="manual"==o.dropdown.position;if(t=void 0===t?this.state.inputText:t,(!c||d||o.templates.dropdownItemNoMatch)&&!1!==o.dropdown.enable&&!this.state.isLoading){if(clearTimeout(this.dropdownHide__bindEventsTimeout),this.suggestedListItems=this.dropdown.filterListItems.call(this,t),t&&!this.suggestedListItems.length&&(this.trigger("dropdown:noMatch",t),o.templates.dropdownItemNoMatch&&(a=o.templates.dropdownItemNoMatch.call(this,{value:t}))),!a){if(this.suggestedListItems.length)t&&d&&!this.state.editing.scope&&!s(this.suggestedListItems[0].value,t)&&this.suggestedListItems.unshift({value:t});else{if(!t||!d||this.state.editing.scope)return this.input.autocomplete.suggest.call(this),void this.dropdown.hide.call(this);this.suggestedListItems=[{value:t}]}i=""+(l(e=this.suggestedListItems[0])?e.value:e),o.autoComplete&&i&&0==i.indexOf(t)&&this.input.autocomplete.suggest.call(this,e)}this.dropdown.fill.call(this,a),o.dropdown.highlightFirst&&this.dropdown.highlightOption.call(this,this.DOM.dropdown.content.children[0]),this.state.dropdown.visible||setTimeout(this.dropdown.events.binding.bind(this)),this.state.dropdown.visible=t||!0,this.state.dropdown.query=t,this.state.selection={anchorOffset:r.anchorOffset,anchorNode:r.anchorNode},h||setTimeout((function(){n.dropdown.position.call(n),n.dropdown.render.call(n)})),setTimeout((function(){n.trigger("dropdown:show",n.DOM.dropdown)}))}},hide:function(t){var e=this,i=this.DOM,s=i.scope,a=i.dropdown,n="manual"==this.settings.dropdown.position&&!t;if(a&&document.body.contains(a)&&!n)return window.removeEventListener("resize",this.dropdown.position),this.dropdown.events.binding.call(this,!1),s.setAttribute("aria-expanded",!1),a.parentNode.removeChild(a),setTimeout((function(){e.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",a),this},render:function(){var t,e,i,s=this,a=(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),n=this.settings;return this.DOM.scope.setAttribute("aria-expanded",!0),document.body.contains(this.DOM.dropdown)||(this.DOM.dropdown.classList.add(n.classNames.dropdownInital),this.dropdown.position.call(this,a),n.dropdown.appendTarget.appendChild(this.DOM.dropdown),setTimeout((function(){return s.DOM.dropdown.classList.remove(n.classNames.dropdownInital)}))),this},fill:function(t){var e;t="string"==typeof t?t:this.dropdown.createListHTML.call(this,t||this.suggestedListItems),this.DOM.dropdown.content.innerHTML=(e=t)?e.replace(/\>[\r\n ]+\<").replace(/(<.*?>)|\s+/g,(function(t,e){return e||" "})):""},refilter:function(t){t=t||this.state.dropdown.query||"",this.suggestedListItems=this.dropdown.filterListItems.call(this,t),this.suggestedListItems.length?this.dropdown.fill.call(this):this.dropdown.hide.call(this),this.trigger("dropdown:updated",this.DOM.dropdown)},position:function(t){if("manual"!=this.settings.dropdown.position){var e,i,s,a,n,o,r,l=this.DOM.dropdown,d=document.documentElement.clientHeight,c=Math.max(document.documentElement.clientWidth||0,window.innerWidth||0)>480?this.settings.dropdown.position:"all",h=this.DOM["input"==c?"input":"scope"];t=t||l.clientHeight,this.state.dropdown.visible&&("text"==c?(a=(i=this.getCaretGlobalPosition()).bottom,s=i.top,n=i.left,o="auto"):(r=function(t){for(var e=0,i=0;t;)e+=t.offsetLeft||0,i+=t.offsetTop||0,t=t.parentNode;return{left:e,top:i}}(this.settings.dropdown.appendTarget),s=(i=h.getBoundingClientRect()).top+2-r.top,a=i.bottom-1-r.top,n=i.left-r.left,o=i.width+"px"),s=Math.floor(s),a=Math.ceil(a),e=d-i.bottom0&&void 0!==arguments[0])||arguments[0],e=this.dropdown.events.callbacks,i=this.listeners.dropdown=this.listeners.dropdown||{position:this.dropdown.position.bind(this),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&&(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:function(t){var e=this.DOM.dropdown.querySelector("."+this.settings.classNames.dropdownItemActive),i=e;switch(t.key){case"ArrowDown":case"ArrowUp":case"Down":case"Up":var s;t.preventDefault(),i&&(i=i[("ArrowUp"==t.key||"Up"==t.key?"previous":"next")+"ElementSibling"]),i||(i=(s=this.DOM.dropdown.content.children)["ArrowUp"==t.key||"Up"==t.key?s.length-1:0]),this.dropdown.highlightOption.call(this,i,!0);break;case"Escape":case"Esc":this.dropdown.hide.call(this);break;case"ArrowRight":if(this.state.actions.ArrowLeft)return;case"Tab":if("mix"!=this.settings.mode&&i&&!this.settings.autoComplete.rightKey&&!this.state.editing){t.preventDefault();var a=i.getAttribute("tagifySuggestionIdx"),n=a?this.suggestedListItems[+a]:"";return this.input.autocomplete.set.call(this,n.value||n),!1}return!0;case"Enter":t.preventDefault(),this.dropdown.selectOption.call(this,e);break;case"Backspace":if("mix"==this.settings.mode||this.state.editing.scope)return;var o=this.state.inputText.trim();""!=o&&8203!=o.charCodeAt(0)||(!0===this.settings.backspace?this.removeTags():"edit"==this.settings.backspace&&setTimeout(this.editTag.bind(this),0))}},onMouseOver:function(t){var e=t.target.closest("."+this.settings.classNames.dropdownItem);e&&this.dropdown.highlightOption.call(this,e)},onMouseLeave:function(t){this.dropdown.highlightOption.call(this)},onClick:function(t){var e=this;if(0==t.button&&t.target!=this.DOM.dropdown){var i=t.target.closest("."+this.settings.classNames.dropdownItem);this.state.actions.selectOption=!0,setTimeout((function(){return e.state.actions.selectOption=!1}),50),this.settings.hooks.suggestionClick(t,{tagify:this,suggestionElm:i}).then((function(){i?e.dropdown.selectOption.call(e,i):e.dropdown.hide.call(e)})).catch((function(t){return t}))}},onScroll:function(t){var e=t.target,i=e.scrollTop/(e.scrollHeight-e.parentNode.clientHeight)*100;this.trigger("dropdown:scroll",{percentage:Math.round(i)})}}},highlightOption:function(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.suggestedListItems[this.getNodeIndex(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.call(this))},selectOption:function(t){var e=this,i=this.settings.dropdown,s=i.clearOnSelect,a=i.closeOnSelect;if(!t)return this.addTags(this.state.inputText,!0),void(a&&this.dropdown.hide.call(this));var n=t.getAttribute("tagifySuggestionIdx"),o=this.suggestedListItems[+n];if(this.trigger("dropdown:select",{data:o,elm:t}),n&&o){if(this.state.editing?this.onEditTagDone(null,d({__isValid:!0},o)):this["mix"==this.settings.mode?"addMixTags":"addTags"]([o],s),setTimeout((function(){e.DOM.input.focus(),e.toggleFocusClass(!0)})),a)return this.dropdown.hide.call(this);this.dropdown.refilter.call(this)}else this.dropdown.hide.call(this)},selectAll:function(){return this.suggestedListItems.length=0,this.dropdown.hide.call(this),this.addTags(this.dropdown.filterListItems.call(this,""),!0),this},filterListItems:function(t,e){var i,s,a,n,o,r=this,d=this.settings,h=d.dropdown,g=(e=e||{},[]),u=d.whitelist,p=h.maxItems||1/0,f=h.searchKeys,m=0;if(!t||!f.length)return(d.duplicates?u:u.filter((function(t){return!r.isTagDuplicate(l(t)?t.value:t)}))).slice(0,p);function v(t,e){return e.toLowerCase().split(" ").every((function(e){return t.includes(e.toLowerCase())}))}for(o=h.caseSensitive?""+t:(""+t).toLowerCase();m",' tagifySuggestionIdx="'.concat(i,'">'))})).join("")}},u={delimiters:",",pattern:null,tagTextProp:"value",maxTags:1/0,callbacks:{},addTagOnBlur:!0,duplicates:!1,whitelist:[],blacklist:[],enforceWhitelist:!1,keepInvalidTags:!1,mixTagsAllowedAfter:/,|\.|\:|\s/,mixTagsInterpolator:["[[","]]"],backspace:!0,skipInvalid:!1,editTags:{clicks:2,keepInvalid:!0},transformTag:function(){},trim:!0,mixMode:{insertAfterTag:" "},autoComplete:{enabled:!0,rightKey:!1},classNames:{namespace:"tagify",mixMode:"tagify--mix",selectMode:"tagify--select",input:"tagify__input",focus:"tagify--focus",tag:"tagify__tag",tagNoAnimation:"tagify--noAnim",tagInvalid:"tagify--invalid",tagNotAllowed:"tagify--notAllowed",inputInvalid:"tagify__input--invalid",tagX:"tagify__tag__removeBtn",tagText:"tagify__tag-text",dropdown:"tagify__dropdown",dropdownWrapper:"tagify__dropdown__wrapper",dropdownItem:"tagify__dropdown__item",dropdownItemActive:"tagify__dropdown__item--active",dropdownInital:"tagify__dropdown--initial",scopeLoading:"tagify--loading",tagLoading:"tagify__tag--loading",tagEditing:"tagify__tag--editable",tagFlash:"tagify__tag--flash",tagHide:"tagify__tag--hide",hasMaxTags:"tagify--hasMaxTags",hasNoTags:"tagify--noTags",empty:"tagify--empty"},dropdown:{classname:"",enabled:2,maxItems:10,searchKeys:["value","searchBy"],fuzzySearch:!0,caseSensitive:!1,accentedSearch:!0,highlightFirst:!1,closeOnSelect:!0,clearOnSelect:!0,position:"all",appendTarget:null},hooks:{beforeRemoveTag:function(){return Promise.resolve()},suggestionClick:function(){return Promise.resolve()}}};var p={customBinding:function(){var t=this;this.customEventsList.forEach((function(e){t.on(e,t.settings.callbacks[e])}))},binding:function(){var t,e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],i=this.events.callbacks,s=e?"addEventListener":"removeEventListener";if(!this.state.mainEvents||!e)for(var a in this.state.mainEvents=e,e&&!this.listeners.main&&(this.DOM.input.addEventListener(this.isIE?"keydown":"input",i[this.isIE?"onInputIE":"onInput"].bind(this)),this.settings.isJQueryPlugin&&jQuery(this.DOM.originalInput).on("tagify.removeAllTags",this.removeAllTags.bind(this))),t=this.listeners.main=this.listeners.main||{focus:["input",i.onFocusBlur.bind(this)],blur:["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)]})("blur"!=a||e)&&this.DOM[t[a][0]][s](a,t[a][1])},callbacks:{onFocusBlur:function(t){var e=t.target?this.trim(t.target.textContent):"",i=this.settings,s=t.type,a=i.dropdown.enabled>=0,n={relatedTarget:t.relatedTarget},o=this.state.actions.selectOption&&(a||!i.dropdown.closeOnSelect),r=this.state.actions.addNew&&a;if("blur"==s){if(t.relatedTarget===this.DOM.scope)return this.dropdown.hide.call(this),void this.DOM.input.focus();this.postUpdate(),this.triggerChangeEvent()}if(!o&&!r)if(this.state.hasFocus="focus"==s&&+new Date,this.toggleFocusClass(this.state.hasFocus),"mix"!=i.mode){if("focus"==s)return this.trigger("focus",n),void(0===i.dropdown.enabled&&this.dropdown.show.call(this));"blur"==s&&(this.trigger("blur",n),this.loading(!1),("select"==this.settings.mode?!this.value.length||this.value[0].value!=e:e&&!this.state.actions.selectOption&&i.addTagOnBlur)&&this.addTags(e,!0)),this.DOM.input.removeAttribute("style"),this.dropdown.hide.call(this)}else"focus"==s?this.trigger("focus",n):"blur"==t.type&&(this.trigger("blur",n),this.loading(!1),this.dropdown.hide.call(this),this.state.dropdown.visible=void 0,this.setStateSelection())},onKeydown:function(t){var e=this,i=this.trim(t.target.textContent);if(this.trigger("keydown",{originalEvent:this.cloneEvent(t)}),"mix"==this.settings.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,o,r=document.getSelection(),l="Delete"==t.key&&r.anchorOffset==(r.anchorNode.length||0),d=1==r.anchorNode.nodeType||!r.anchorOffset&&r.anchorNode.previousElementSibling,c=a(this.DOM.input.innerHTML),g=this.getTagElms();if(h&&d)return o=n(d),d.hasAttribute("readonly")||d.remove(),this.DOM.input.focus(),void setTimeout((function(){e.placeCaretAfterNode(o),e.DOM.input.click()}));if("BR"==r.anchorNode.nodeName)return;if((l||d)&&1==r.anchorNode.nodeType?s=0==r.anchorOffset?l?g[0]:null:g[r.anchorOffset-1]:l?s=r.anchorNode.nextElementSibling:d&&(s=d),3==r.anchorNode.nodeType&&!r.anchorNode.nodeValue&&r.anchorNode.previousElementSibling&&t.preventDefault(),(d||l)&&!this.settings.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&&s&&s.hasAttribute("readonly"))return void this.placeCaretAfterNode(n(s));this.isFirefox&&1==r.anchorNode.nodeType&&0!=r.anchorOffset&&(this.removeTags(),this.placeCaretAfterNode(this.setRangeAtStartEnd())),setTimeout((function(){var t=document.getSelection(),i=a(e.DOM.input.innerHTML),s=t.anchorNode.previousElementSibling;if(!h&&i.length>=c.length&&s&&!s.hasAttribute("readonly")&&(e.removeTags(s),e.fixFirefoxLastTagNoCaret(),2==e.DOM.input.children.length&&"BR"==e.DOM.input.children[1].tagName))return e.DOM.input.innerHTML="",e.value.length=0,!0;e.value=[].map.call(g,(function(t,i){var s=e.tagData(t);if(t.parentNode||s.readonly)return s;e.trigger("remove",{tag:t,index:i,data:s})})).filter((function(t){return t}))}),50)}return!0}switch(t.key){case"Backspace":this.state.dropdown.visible&&"manual"!=this.settings.dropdown.position||""!=i&&8203!=i.charCodeAt(0)||(!0===this.settings.backspace?this.removeTags():"edit"==this.settings.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.call(this);break;case"ArrowRight":var u=this.state.inputSuggestion||this.state.ddItemData;if(u&&this.settings.autoComplete.rightKey)return void this.addTags([u],!0);break;case"Tab":var p="select"==this.settings.mode;if(!i||p)return!0;t.preventDefault();case"Enter":if(this.state.dropdown.visible||229==t.keyCode)return;t.preventDefault(),setTimeout((function(){e.state.actions.selectOption||e.addTags(i,!0)}))}},onInput:function(t){if("mix"==this.settings.mode)return this.events.callbacks.onMixTagsInput.call(this,t);var e=this.input.normalize.call(this),i=e.length>=this.settings.dropdown.enabled,s={value:e,inputElm:this.DOM.input};s.isValid=this.validateTag({value:e}),this.trigger("input",s),this.state.inputText!=e&&(this.input.set.call(this,e,!1),-1!=e.search(this.settings.delimiters)?this.addTags(e)&&this.input.set.call(this):this.settings.dropdown.enabled>=0&&this.dropdown[i?"show":"hide"].call(this,e))},onMixTagsInput:function(t){var e,i,s,a,n,o,r,l,c=this,g=this.settings,u=this.value.length,p=this.getTagElms(),f=document.createDocumentFragment(),m=window.getSelection().getRangeAt(0),v=[].map.call(p,(function(t){return c.tagData(t).value}));if("deleteContentBackward"==t.inputType&&h&&this.events.callbacks.onKeydown.call(this,{target:t.target,key:"Backspace"}),this.value.slice().forEach((function(t){t.readonly&&!v.includes(t.value)&&f.appendChild(c.createTagElem(t))})),f.childNodes.length&&(m.insertNode(f),this.setRangeAtStartEnd(!1,f.lastChild)),p.length!=u)return this.value=[].map.call(this.getTagElms(),(function(t){return c.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(g.pattern).length-1,(i=e.match(g.pattern))&&(a=e.slice(e.lastIndexOf(i[i.length-1]))),a){if(this.state.actions.ArrowLeft=!1,this.state.tag={prefix:a.match(g.pattern)[0],value:a.replace(g.pattern,"")},this.state.tag.baseOffset=o.baseOffset-this.state.tag.value.length,l=this.state.tag.value.match(g.delimiters))return this.state.tag.value=this.state.tag.value.replace(g.delimiters,""),this.state.tag.delimiters=l[0],this.addTags(this.state.tag.value,g.dropdown.clearOnSelect),void this.dropdown.hide.call(this);n=this.state.tag.value.length>=g.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||s500)?this.state.dropdown.visible?this.dropdown.hide.call(this):0===e.dropdown.enabled&&"mix"!=e.mode&&this.dropdown.show.call(this):"select"==e.mode&&!this.state.dropdown.visible&&this.dropdown.show.call(this));this.removeTags(t.target.parentNode)}else this.state.hasFocus||this.DOM.input.focus()},onPaste:function(t){var e;t.preventDefault(),this.settings.readonly||(e=(t.clipboardData||window.clipboardData).getData("Text"),this.injectAtCaret(e,window.getSelection().getRangeAt(0)),"mix"!=this.settings.mode&&this.addTags(this.DOM.input.textContent,!0))},onEditTagInput:function(t,i){var s=t.closest("."+this.settings.classNames.tag),a=this.getNodeIndex(s),n=this.tagData(s),o=this.input.normalize.call(this,t),r=s.innerHTML!=s.__tagifyTagData.__originalHTML,l=this.validateTag(e({},this.settings.tagTextProp,o));r||!0!==t.originalIsValid||(l=!0),s.classList.toggle(this.settings.classNames.tagInvalid,!0!==l),n.__isValid=l,s.title=!0===l?n.title||n.value:l,o.length>=this.settings.dropdown.enabled&&(this.state.editing&&(this.state.editing.value=o),this.dropdown.show.call(this,o)),this.trigger("edit:input",{tag:s,index:a,data:d({},this.value[a],{newValue:o}),originalEvent:this.cloneEvent(i)})},onEditTagFocus:function(t){this.state.editing={scope:t,input:t.querySelector("[contenteditable]")}},onEditTagBlur:function(t){var i;if(this.state.hasFocus||this.toggleFocusClass(),this.DOM.scope.contains(t)){var s,a=this.settings,n=t.closest("."+a.classNames.tag),o=this.input.normalize.call(this,t),r=this.tagData(n).__originalData,l=n.innerHTML!=n.__tagifyTagData.__originalHTML,d=this.validateTag(e({},a.tagTextProp,o));if(o)if(l){if(s=this.getWhitelistItem(o)||(e(i={},a.tagTextProp,o),e(i,"value",o),i),a.transformTag.call(this,s,r),!0!==(d=this.validateTag(e({},a.tagTextProp,s[a.tagTextProp])))){if(this.trigger("invalid",{data:s,tag:n,message:d}),a.editTags.keepInvalid)return;a.keepInvalidTags?s.__isValid=d:s=r}this.onEditTagDone(n,s)}else this.onEditTagDone(n,r);else this.onEditTagDone(n)}},onEditTagkeydown:function(t,e){switch(this.trigger("edit:keydown",{originalEvent:this.cloneEvent(t)}),t.key){case"Esc":case"Escape":e.innerHTML=e.__tagifyTagData.__originalHTML;case"Enter":case"Tab":t.preventDefault(),t.target.blur()}},onDoubleClickScope:function(t){var e,i,s=t.target.closest("."+this.settings.classNames.tag),a=this.settings;s&&(e=s.classList.contains(this.settings.classNames.tagEditing),i=s.hasAttribute("readonly"),"select"==a.mode||a.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)}))}}};function f(e,i){return e?e.previousElementSibling&&e.previousElementSibling.classList.contains("tagify")?(console.warn("Tagify: ","input element is already Tagified",e),this):(d(this,function(e){var i=document.createTextNode("");function s(t,e,s){s&&e.split(/\s+/g).forEach((function(e){return i[t+"EventListener"].call(i,e,s)}))}return{off:function(t,e){return s("remove",t,e),this},on:function(t,e){return e&&"function"==typeof e&&s("add",t,e),this},trigger:function(s,a){var n;if(s)if(e.settings.isJQueryPlugin)"remove"==s&&(s="removeTag"),jQuery(e.DOM.originalInput).triggerHandler(s,[a]);else{try{var o=d({},"object"===t(a)?a:{value:a});if(o.tagify=this,a instanceof Object)for(var r in a)a[r]instanceof HTMLElement&&(o[r]=a[r]);n=new CustomEvent(s,{detail:o})}catch(t){console.warn(t)}i.dispatchEvent(n)}}}}(this)),this.isFirefox="undefined"!=typeof InstallTrigger,this.isIE=window.document.documentMode,this.applySettings(e,i||{}),this.state={inputText:"",editing:!1,actions:{},mixMode:{},dropdown:{},flaggedTags:{}},this.value=[],this.listeners={},this.DOM={},this.build(e),this.getCSSVars(),this.loadOriginalValues(),this.events.customBinding.call(this),this.events.binding.call(this),void(e.autofocus&&this.DOM.input.focus())):(console.warn("Tagify: ","input element not found",e),this)}return f.prototype={dropdown:g,TEXTS:{empty:"empty",exceed:"number of tags exceeded",pattern:"pattern mismatch",duplicate:"already exists",notAllowed:"not allowed"},customEventsList:["change","add","remove","invalid","input","click","keydown","focus","blur","edit:input","edit:updated","edit:start","edit:keydown","dropdown:show","dropdown:hide","dropdown:select","dropdown:updated","dropdown:noMatch"],trim:function(t){return this.settings.trim&&t&&"string"==typeof t?t.trim():t},parseHTML:function(t){return(new DOMParser).parseFromString(t.trim(),"text/html").body.firstElementChild},templates:{wrapper:function(t,e){return'\n \n ')},tag:function(t){return'\n \n
\n ').concat(t[this.settings.tagTextProp]||t.value,"\n
\n
")},dropdown:function(t){var e=t.dropdown,i="manual"==e.position,s="".concat(t.classNames.dropdown);return'
\n
\n
')},dropdownItem:function(t){return"
').concat(t.value,"
")},dropdownItemNoMatch:null},parseTemplate:function(t,e){return t=this.settings.templates[t]||t,this.parseHTML(t.apply(this,e))},applySettings:function(t,e){u.templates=this.templates;var i=this.settings=d({},u,e);if(i.readonly=t.hasAttribute("readonly"),i.placeholder=t.getAttribute("placeholder")||i.placeholder||"",i.required=t.hasAttribute("required"),this.isIE&&(i.autoComplete=!1),["whitelist","blacklist"].forEach((function(e){var s=t.getAttribute("data-"+e);s&&(s=s.split(i.delimiters))instanceof Array&&(i[e]=s)})),"autoComplete"in e&&!l(e.autoComplete)&&(i.autoComplete=u.autoComplete,i.autoComplete.enabled=e.autoComplete),"mix"==i.mode&&(i.autoComplete.rightKey=!0,i.delimiters=e.delimiters||null,i.tagTextProp&&!i.dropdown.searchKeys.includes(i.tagTextProp)&&i.dropdown.searchKeys.push(i.tagTextProp)),t.pattern)try{i.pattern=new RegExp(t.pattern)}catch(t){}if(this.settings.delimiters)try{i.delimiters=new RegExp(this.settings.delimiters,"g")}catch(t){}"select"==i.mode&&(i.dropdown.enabled=0),i.dropdown.appendTarget=e.dropdown&&e.dropdown.appendTarget?e.dropdown.appendTarget:document.body},getAttributes:function(t){if("[object Object]"!=Object.prototype.toString.call(t))return"";var e,i,s=Object.keys(t),a="";for(i=s.length;i--;)"class"!=(e=s[i])&&t.hasOwnProperty(e)&&void 0!==t[e]&&(a+=" "+e+(void 0!==t[e]?'="'.concat(t[e],'"'):""));return a},setStateSelection:function(){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},getCaretGlobalPosition:function(){var t=document.getSelection();if(t.rangeCount){var e,i,s=t.getRangeAt(0),a=s.startContainer,n=s.startOffset;if(n>0)return(i=document.createRange()).setStart(a,n-1),i.setEnd(a,n),{left:(e=i.getBoundingClientRect()).right,top:e.top,bottom:e.bottom};if(a.getBoundingClientRect)return a.getBoundingClientRect()}return{left:-9999,top:-9999}},getCSSVars:function(){var t,e=getComputedStyle(this.DOM.scope,null);this.CSSVars={tagHideTransition:function(t){var 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((function(t){return t})).pop().trim();return{value:+t.split(e).filter((function(t){return t}))[0].trim(),unit:e}}((t="tag-hide-transition",e.getPropertyValue("--"+t))))}},build:function(t){var e=this.DOM;this.settings.mixMode.integrated?(e.originalInput=null,e.scope=t,e.input=t):(e.originalInput=t,e.scope=this.parseTemplate("wrapper",[t,this.settings]),e.input=e.scope.querySelector("."+this.settings.classNames.input),t.parentNode.insertBefore(e.scope,t)),this.settings.dropdown.enabled>=0&&this.dropdown.init.call(this)},destroy:function(){this.DOM.scope.parentNode.removeChild(this.DOM.scope),this.dropdown.hide.call(this,!0),clearTimeout(this.dropdownHide__bindEventsTimeout)},loadOriginalValues:function(t){var e,i=this.settings;if(t=t||(i.mixMode.integrated?this.DOM.input.textContent:this.DOM.originalInput.value))if(this.removeAllTags(),"mix"==i.mode)this.parseMixTags(t.trim()),(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).forEach((function(t){return t&&t.classList.add(i.classNames.tagNoAnimation)}))}else this.postUpdate();this.state.lastOriginalValueReported=i.mixMode.integrated?"":this.DOM.originalInput.value,this.state.loadedOriginalValues=!0},cloneEvent:function(t){var e={};for(var i in t)e[i]=t[i];return e},loading:function(t){return this.state.isLoading=t,this.DOM.scope.classList[t?"add":"remove"](this.settings.classNames.scopeLoading),this},tagLoading:function(t,e){return t&&t.classList[e?"add":"remove"](this.settings.classNames.tagLoading),this},toggleClass:function(t,e){"string"==typeof t&&this.DOM.scope.classList.toggle(t,e)},toggleFocusClass:function(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:p,fixFirefoxLastTagNoCaret:function(){},placeCaretAfterNode:function(t){if(t&&t.parentNode){var e=t.nextSibling,i=window.getSelection(),s=i.getRangeAt(0);i.rangeCount&&(s.setStartBefore(e||t),s.setEndBefore(e||t),i.removeAllRanges(),i.addRange(s))}},insertAfterTag:function(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},editTag:function(t,e){var i=this;t=t||this.getLastTag(),e=e||{},this.dropdown.hide.call(this);var s=this.settings;function a(){return t.querySelector("."+s.classNames.tagText)}var n=a(),o=this.getNodeIndex(t),r=this.tagData(t),l=this.events.callbacks,c=this,h=!0;if(n){if(!(r instanceof Object&&"editable"in r)||r.editable)return n.setAttribute("contenteditable",!0),t.classList.add(s.classNames.tagEditing),this.tagData(t,{__originalData:d({},r),__originalHTML:t.innerHTML}),n.addEventListener("focus",l.onEditTagFocus.bind(this,t)),n.addEventListener("blur",(function(){setTimeout((function(){return l.onEditTagBlur.call(c,a())}))})),n.addEventListener("input",l.onEditTagInput.bind(this,n)),n.addEventListener("keydown",(function(e){return l.onEditTagkeydown.call(i,e,t)})),n.focus(),this.setRangeAtStartEnd(!1,n),e.skipValidation||(h=this.editTagToggleValidity(t,r.value)),n.originalIsValid=h,this.trigger("edit:start",{tag:t,index:o,data:r,isValid:h}),this}else console.warn("Cannot find element in Tag template: .",s.classNames.tagText)},editTagToggleValidity:function(t,e){var i,s=this.tagData(t);if(s)return i=!(!s.__isValid||1==s.__isValid),t.classList.toggle(this.settings.classNames.tagInvalid,i),s.__isValid;console.warn("tag has no data: ",t,s)},onEditTagDone:function(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),this.state.editing=!1,delete e.__originalData,delete e.__originalHTML,t&&e[this.settings.tagTextProp]?(this.editTagToggleValidity(t),this.replaceTag(t,e)):t&&this.removeTags(t),this.trigger("edit:updated",i),this.dropdown.hide.call(this),this.settings.keepInvalidTags&&this.reCheckInvalidTags()},replaceTag:function(t,e){e&&e.value||(e=t.__tagifyTagData),e.__isValid&&1!=e.__isValid&&d(e,this.getInvalidTagAttrs(e,e.__isValid));var i=this.createTagElem(e);t.parentNode.replaceChild(i,t),this.updateValueByDOMTags()},updateValueByDOMTags:function(){var t=this;this.value.length=0,[].forEach.call(this.getTagElms(),(function(e){e.classList.contains(t.settings.classNames.tagNotAllowed)||t.value.push(t.tagData(e))})),this.update()},setRangeAtStartEnd:function(t,e){t="number"==typeof t?t:!!t,e=(e=e||this.DOM.input).lastChild||e;var i=document.getSelection();try{i.rangeCount>=1&&["Start","End"].forEach((function(s){return i.getRangeAt(0)["set"+s](e,t||e.length)}))}catch(t){console.warn("Tagify: ",t)}},injectAtCaret:function(t,e){if(e=e||this.state.selection.range)return"string"==typeof t&&(t=document.createTextNode(t)),e.deleteContents(),e.insertNode(t),this.setRangeAtStartEnd(!1,t),this.updateValueByDOMTags(),this.update(),this},input:{set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=this.settings.dropdown.closeOnSelect;this.state.inputText=t,e&&(this.DOM.input.innerHTML=t),!t&&i&&this.dropdown.hide.bind(this),this.input.autocomplete.suggest.call(this),this.input.validate.call(this)},validate:function(){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:function(t){var e=t||this.DOM.input,i=[];e.childNodes.forEach((function(t){return 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.settings.trim&&(i=i.replace(/^\s+/,"")),i},autocomplete:{suggest:function(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:function(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()),this.input.autocomplete.suggest.call(this),this.dropdown.hide.call(this),!0)}}},getTagIdx:function(t){return this.value.findIndex((function(e){return e.value==t.value}))},getNodeIndex:function(t){var e=0;if(t)for(;t=t.previousElementSibling;)e++;return e},getTagElms:function(){for(var t=arguments.length,e=new Array(t),i=0;i=this.settings.maxTags&&this.TEXTS.exceed},setReadonly:function(t){var e=this.settings;document.activeElement.blur(),e.readonly=t,this.DOM.scope[(t?"set":"remove")+"Attribute"]("readonly",!0),"mix"==e.mode&&(this.DOM.input.contentEditable=!t)},normalizeTags:function(t){var s=this,a=this.settings,n=a.whitelist,o=a.delimiters,r=a.mode,l=a.tagTextProp,d=a.enforceWhitelist,c=[],h=!!n&&n[0]instanceof Object,g=t instanceof Array,u=function(t){return(t+"").split(o).filter((function(t){return t})).map((function(t){var i;return e(i={},l,s.trim(t)),e(i,"value",s.trim(t)),i}))};if("number"==typeof t&&(t=t.toString()),"string"==typeof t){if(!t.trim())return[];t=u(t)}else if(g){var p;t=(p=[]).concat.apply(p,i(t.map((function(t){return t.value?t:u(t)}))))}return h&&(t.forEach((function(t){var e=c.map((function(t){return t.value})),i=s.dropdown.filterListItems.call(s,t[l],{exact:!0}).filter((function(t){return!e.includes(t.value)})),a=i.length>1?s.getWhitelistItem(t[l],l,i):i[0];a&&a instanceof Object?c.push(a):"mix"==r||d||(null==t.value&&(t.value=t[l]),c.push(t))})),t=c),t},parseMixTags:function(t){var e=this,i=this.settings,s=i.mixTagsInterpolator,a=i.duplicates,n=i.transformTag,o=i.enforceWhitelist,r=i.maxTags,l=i.tagTextProp,d=[];return t=t.split(s[0]).map((function(t,i){var c,h,g,u=t.split(s[1]),p=u[0],f=d.length==r;try{if(p==+p)throw Error;h=JSON.parse(p)}catch(t){h=e.normalizeTags(p)[0]||{value:p}}if(f||!(u.length>1)||o&&!e.isTagWhitelisted(h.value)||!a&&e.isTagDuplicate(h.value)){if(t)return i?s[0]+t:t}else n.call(e,h),h[c=h[l]?l:"value"]=e.trim(h[c]),g=e.createTagElem(h),d.push(h),g.classList.add(e.settings.classNames.tagNoAnimation),u[0]=g.outerHTML,e.value.push(h);return u.join("")})).join(""),this.DOM.input.innerHTML=t,this.DOM.input.appendChild(document.createTextNode("")),this.DOM.input.normalize(),this.getTagElms().forEach((function(t,i){return e.tagData(t,d[i])})),this.update({withoutChangeEvent:!0}),t},replaceTextWithNode:function(t,e){if(this.state.tag||e){e=e||this.state.tag.prefix+this.state.tag.value;var i,s,a=window.getSelection(),n=a.anchorNode,o=this.state.tag.delimiters?this.state.tag.delimiters.length:0;return n.splitText(a.anchorOffset-o),i=n.nodeValue.lastIndexOf(e),s=n.splitText(i),t&&n.parentNode.replaceChild(t,s),!0}},selectTag:function(t,e){if(!this.settings.enforceWhitelist||this.isTagWhitelisted(e.value))return this.input.set.call(this,e.value,!0),this.state.actions.selectOption&&setTimeout(this.setRangeAtStartEnd.bind(this)),this.getLastTag()?this.replaceTag(this.getLastTag(),e):this.appendTag(t),this.value[0]=e,this.trigger("add",{tag:t,data:e}),this.update(),[t]},addEmptyTag:function(t){var e=d({value:""},t||{}),i=this.createTagElem(e);this.tagData(i,e),this.appendTag(i),this.editTag(i,{skipValidation:!0})},addTags:function(t,e){var i=this,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.settings.skipInvalid,a=[],n=this.settings;return t&&0!=t.length?(t=this.normalizeTags(t),"mix"==n.mode?this.addMixTags(t):("select"==n.mode&&(e=!1),this.DOM.input.removeAttribute("style"),t.forEach((function(t){var e,o={},r=Object.assign({},t,{value:t.value+""});if((t=Object.assign({},r)).__isValid=i.hasMaxTags()||i.validateTag(t),n.transformTag.call(i,t),!0!==t.__isValid){if(s)return;d(o,i.getInvalidTagAttrs(t,t.__isValid),{__preInvalidData:r}),t.__isValid==i.TEXTS.duplicate&&i.flashTag(i.getTagElmByValue(t.value))}if(t.readonly&&(o["aria-readonly"]=!0),e=i.createTagElem(d({},t,o)),a.push(e),"select"==n.mode)return i.selectTag(e,t);i.appendTag(e),t.__isValid&&!0===t.__isValid?(i.value.push(t),i.update(),i.trigger("add",{tag:e,index:i.value.length-1,data:t})):(i.trigger("invalid",{data:t,index:i.value.length,tag:e,message:t.__isValid}),n.keepInvalidTags||setTimeout((function(){return i.removeTags(e,!0)}),1e3)),i.dropdown.position.call(i)})),t.length&&e&&this.input.set.call(this),this.dropdown.refilter.call(this),a)):("select"==n.mode&&this.removeAllTags(),a)},addMixTags:function(t){var e=this;if(t[0].prefix||this.state.tag)this.prefixedTextToTag(t[0]);else{"string"==typeof t&&(t=[{value:t}]);var i=!!this.state.selection,s=document.createDocumentFragment();t.forEach((function(t){var i=e.createTagElem(t);s.appendChild(i),e.insertAfterTag(i)})),i?this.injectAtCaret(s):(this.DOM.input.focus(),(i=this.setStateSelection()).range.setStart(this.DOM.input,i.range.endOffset),i.range.setEnd(this.DOM.input,i.range.endOffset),this.DOM.input.appendChild(s),this.updateValueByDOMTags(),this.update())}},prefixedTextToTag:function(t){var e,i=this,s=this.settings,a=this.state.tag.delimiters;if(s.transformTag.call(this,t),t.prefix=t.prefix||this.state.tag?this.state.tag.prefix:(s.pattern.source||s.pattern)[0],e=this.createTagElem(t),this.replaceTextWithNode(e)||this.DOM.input.appendChild(e),setTimeout((function(){return e.classList.add(i.settings.classNames.tagNoAnimation)}),300),this.value.push(t),this.update(),!a){var n=this.insertAfterTag(e)||e;this.placeCaretAfterNode(n)}return this.state.tag=null,this.trigger("add",d({},{tag:e},{data:t})),e},appendTag:function(t){var e=this.DOM.scope.lastElementChild;e===this.DOM.input?this.DOM.scope.insertBefore(t,e):this.DOM.scope.appendChild(t)},createTagElem:function(t){var e,i=d({},t,{value:o(t.value+"")});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)}(e=this.parseTemplate("tag",[i])),this.tagData(e,t),e},reCheckInvalidTags:function(){var t=this,e=this.settings,i=".".concat(e.classNames.tag,".").concat(e.classNames.tagNotAllowed),s=this.DOM.scope.querySelectorAll(i);[].forEach.call(s,(function(e){var i=t.tagData(e),s=e.getAttribute("title")==t.TEXTS.duplicate,a=!0===t.validateTag(i);s&&a&&(i=i.__preInvalidData?i.__preInvalidData:{value:i.value},t.replaceTag(e,i))}))},removeTags:function(t,e,i){var s,a=this;t=t&&t instanceof HTMLElement?[t]:t instanceof Array?t:t?[t]:[this.getLastTag()],s=t.reduce((function(t,e){return e&&"string"==typeof e&&(e=a.getTagElmByValue(e)),e&&t.push({node:e,idx:a.getTagIdx(a.tagData(e)),data:a.tagData(e,{__removed:!0})}),t}),[]),i="number"==typeof i?i:this.CSSVars.tagHideTransition,"select"==this.settings.mode&&(i=0,this.input.set.call(this)),1==s.length&&s[0].node.classList.contains(this.settings.classNames.tagNotAllowed)&&(e=!0),s.length&&this.settings.hooks.beforeRemoveTag(s,{tagify:this}).then((function(){function t(t){t.node.parentNode&&(t.node.parentNode.removeChild(t.node),e?this.settings.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.call(this),this.dropdown.position.call(this),this.DOM.input.normalize(),this.settings.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(this.settings.classNames.tagHide),setTimeout(t.bind(this),i,e)}.call(a,s[0]):s.forEach(t.bind(a)),e||(s.forEach((function(t){var e=Object.assign({},t.data);delete e.__removed;var i=a.getTagIdx(e);i>-1&&a.value.splice(i,1)})),a.update())})).catch((function(t){}))},removeAllTags:function(){this.value=[],"mix"==this.settings.mode?this.DOM.input.innerHTML="":Array.prototype.slice.call(this.getTagElms()).forEach((function(t){return t.parentNode.removeChild(t)})),this.dropdown.position.call(this),"select"==this.settings.mode&&this.input.set.call(this),this.update()},postUpdate:function(){var t=this.settings.classNames,e="mix"==this.settings.mode?this.settings.mixMode.integrated?this.DOM.input.textContent:this.DOM.originalInput.value:this.value.length;this.toggleClass(t.hasMaxTags,this.value.length>=this.settings.maxTags),this.toggleClass(t.hasNoTags,!this.value.length),this.toggleClass(t.empty,!e)},update:function(t){var e,i,s=this.DOM.originalInput,a=(t||{}).withoutChangeEvent,n=(e=this.value,i=["__isValid","__removed"],e.map((function(t){var e={};for(var s in t)i.indexOf(s)<0&&(e[s]=t[s]);return e})));this.settings.mixMode.integrated||(s.value="mix"==this.settings.mode?this.getMixedTagsAsString(n):n.length?this.settings.originalInputValueFormat?this.settings.originalInputValueFormat(n):JSON.stringify(n):""),this.postUpdate(),!a&&this.state.loadedOriginalValues&&this.triggerChangeEvent()},getMixedTagsAsString:function(){var t="",e=this,i=this.settings.mixTagsInterpolator;return function s(a){a.childNodes.forEach((function(a){if(1==a.nodeType){if(a.classList.contains(e.settings.classNames.tag)&&e.tagData(a)){if(e.tagData(a).__removed)return;return void(t+=i[0]+JSON.stringify(a.__tagifyTagData)+i[1])}"BR"!=a.tagName||a.parentNode!=e.DOM.input&&1!=a.parentNode.childNodes.length?"DIV"!=a.tagName&&"P"!=a.tagName||(t+="\r\n",s(a)):t+="\r\n"}else t+=a.textContent}))}(this.DOM.input),t}},f.prototype.removeTag=f.prototype.removeTags,f})); +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).Tagify=e()}(this,(function(){"use strict";function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}function e(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function i(t){return function(t){if(Array.isArray(t)){for(var e=0,i=new Array(t.length);e/g,">").replace(/"/g,""").replace(/`|'/g,"'")}function r(t){return t instanceof Array}function l(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 d(t,e,i){function s(t,e){for(var i in e)if(e.hasOwnProperty(i)){if(l(e[i])){l(t[i])?s(t[i],e[i]):t[i]=Object.assign({},e[i]);continue}if(r(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 c(t){return String.prototype.normalize?"string"==typeof t?t.normalize("NFD").replace(/[\u0300-\u036f]/g,""):void 0:t}var h=/(?=.*chrome)(?=.*android)/i.test(navigator.userAgent),g={init:function(){this.DOM.dropdown=this.parseTemplate("dropdown",[this.settings]),this.DOM.dropdown.content=this.DOM.dropdown.querySelector(this.settings.classNames.dropdownWrapperSelector)},show:function(t){var e,i,a,n=this,o=this.settings,r=window.getSelection(),d="mix"==o.mode&&!o.enforceWhitelist,c=!o.whitelist||!o.whitelist.length,h="manual"==o.dropdown.position;if(t=void 0===t?this.state.inputText:t,(!c||d||o.templates.dropdownItemNoMatch)&&!1!==o.dropdown.enable&&!this.state.isLoading){if(clearTimeout(this.dropdownHide__bindEventsTimeout),this.suggestedListItems=this.dropdown.filterListItems.call(this,t),t&&!this.suggestedListItems.length&&(this.trigger("dropdown:noMatch",t),o.templates.dropdownItemNoMatch&&(a=o.templates.dropdownItemNoMatch.call(this,{value:t}))),!a){if(this.suggestedListItems.length)t&&d&&!this.state.editing.scope&&!s(this.suggestedListItems[0].value,t)&&this.suggestedListItems.unshift({value:t});else{if(!t||!d||this.state.editing.scope)return this.input.autocomplete.suggest.call(this),void this.dropdown.hide.call(this);this.suggestedListItems=[{value:t}]}i=""+(l(e=this.suggestedListItems[0])?e.value:e),o.autoComplete&&i&&0==i.indexOf(t)&&this.input.autocomplete.suggest.call(this,e)}this.dropdown.fill.call(this,a),o.dropdown.highlightFirst&&this.dropdown.highlightOption.call(this,this.DOM.dropdown.content.children[0]),this.state.dropdown.visible||setTimeout(this.dropdown.events.binding.bind(this)),this.state.dropdown.visible=t||!0,this.state.dropdown.query=t,this.state.selection={anchorOffset:r.anchorOffset,anchorNode:r.anchorNode},h||setTimeout((function(){n.dropdown.position.call(n),n.dropdown.render.call(n)})),setTimeout((function(){n.trigger("dropdown:show",n.DOM.dropdown)}))}},hide:function(t){var e=this,i=this.DOM,s=i.scope,a=i.dropdown,n="manual"==this.settings.dropdown.position&&!t;if(a&&document.body.contains(a)&&!n)return window.removeEventListener("resize",this.dropdown.position),this.dropdown.events.binding.call(this,!1),s.setAttribute("aria-expanded",!1),a.parentNode.removeChild(a),setTimeout((function(){e.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",a),this},render:function(){var t,e,i,s=this,a=(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),n=this.settings;return this.DOM.scope.setAttribute("aria-expanded",!0),document.body.contains(this.DOM.dropdown)||(this.DOM.dropdown.classList.add(n.classNames.dropdownInital),this.dropdown.position.call(this,a),n.dropdown.appendTarget.appendChild(this.DOM.dropdown),setTimeout((function(){return s.DOM.dropdown.classList.remove(n.classNames.dropdownInital)}))),this},fill:function(t){var e;t="string"==typeof t?t:this.dropdown.createListHTML.call(this,t||this.suggestedListItems),this.DOM.dropdown.content.innerHTML=(e=t)?e.replace(/\>[\r\n ]+\<").replace(/(<.*?>)|\s+/g,(function(t,e){return e||" "})):""},refilter:function(t){t=t||this.state.dropdown.query||"",this.suggestedListItems=this.dropdown.filterListItems.call(this,t),this.dropdown.fill.call(this),this.suggestedListItems.length||this.dropdown.hide.call(this),this.trigger("dropdown:updated",this.DOM.dropdown)},position:function(t){if("manual"!=this.settings.dropdown.position){var e,i,s,a,n,o,r,l=this.DOM.dropdown,d=document.documentElement.clientHeight,c=Math.max(document.documentElement.clientWidth||0,window.innerWidth||0)>480?this.settings.dropdown.position:"all",h=this.DOM["input"==c?"input":"scope"];t=t||l.clientHeight,this.state.dropdown.visible&&("text"==c?(a=(i=this.getCaretGlobalPosition()).bottom,s=i.top,n=i.left,o="auto"):(r=function(t){for(var e=0,i=0;t;)e+=t.offsetLeft||0,i+=t.offsetTop||0,t=t.parentNode;return{left:e,top:i}}(this.settings.dropdown.appendTarget),s=(i=h.getBoundingClientRect()).top+2-r.top,a=i.bottom-1-r.top,n=i.left-r.left,o=i.width+"px"),s=Math.floor(s),a=Math.ceil(a),e=d-i.bottom0&&void 0!==arguments[0])||arguments[0],e=this.dropdown.events.callbacks,i=this.listeners.dropdown=this.listeners.dropdown||{position:this.dropdown.position.bind(this),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&&(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:function(t){var e=this.DOM.dropdown.querySelector(this.settings.classNames.dropdownItemActiveSelector),i=e;switch(t.key){case"ArrowDown":case"ArrowUp":case"Down":case"Up":var s;t.preventDefault(),i&&(i=i[("ArrowUp"==t.key||"Up"==t.key?"previous":"next")+"ElementSibling"]),i||(i=(s=this.DOM.dropdown.content.children)["ArrowUp"==t.key||"Up"==t.key?s.length-1:0]),this.dropdown.highlightOption.call(this,i,!0);break;case"Escape":case"Esc":this.dropdown.hide.call(this);break;case"ArrowRight":if(this.state.actions.ArrowLeft)return;case"Tab":if("mix"!=this.settings.mode&&i&&!this.settings.autoComplete.rightKey&&!this.state.editing){t.preventDefault();var a=i.getAttribute("tagifySuggestionIdx"),n=a?this.suggestedListItems[+a]:"",o=this.dropdown.getMappedValue.call(this,n);return this.input.autocomplete.set.call(this,o),!1}return!0;case"Enter":t.preventDefault(),this.dropdown.selectOption.call(this,e);break;case"Backspace":if("mix"==this.settings.mode||this.state.editing.scope)return;var r=this.state.inputText.trim();""!=r&&8203!=r.charCodeAt(0)||(!0===this.settings.backspace?this.removeTags():"edit"==this.settings.backspace&&setTimeout(this.editTag.bind(this),0))}},onMouseOver:function(t){var e=t.target.closest(this.settings.classNames.dropdownItemSelector);e&&this.dropdown.highlightOption.call(this,e)},onMouseLeave:function(t){this.dropdown.highlightOption.call(this)},onClick:function(t){var e=this;if(0==t.button&&t.target!=this.DOM.dropdown&&t.target!=this.DOM.dropdown.content){var i=t.target.closest(this.settings.classNames.dropdownItemSelector);this.state.actions.selectOption=!0,setTimeout((function(){return e.state.actions.selectOption=!1}),50),this.settings.hooks.suggestionClick(t,{tagify:this,suggestionElm:i}).then((function(){i?e.dropdown.selectOption.call(e,i):e.dropdown.hide.call(e)})).catch((function(t){return t}))}},onScroll:function(t){var e=t.target,i=e.scrollTop/(e.scrollHeight-e.parentNode.clientHeight)*100;this.trigger("dropdown:scroll",{percentage:Math.round(i)})}}},highlightOption:function(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.suggestedListItems[this.getNodeIndex(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.call(this))},selectOption:function(t){var e=this,i=this.settings.dropdown,s=i.clearOnSelect,a=i.closeOnSelect;if(!t)return this.addTags(this.state.inputText,!0),void(a&&this.dropdown.hide.call(this));var n=t.getAttribute("tagifySuggestionIdx"),o=this.suggestedListItems[+n];if(this.trigger("dropdown:select",{data:o,elm:t}),n&&o){if(this.state.editing?this.onEditTagDone(null,d({__isValid:!0},o)):this["mix"==this.settings.mode?"addMixTags":"addTags"]([o],s),setTimeout((function(){e.DOM.input.focus(),e.toggleFocusClass(!0)})),a)return this.dropdown.hide.call(this);this.dropdown.refilter.call(this)}else this.dropdown.hide.call(this)},selectAll:function(){return this.suggestedListItems.length=0,this.dropdown.hide.call(this),this.addTags(this.dropdown.filterListItems.call(this,""),!0),this},filterListItems:function(t,e){var i,s,a,n,o,r=this,d=this.settings,h=d.dropdown,g=(e=e||{},[]),u=d.whitelist,p=h.maxItems||1/0,f=h.searchKeys,m=0;if(!t||!f.length)return(d.duplicates?u:u.filter((function(t){return!r.isTagDuplicate(l(t)?t.value:t)}))).slice(0,p);function v(t,e){return e.toLowerCase().split(" ").every((function(e){return t.includes(e.toLowerCase())}))}for(o=h.caseSensitive?""+t:(""+t).toLowerCase();m",' tagifySuggestionIdx="'.concat(i,'">'))})).join("")}},u={delimiters:",",pattern:null,tagTextProp:"value",maxTags:1/0,callbacks:{},addTagOnBlur:!0,duplicates:!1,whitelist:[],blacklist:[],enforceWhitelist:!1,keepInvalidTags:!1,mixTagsAllowedAfter:/,|\.|\:|\s/,mixTagsInterpolator:["[[","]]"],backspace:!0,skipInvalid:!1,editTags:{clicks:2,keepInvalid:!0},transformTag:function(){},trim:!0,mixMode:{insertAfterTag:" "},autoComplete:{enabled:!0,rightKey:!1},classNames:{namespace:"tagify",mixMode:"tagify--mix",selectMode:"tagify--select",input:"tagify__input",focus:"tagify--focus",tag:"tagify__tag",tagNoAnimation:"tagify--noAnim",tagInvalid:"tagify--invalid",tagNotAllowed:"tagify--notAllowed",inputInvalid:"tagify__input--invalid",tagX:"tagify__tag__removeBtn",tagText:"tagify__tag-text",dropdown:"tagify__dropdown",dropdownWrapper:"tagify__dropdown__wrapper",dropdownItem:"tagify__dropdown__item",dropdownItemActive:"tagify__dropdown__item--active",dropdownInital:"tagify__dropdown--initial",scopeLoading:"tagify--loading",tagLoading:"tagify__tag--loading",tagEditing:"tagify__tag--editable",tagFlash:"tagify__tag--flash",tagHide:"tagify__tag--hide",hasMaxTags:"tagify--hasMaxTags",hasNoTags:"tagify--noTags",empty:"tagify--empty"},dropdown:{classname:"",enabled:2,maxItems:10,searchKeys:["value","searchBy"],fuzzySearch:!0,caseSensitive:!1,accentedSearch:!0,highlightFirst:!1,closeOnSelect:!0,clearOnSelect:!0,position:"all",appendTarget:null},hooks:{beforeRemoveTag:function(){return Promise.resolve()},suggestionClick:function(){return Promise.resolve()}}};var p={customBinding:function(){var t=this;this.customEventsList.forEach((function(e){t.on(e,t.settings.callbacks[e])}))},binding:function(){var t,e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],i=this.events.callbacks,s=e?"addEventListener":"removeEventListener";if(!this.state.mainEvents||!e)for(var a in this.state.mainEvents=e,e&&!this.listeners.main&&(this.DOM.input.addEventListener(this.isIE?"keydown":"input",i[this.isIE?"onInputIE":"onInput"].bind(this)),this.settings.isJQueryPlugin&&jQuery(this.DOM.originalInput).on("tagify.removeAllTags",this.removeAllTags.bind(this))),t=this.listeners.main=this.listeners.main||{focus:["input",i.onFocusBlur.bind(this)],blur:["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)]})("blur"!=a||e)&&this.DOM[t[a][0]][s](a,t[a][1])},callbacks:{onFocusBlur:function(t){var e=t.target?this.trim(t.target.textContent):"",i=this.settings,s=t.type,a=i.dropdown.enabled>=0,n={relatedTarget:t.relatedTarget},o=this.state.actions.selectOption&&(a||!i.dropdown.closeOnSelect),r=this.state.actions.addNew&&a;if("blur"==s){if(t.relatedTarget===this.DOM.scope)return this.dropdown.hide.call(this),void this.DOM.input.focus();this.postUpdate(),this.triggerChangeEvent()}if(!o&&!r)if(this.state.hasFocus="focus"==s&&+new Date,this.toggleFocusClass(this.state.hasFocus),"mix"!=i.mode){if("focus"==s)return this.trigger("focus",n),void(0===i.dropdown.enabled&&this.dropdown.show.call(this));"blur"==s&&(this.trigger("blur",n),this.loading(!1),("select"==this.settings.mode?!this.value.length||this.value[0].value!=e:e&&!this.state.actions.selectOption&&i.addTagOnBlur)&&this.addTags(e,!0)),this.DOM.input.removeAttribute("style"),this.dropdown.hide.call(this)}else"focus"==s?this.trigger("focus",n):"blur"==t.type&&(this.trigger("blur",n),this.loading(!1),this.dropdown.hide.call(this),this.state.dropdown.visible=void 0,this.setStateSelection())},onKeydown:function(t){var e=this,i=this.trim(t.target.textContent);if(this.trigger("keydown",{originalEvent:this.cloneEvent(t)}),"mix"==this.settings.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,o,r=document.getSelection(),l="Delete"==t.key&&r.anchorOffset==(r.anchorNode.length||0),d=1==r.anchorNode.nodeType||!r.anchorOffset&&r.anchorNode.previousElementSibling,c=a(this.DOM.input.innerHTML),g=this.getTagElms();if(h&&d)return o=n(d),d.hasAttribute("readonly")||d.remove(),this.DOM.input.focus(),void setTimeout((function(){e.placeCaretAfterNode(o),e.DOM.input.click()}));if("BR"==r.anchorNode.nodeName)return;if((l||d)&&1==r.anchorNode.nodeType?s=0==r.anchorOffset?l?g[0]:null:g[r.anchorOffset-1]:l?s=r.anchorNode.nextElementSibling:d&&(s=d),3==r.anchorNode.nodeType&&!r.anchorNode.nodeValue&&r.anchorNode.previousElementSibling&&t.preventDefault(),(d||l)&&!this.settings.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&&s&&s.hasAttribute("readonly"))return void this.placeCaretAfterNode(n(s));this.isFirefox&&1==r.anchorNode.nodeType&&0!=r.anchorOffset&&(this.removeTags(),this.placeCaretAfterNode(this.setRangeAtStartEnd())),setTimeout((function(){var t=document.getSelection(),i=a(e.DOM.input.innerHTML),s=t.anchorNode.previousElementSibling;if(!h&&i.length>=c.length&&s&&!s.hasAttribute("readonly")&&(e.removeTags(s),e.fixFirefoxLastTagNoCaret(),2==e.DOM.input.children.length&&"BR"==e.DOM.input.children[1].tagName))return e.DOM.input.innerHTML="",e.value.length=0,!0;e.value=[].map.call(g,(function(t,i){var s=e.tagData(t);if(t.parentNode||s.readonly)return s;e.trigger("remove",{tag:t,index:i,data:s})})).filter((function(t){return t}))}),50)}return!0}switch(t.key){case"Backspace":this.state.dropdown.visible&&"manual"!=this.settings.dropdown.position||""!=i&&8203!=i.charCodeAt(0)||(!0===this.settings.backspace?this.removeTags():"edit"==this.settings.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.call(this);break;case"ArrowRight":var u=this.state.inputSuggestion||this.state.ddItemData;if(u&&this.settings.autoComplete.rightKey)return void this.addTags([u],!0);break;case"Tab":var p="select"==this.settings.mode;if(!i||p)return!0;t.preventDefault();case"Enter":if(this.state.dropdown.visible||229==t.keyCode)return;t.preventDefault(),setTimeout((function(){e.state.actions.selectOption||e.addTags(i,!0)}))}},onInput:function(t){if("mix"==this.settings.mode)return this.events.callbacks.onMixTagsInput.call(this,t);var e=this.input.normalize.call(this),i=e.length>=this.settings.dropdown.enabled,s={value:e,inputElm:this.DOM.input};s.isValid=this.validateTag({value:e}),this.trigger("input",s),this.state.inputText!=e&&(this.input.set.call(this,e,!1),-1!=e.search(this.settings.delimiters)?this.addTags(e)&&this.input.set.call(this):this.settings.dropdown.enabled>=0&&this.dropdown[i?"show":"hide"].call(this,e))},onMixTagsInput:function(t){var e,i,s,a,n,o,r,l,c=this,g=this.settings,u=this.value.length,p=this.getTagElms(),f=document.createDocumentFragment(),m=window.getSelection().getRangeAt(0),v=[].map.call(p,(function(t){return c.tagData(t).value}));if("deleteContentBackward"==t.inputType&&h&&this.events.callbacks.onKeydown.call(this,{target:t.target,key:"Backspace"}),this.value.slice().forEach((function(t){t.readonly&&!v.includes(t.value)&&f.appendChild(c.createTagElem(t))})),f.childNodes.length&&(m.insertNode(f),this.setRangeAtStartEnd(!1,f.lastChild)),p.length!=u)return this.value=[].map.call(this.getTagElms(),(function(t){return c.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(g.pattern).length-1,(i=e.match(g.pattern))&&(a=e.slice(e.lastIndexOf(i[i.length-1]))),a){if(this.state.actions.ArrowLeft=!1,this.state.tag={prefix:a.match(g.pattern)[0],value:a.replace(g.pattern,"")},this.state.tag.baseOffset=o.baseOffset-this.state.tag.value.length,l=this.state.tag.value.match(g.delimiters))return this.state.tag.value=this.state.tag.value.replace(g.delimiters,""),this.state.tag.delimiters=l[0],this.addTags(this.state.tag.value,g.dropdown.clearOnSelect),void this.dropdown.hide.call(this);n=this.state.tag.value.length>=g.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||s500)?this.state.dropdown.visible?this.dropdown.hide.call(this):0===e.dropdown.enabled&&"mix"!=e.mode&&this.dropdown.show.call(this):"select"==e.mode&&!this.state.dropdown.visible&&this.dropdown.show.call(this));this.removeTags(t.target.parentNode)}else this.state.hasFocus||this.DOM.input.focus()},onPaste:function(t){var e;t.preventDefault(),this.settings.readonly||(e=(t.clipboardData||window.clipboardData).getData("Text"),this.injectAtCaret(e,window.getSelection().getRangeAt(0)),"mix"!=this.settings.mode&&this.addTags(this.DOM.input.textContent,!0))},onEditTagInput:function(t,i){var s=t.closest("."+this.settings.classNames.tag),a=this.getNodeIndex(s),n=this.tagData(s),o=this.input.normalize.call(this,t),r=s.innerHTML!=s.__tagifyTagData.__originalHTML,l=this.validateTag(e({},this.settings.tagTextProp,o));r||!0!==t.originalIsValid||(l=!0),s.classList.toggle(this.settings.classNames.tagInvalid,!0!==l),n.__isValid=l,s.title=!0===l?n.title||n.value:l,o.length>=this.settings.dropdown.enabled&&(this.state.editing&&(this.state.editing.value=o),this.dropdown.show.call(this,o)),this.trigger("edit:input",{tag:s,index:a,data:d({},this.value[a],{newValue:o}),originalEvent:this.cloneEvent(i)})},onEditTagFocus:function(t){this.state.editing={scope:t,input:t.querySelector("[contenteditable]")}},onEditTagBlur:function(t){var i;if(this.state.hasFocus||this.toggleFocusClass(),this.DOM.scope.contains(t)){var s,a=this.settings,n=t.closest("."+a.classNames.tag),o=this.input.normalize.call(this,t),r=this.tagData(n).__originalData,l=n.innerHTML!=n.__tagifyTagData.__originalHTML,d=this.validateTag(e({},a.tagTextProp,o));if(o)if(l){if(s=this.getWhitelistItem(o)||(e(i={},a.tagTextProp,o),e(i,"value",o),i),a.transformTag.call(this,s,r),!0!==(d=this.validateTag(e({},a.tagTextProp,s[a.tagTextProp])))){if(this.trigger("invalid",{data:s,tag:n,message:d}),a.editTags.keepInvalid)return;a.keepInvalidTags?s.__isValid=d:s=r}this.onEditTagDone(n,s)}else this.onEditTagDone(n,r);else this.onEditTagDone(n)}},onEditTagkeydown:function(t,e){switch(this.trigger("edit:keydown",{originalEvent:this.cloneEvent(t)}),t.key){case"Esc":case"Escape":e.innerHTML=e.__tagifyTagData.__originalHTML;case"Enter":case"Tab":t.preventDefault(),t.target.blur()}},onDoubleClickScope:function(t){var e,i,s=t.target.closest("."+this.settings.classNames.tag),a=this.settings;s&&(e=s.classList.contains(this.settings.classNames.tagEditing),i=s.hasAttribute("readonly"),"select"==a.mode||a.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)}))}}};function f(e,i){return e?e.previousElementSibling&&e.previousElementSibling.classList.contains("tagify")?(console.warn("Tagify: ","input element is already Tagified",e),this):(d(this,function(e){var i=document.createTextNode("");function s(t,e,s){s&&e.split(/\s+/g).forEach((function(e){return i[t+"EventListener"].call(i,e,s)}))}return{off:function(t,e){return s("remove",t,e),this},on:function(t,e){return e&&"function"==typeof e&&s("add",t,e),this},trigger:function(s,a){var n;if(s)if(e.settings.isJQueryPlugin)"remove"==s&&(s="removeTag"),jQuery(e.DOM.originalInput).triggerHandler(s,[a]);else{try{var o=d({},"object"===t(a)?a:{value:a});if(o.tagify=this,a instanceof Object)for(var r in a)a[r]instanceof HTMLElement&&(o[r]=a[r]);n=new CustomEvent(s,{detail:o})}catch(t){console.warn(t)}i.dispatchEvent(n)}}}}(this)),this.isFirefox="undefined"!=typeof InstallTrigger,this.isIE=window.document.documentMode,this.applySettings(e,i||{}),this.state={inputText:"",editing:!1,actions:{},mixMode:{},dropdown:{},flaggedTags:{}},this.value=[],this.listeners={},this.DOM={},this.build(e),this.getCSSVars(),this.loadOriginalValues(),this.events.customBinding.call(this),this.events.binding.call(this),void(e.autofocus&&this.DOM.input.focus())):(console.warn("Tagify: ","input element not found",e),this)}return f.prototype={dropdown:g,TEXTS:{empty:"empty",exceed:"number of tags exceeded",pattern:"pattern mismatch",duplicate:"already exists",notAllowed:"not allowed"},customEventsList:["change","add","remove","invalid","input","click","keydown","focus","blur","edit:input","edit:updated","edit:start","edit:keydown","dropdown:show","dropdown:hide","dropdown:select","dropdown:updated","dropdown:noMatch"],trim:function(t){return this.settings.trim&&t&&"string"==typeof t?t.trim():t},parseHTML:function(t){return(new DOMParser).parseFromString(t.trim(),"text/html").body.firstElementChild},templates:{wrapper:function(t,e){return'\n \n ')},tag:function(t){return'\n \n
\n ').concat(t[this.settings.tagTextProp]||t.value,"\n
\n
")},dropdown:function(t){var e=t.dropdown,i="manual"==e.position,s="".concat(t.classNames.dropdown);return'
\n
\n
')},dropdownItem:function(t){return"
').concat(t.value,"
")},dropdownItemNoMatch:null},parseTemplate:function(t,e){return t=this.settings.templates[t]||t,this.parseHTML(t.apply(this,e))},applySettings:function(t,e){u.templates=this.templates;var i=this.settings=d({},u,e);i.readonly=t.hasAttribute("readonly"),i.placeholder=t.getAttribute("placeholder")||i.placeholder||"",i.required=t.hasAttribute("required");var s=function(t){Object.defineProperty(i.classNames,t+"Selector",{get:function(){return"."+this[t].split(" ").join(".")}})};for(var a in i.classNames)s(a);if(this.isIE&&(i.autoComplete=!1),["whitelist","blacklist"].forEach((function(e){var s=t.getAttribute("data-"+e);s&&(s=s.split(i.delimiters))instanceof Array&&(i[e]=s)})),"autoComplete"in e&&!l(e.autoComplete)&&(i.autoComplete=u.autoComplete,i.autoComplete.enabled=e.autoComplete),"mix"==i.mode&&(i.autoComplete.rightKey=!0,i.delimiters=e.delimiters||null,i.tagTextProp&&!i.dropdown.searchKeys.includes(i.tagTextProp)&&i.dropdown.searchKeys.push(i.tagTextProp)),t.pattern)try{i.pattern=new RegExp(t.pattern)}catch(t){}if(this.settings.delimiters)try{i.delimiters=new RegExp(this.settings.delimiters,"g")}catch(t){}"select"==i.mode&&(i.dropdown.enabled=0),i.dropdown.appendTarget=e.dropdown&&e.dropdown.appendTarget?e.dropdown.appendTarget:document.body},getAttributes:function(t){if("[object Object]"!=Object.prototype.toString.call(t))return"";var e,i,s=Object.keys(t),a="";for(i=s.length;i--;)"class"!=(e=s[i])&&t.hasOwnProperty(e)&&void 0!==t[e]&&(a+=" "+e+(void 0!==t[e]?'="'.concat(t[e],'"'):""));return a},setStateSelection:function(){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},getCaretGlobalPosition:function(){var t=document.getSelection();if(t.rangeCount){var e,i,s=t.getRangeAt(0),a=s.startContainer,n=s.startOffset;if(n>0)return(i=document.createRange()).setStart(a,n-1),i.setEnd(a,n),{left:(e=i.getBoundingClientRect()).right,top:e.top,bottom:e.bottom};if(a.getBoundingClientRect)return a.getBoundingClientRect()}return{left:-9999,top:-9999}},getCSSVars:function(){var t,e=getComputedStyle(this.DOM.scope,null);this.CSSVars={tagHideTransition:function(t){var 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((function(t){return t})).pop().trim();return{value:+t.split(e).filter((function(t){return t}))[0].trim(),unit:e}}((t="tag-hide-transition",e.getPropertyValue("--"+t))))}},build:function(t){var e=this.DOM;this.settings.mixMode.integrated?(e.originalInput=null,e.scope=t,e.input=t):(e.originalInput=t,e.scope=this.parseTemplate("wrapper",[t,this.settings]),e.input=e.scope.querySelector(this.settings.classNames.inputSelector),t.parentNode.insertBefore(e.scope,t)),this.settings.dropdown.enabled>=0&&this.dropdown.init.call(this)},destroy:function(){this.DOM.scope.parentNode.removeChild(this.DOM.scope),this.dropdown.hide.call(this,!0),clearTimeout(this.dropdownHide__bindEventsTimeout)},loadOriginalValues:function(t){var e,i=this.settings;if(t=t||(i.mixMode.integrated?this.DOM.input.textContent:this.DOM.originalInput.value))if(this.removeAllTags({withoutChangeEvent:!0}),"mix"==i.mode)this.parseMixTags(t.trim()),(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).forEach((function(t){return t&&t.classList.add(i.classNames.tagNoAnimation)}))}else this.postUpdate();this.state.lastOriginalValueReported=i.mixMode.integrated?"":this.DOM.originalInput.value,this.state.loadedOriginalValues=!0},cloneEvent:function(t){var e={};for(var i in t)e[i]=t[i];return e},loading:function(t){return this.state.isLoading=t,this.DOM.scope.classList[t?"add":"remove"](this.settings.classNames.scopeLoading),this},tagLoading:function(t,e){return t&&t.classList[e?"add":"remove"](this.settings.classNames.tagLoading),this},toggleClass:function(t,e){"string"==typeof t&&this.DOM.scope.classList.toggle(t,e)},toggleFocusClass:function(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:p,fixFirefoxLastTagNoCaret:function(){},placeCaretAfterNode:function(t){if(t&&t.parentNode){var e=t.nextSibling,i=window.getSelection(),s=i.getRangeAt(0);i.rangeCount&&(s.setStartBefore(e||t),s.setEndBefore(e||t),i.removeAllRanges(),i.addRange(s))}},insertAfterTag:function(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},editTag:function(t,e){var i=this;t=t||this.getLastTag(),e=e||{},this.dropdown.hide.call(this);var s=this.settings;function a(){return t.querySelector(s.classNames.tagTextSelector)}var n=a(),o=this.getNodeIndex(t),r=this.tagData(t),l=this.events.callbacks,c=this,h=!0;if(n){if(!(r instanceof Object&&"editable"in r)||r.editable)return n.setAttribute("contenteditable",!0),t.classList.add(s.classNames.tagEditing),this.tagData(t,{__originalData:d({},r),__originalHTML:t.innerHTML}),n.addEventListener("focus",l.onEditTagFocus.bind(this,t)),n.addEventListener("blur",(function(){setTimeout((function(){return l.onEditTagBlur.call(c,a())}))})),n.addEventListener("input",l.onEditTagInput.bind(this,n)),n.addEventListener("keydown",(function(e){return l.onEditTagkeydown.call(i,e,t)})),n.focus(),this.setRangeAtStartEnd(!1,n),e.skipValidation||(h=this.editTagToggleValidity(t,r.value)),n.originalIsValid=h,this.trigger("edit:start",{tag:t,index:o,data:r,isValid:h}),this}else console.warn("Cannot find element in Tag template: .",s.classNames.tagTextSelector)},editTagToggleValidity:function(t,e){var i,s=this.tagData(t);if(s)return i=!(!s.__isValid||1==s.__isValid),t.classList.toggle(this.settings.classNames.tagInvalid,i),s.__isValid;console.warn("tag has no data: ",t,s)},onEditTagDone:function(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),this.state.editing=!1,delete e.__originalData,delete e.__originalHTML,t&&e[this.settings.tagTextProp]?(this.editTagToggleValidity(t),this.replaceTag(t,e)):t&&this.removeTags(t),this.trigger("edit:updated",i),this.dropdown.hide.call(this),this.settings.keepInvalidTags&&this.reCheckInvalidTags()},replaceTag:function(t,e){e&&e.value||(e=t.__tagifyTagData),e.__isValid&&1!=e.__isValid&&d(e,this.getInvalidTagAttrs(e,e.__isValid));var i=this.createTagElem(e);t.parentNode.replaceChild(i,t),this.updateValueByDOMTags()},updateValueByDOMTags:function(){var t=this;this.value.length=0,[].forEach.call(this.getTagElms(),(function(e){e.classList.contains(t.settings.classNames.tagNotAllowed.split(" ")[0])||t.value.push(t.tagData(e))})),this.update()},setRangeAtStartEnd:function(t,e){t="number"==typeof t?t:!!t,e=(e=e||this.DOM.input).lastChild||e;var i=document.getSelection();try{i.rangeCount>=1&&["Start","End"].forEach((function(s){return i.getRangeAt(0)["set"+s](e,t||e.length)}))}catch(t){console.warn("Tagify: ",t)}},injectAtCaret:function(t,e){if(e=e||this.state.selection.range)return"string"==typeof t&&(t=document.createTextNode(t)),e.deleteContents(),e.insertNode(t),this.setRangeAtStartEnd(!1,t),this.updateValueByDOMTags(),this.update(),this},input:{set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=this.settings.dropdown.closeOnSelect;this.state.inputText=t,e&&(this.DOM.input.innerHTML=o(""+t)),!t&&i&&this.dropdown.hide.bind(this),this.input.autocomplete.suggest.call(this),this.input.validate.call(this)},validate:function(){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:function(t){var e=t||this.DOM.input,i=[];e.childNodes.forEach((function(t){return 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.settings.trim&&(i=i.replace(/^\s+/,"")),i},autocomplete:{suggest:function(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:function(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()),this.input.autocomplete.suggest.call(this),this.dropdown.hide.call(this),!0)}}},getTagIdx:function(t){return this.value.findIndex((function(e){return e.value==t.value}))},getNodeIndex:function(t){var e=0;if(t)for(;t=t.previousElementSibling;)e++;return e},getTagElms:function(){for(var t=arguments.length,e=new Array(t),s=0;s=this.settings.maxTags&&this.TEXTS.exceed},setReadonly:function(t){var e=this.settings;document.activeElement.blur(),e.readonly=t,this.DOM.scope[(t?"set":"remove")+"Attribute"]("readonly",!0),"mix"==e.mode&&(this.DOM.input.contentEditable=!t)},normalizeTags:function(t){var s=this,a=this.settings,n=a.whitelist,o=a.delimiters,r=a.mode,l=a.tagTextProp,d=a.enforceWhitelist,c=[],h=!!n&&n[0]instanceof Object,g=t instanceof Array,u=function(t){return(t+"").split(o).filter((function(t){return t})).map((function(t){var i;return e(i={},l,s.trim(t)),e(i,"value",s.trim(t)),i}))};if("number"==typeof t&&(t=t.toString()),"string"==typeof t){if(!t.trim())return[];t=u(t)}else if(g){var p;t=(p=[]).concat.apply(p,i(t.map((function(t){return t.value?t:u(t)}))))}return h&&(t.forEach((function(t){var e=c.map((function(t){return t.value})),i=s.dropdown.filterListItems.call(s,t[l],{exact:!0}).filter((function(t){return!e.includes(t.value)})),a=i.length>1?s.getWhitelistItem(t[l],l,i):i[0];a&&a instanceof Object?c.push(a):"mix"==r||d||(null==t.value&&(t.value=t[l]),c.push(t))})),t=c),t},parseMixTags:function(t){var e=this,i=this.settings,s=i.mixTagsInterpolator,a=i.duplicates,n=i.transformTag,o=i.enforceWhitelist,r=i.maxTags,l=i.tagTextProp,d=[];return t=t.split(s[0]).map((function(t,i){var c,h,g,u=t.split(s[1]),p=u[0],f=d.length==r;try{if(p==+p)throw Error;h=JSON.parse(p)}catch(t){h=e.normalizeTags(p)[0]||{value:p}}if(f||!(u.length>1)||o&&!e.isTagWhitelisted(h.value)||!a&&e.isTagDuplicate(h.value)){if(t)return i?s[0]+t:t}else n.call(e,h),h[c=h[l]?l:"value"]=e.trim(h[c]),g=e.createTagElem(h),d.push(h),g.classList.add(e.settings.classNames.tagNoAnimation),u[0]=g.outerHTML,e.value.push(h);return u.join("")})).join(""),this.DOM.input.innerHTML=t,this.DOM.input.appendChild(document.createTextNode("")),this.DOM.input.normalize(),this.getTagElms().forEach((function(t,i){return e.tagData(t,d[i])})),this.update({withoutChangeEvent:!0}),t},replaceTextWithNode:function(t,e){if(this.state.tag||e){e=e||this.state.tag.prefix+this.state.tag.value;var i,s,a=window.getSelection(),n=a.anchorNode,o=this.state.tag.delimiters?this.state.tag.delimiters.length:0;return n.splitText(a.anchorOffset-o),i=n.nodeValue.lastIndexOf(e),s=n.splitText(i),t&&n.parentNode.replaceChild(t,s),!0}},selectTag:function(t,e){if(!this.settings.enforceWhitelist||this.isTagWhitelisted(e.value))return this.input.set.call(this,e.value,!0),this.state.actions.selectOption&&setTimeout(this.setRangeAtStartEnd.bind(this)),this.getLastTag()?this.replaceTag(this.getLastTag(),e):this.appendTag(t),this.value[0]=e,this.trigger("add",{tag:t,data:e}),this.update(),[t]},addEmptyTag:function(t){var e=d({value:""},t||{}),i=this.createTagElem(e);this.tagData(i,e),this.appendTag(i),this.editTag(i,{skipValidation:!0})},addTags:function(t,e){var i=this,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.settings.skipInvalid,a=[],n=this.settings;return t&&0!=t.length?(t=this.normalizeTags(t),"mix"==n.mode?this.addMixTags(t):("select"==n.mode&&(e=!1),this.DOM.input.removeAttribute("style"),t.forEach((function(t){var e,o={},r=Object.assign({},t,{value:t.value+""});if((t=Object.assign({},r)).__isValid=i.hasMaxTags()||i.validateTag(t),n.transformTag.call(i,t),!0!==t.__isValid){if(s)return;d(o,i.getInvalidTagAttrs(t,t.__isValid),{__preInvalidData:r}),t.__isValid==i.TEXTS.duplicate&&i.flashTag(i.getTagElmByValue(t.value))}if(t.readonly&&(o["aria-readonly"]=!0),e=i.createTagElem(d({},t,o)),a.push(e),"select"==n.mode)return i.selectTag(e,t);i.appendTag(e),t.__isValid&&!0===t.__isValid?(i.value.push(t),i.update(),i.trigger("add",{tag:e,index:i.value.length-1,data:t})):(i.trigger("invalid",{data:t,index:i.value.length,tag:e,message:t.__isValid}),n.keepInvalidTags||setTimeout((function(){return i.removeTags(e,!0)}),1e3)),i.dropdown.position.call(i)})),t.length&&e&&this.input.set.call(this),this.dropdown.refilter.call(this),a)):("select"==n.mode&&this.removeAllTags(),a)},addMixTags:function(t){var e=this;if(t[0].prefix||this.state.tag)this.prefixedTextToTag(t[0]);else{"string"==typeof t&&(t=[{value:t}]);var i=!!this.state.selection,s=document.createDocumentFragment();t.forEach((function(t){var i=e.createTagElem(t);s.appendChild(i),e.insertAfterTag(i)})),i?this.injectAtCaret(s):(this.DOM.input.focus(),(i=this.setStateSelection()).range.setStart(this.DOM.input,i.range.endOffset),i.range.setEnd(this.DOM.input,i.range.endOffset),this.DOM.input.appendChild(s),this.updateValueByDOMTags(),this.update())}},prefixedTextToTag:function(t){var e,i=this,s=this.settings,a=this.state.tag.delimiters;if(s.transformTag.call(this,t),t.prefix=t.prefix||this.state.tag?this.state.tag.prefix:(s.pattern.source||s.pattern)[0],e=this.createTagElem(t),this.replaceTextWithNode(e)||this.DOM.input.appendChild(e),setTimeout((function(){return e.classList.add(i.settings.classNames.tagNoAnimation)}),300),this.value.push(t),this.update(),!a){var n=this.insertAfterTag(e)||e;this.placeCaretAfterNode(n)}return this.state.tag=null,this.trigger("add",d({},{tag:e},{data:t})),e},appendTag:function(t){var e=this.DOM.scope.lastElementChild;e===this.DOM.input?this.DOM.scope.insertBefore(t,e):this.DOM.scope.appendChild(t)},createTagElem:function(t){var e,i=d({},t,{value:o(t.value+"")});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)}(e=this.parseTemplate("tag",[i])),this.tagData(e,t),e},reCheckInvalidTags:function(){var t=this,e=this.settings,i="".concat(e.classNames.tagSelector).concat(e.classNames.tagNotAllowedSelector),s=this.DOM.scope.querySelectorAll(i);[].forEach.call(s,(function(e){var i=t.tagData(e),s=e.getAttribute("title")==t.TEXTS.duplicate,a=!0===t.validateTag(i);s&&a&&(i=i.__preInvalidData?i.__preInvalidData:{value:i.value},t.replaceTag(e,i))}))},removeTags:function(t,e,i){var s,a=this;t=t&&t instanceof HTMLElement?[t]:t instanceof Array?t:t?[t]:[this.getLastTag()],s=t.reduce((function(t,e){return e&&"string"==typeof e&&(e=a.getTagElmByValue(e)),e&&t.push({node:e,idx:a.getTagIdx(a.tagData(e)),data:a.tagData(e,{__removed:!0})}),t}),[]),i="number"==typeof i?i:this.CSSVars.tagHideTransition,"select"==this.settings.mode&&(i=0,this.input.set.call(this)),1==s.length&&s[0].node.classList.contains(this.settings.classNames.tagNotAllowed)&&(e=!0),s.length&&this.settings.hooks.beforeRemoveTag(s,{tagify:this}).then((function(){function t(t){t.node.parentNode&&(t.node.parentNode.removeChild(t.node),e?this.settings.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.call(this),this.dropdown.position.call(this),this.DOM.input.normalize(),this.settings.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(this.settings.classNames.tagHide),setTimeout(t.bind(this),i,e)}.call(a,s[0]):s.forEach(t.bind(a)),e||(s.forEach((function(t){var e=Object.assign({},t.data);delete e.__removed;var i=a.getTagIdx(e);i>-1&&a.value.splice(i,1)})),a.update())})).catch((function(t){}))},removeAllTags:function(t){var e=t.withoutChangeEvent;this.value=[],"mix"==this.settings.mode?this.DOM.input.innerHTML="":Array.prototype.slice.call(this.getTagElms()).forEach((function(t){return t.parentNode.removeChild(t)})),this.dropdown.position.call(this),"select"==this.settings.mode&&this.input.set.call(this),this.update({withoutChangeEvent:e})},postUpdate:function(){var t=this.settings.classNames,e="mix"==this.settings.mode?this.settings.mixMode.integrated?this.DOM.input.textContent:this.DOM.originalInput.value:this.value.length;this.toggleClass(t.hasMaxTags,this.value.length>=this.settings.maxTags),this.toggleClass(t.hasNoTags,!this.value.length),this.toggleClass(t.empty,!e)},update:function(t){var e,i,s=this.DOM.originalInput,a=(t||{}).withoutChangeEvent,n=(e=this.value,i=["__isValid","__removed"],e.map((function(t){var e={};for(var s in t)i.indexOf(s)<0&&(e[s]=t[s]);return e})));this.settings.mixMode.integrated||(s.value="mix"==this.settings.mode?this.getMixedTagsAsString(n):n.length?this.settings.originalInputValueFormat?this.settings.originalInputValueFormat(n):JSON.stringify(n):""),this.postUpdate(),!a&&this.state.loadedOriginalValues&&this.triggerChangeEvent()},getMixedTagsAsString:function(){var t="",e=this,i=this.settings.mixTagsInterpolator;return function s(a){a.childNodes.forEach((function(a){if(1==a.nodeType){if(a.classList.contains(e.settings.classNames.tag)&&e.tagData(a)){if(e.tagData(a).__removed)return;return void(t+=i[0]+JSON.stringify(a.__tagifyTagData)+i[1])}"BR"!=a.tagName||a.parentNode!=e.DOM.input&&1!=a.parentNode.childNodes.length?"DIV"!=a.tagName&&"P"!=a.tagName||(t+="\r\n",s(a)):t+="\r\n"}else t+=a.textContent}))}(this.DOM.input),t}},f.prototype.removeTag=f.prototype.removeTags,f})); })(jQuery); \ No newline at end of file diff --git a/dist/react.tagify.js b/dist/react.tagify.js index 2f4cbaa5..30d71260 100644 --- a/dist/react.tagify.js +++ b/dist/react.tagify.js @@ -1,260 +1 @@ -;(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"; - -function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } - -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 _tagifyMin = _interopRequireDefault(require("./tagify.min.js")); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (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 || 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; } - -function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } - -function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); } - -function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); } - -function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } } - -var noop = function noop(_) { - return _; -}; // 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 (var templateName in templates) { - var isReactComp = String(templates[templateName]).includes(".createElement"); - - if (isReactComp) { - (function () { - var Template = templates[templateName]; - - templates[templateName] = function (data) { - return (0, _server.renderToStaticMarkup)( - /*#__PURE__*/ - _react.default.createElement(Template, data)); - }; - })(); - } - } - } -} - -var TagifyWrapper = function TagifyWrapper(_ref) { - var name = _ref.name, - _ref$value = _ref.value, - value = _ref$value === void 0 ? "" : _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$onEdit = _ref.onEdit, - onEdit = _ref$onEdit === void 0 ? noop : _ref$onEdit, - _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, - readOnly = _ref.readOnly, - 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; - var mountedRef = (0, _react.useRef)(); - var inputElmRef = (0, _react.useRef)(); - var tagify = (0, _react.useRef)(); - - var handleRef = function handleRef(elm) { - inputElmRef.current = elm; - }; - - var inputAttrs = (0, _react.useMemo)(function () { - return { - ref: handleRef, - name: name, - value: children ? children : typeof value === "string" ? value : JSON.stringify(value), - className: className, - readOnly: readOnly, - onChange: onChange, - autoFocus: autoFocus, - placeholder: placeholder, - defaultValue: defaultValue - }; - }, []); - var setFocus = (0, _react.useCallback)(function () { - autoFocus && tagify.current && tagify.current.DOM.input.focus(); - }, [tagify]); - (0, _react.useEffect)(function () { - templatesToString(settings.templates); - if (InputMode == "textarea") settings.mode = "mix"; // "whitelist" prop takes precedence - - if (whitelist && whitelist.length) settings.whitelist = whitelist; - var t = new _tagifyMin.default(inputElmRef.current, settings); - onInput && t.on("input", onInput); - onAdd && t.on("add", onAdd); - onRemove && t.on("remove", onRemove); - onEdit && t.on("edit", onEdit); - onInvalid && t.on("invalid", onInvalid); - onKeydown && t.on("keydown", onKeydown); - onFocus && t.on("focus", onFocus); - onBlur && t.on("blur", onBlur); - onClick && t.on("click", onClick); // Bridge Tagify instance with parent component - - if (tagifyRef) { - tagifyRef.current = t; - } - - tagify.current = t; - setFocus(); // cleanup - - return function () { - t.destroy(); - }; - }, []); - (0, _react.useEffect)(function () { - setFocus(); - }, [autoFocus]); - (0, _react.useEffect)(function () { - if (mountedRef.current) { - var _tagify$current$setti; - - tagify.current.settings.whitelist.length = 0; // replace whitelist array items - - whitelist && whitelist.length && (_tagify$current$setti = tagify.current.settings.whitelist).push.apply(_tagify$current$setti, _toConsumableArray(whitelist)); - } - }, [whitelist]); - (0, _react.useEffect)(function () { - if (mountedRef.current) { - tagify.current.loadOriginalValues(value); - } - }, [value]); - (0, _react.useEffect)(function () { - if (mountedRef.current) { - tagify.current.toggleClass(className); - } - }, [className]); - (0, _react.useEffect)(function () { - if (mountedRef.current) { - tagify.current.loading(loading); - } - }, [loading]); - (0, _react.useEffect)(function () { - if (mountedRef.current) { - tagify.current.setReadonly(readOnly); - } - }, [readOnly]); - (0, _react.useEffect)(function () { - var 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)(function () { - 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: _propTypes.element, - 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]) -}; - -var Tags = _react.default.memo(TagifyWrapper); - -Tags.displayName = "Tags"; - -var MixedTags = function MixedTags(_ref2) { - var children = _ref2.children, - rest = _objectWithoutProperties(_ref2, ["children"]); - - return ( - /*#__PURE__*/ - _react.default.createElement(Tags, _extends({ - InputMode: "textarea" - }, rest), children) - ); -}; - -exports.MixedTags = MixedTags; -var _default = Tags; -exports.default = _default; -return Tags; -})); +!function(e,t){"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?module.exports=t():e.React.tagify=t()}(this,function(){"use strict";function i(e){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=exports.MixedTags=void 0;var e,H=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!==i(e)&&"function"!=typeof e)return{default:e};var t=a();if(t&&t.has(e))return t.get(e);var n={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)){var u=r?Object.getOwnPropertyDescriptor(e,o):null;u&&(u.get||u.set)?Object.defineProperty(n,o,u):n[o]=e[o]}n.default=e,t&&t.set(e,n);return n}(require("react")),L=require("react-dom/server"),t=require("prop-types"),Q=(e=require("./tagify.min.js"))&&e.__esModule?e:{default:e};function a(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return a=function(){return e},e}function r(){return(r=Object.assign||function(e){for(var t=1;t (new Date().getTime() + Math.floor((Math.random()*10000)+1)).toString(16) - - var removeCollectionProp = function removeCollectionProp(collection, unwantedProps) { - return collection.map(function (v) { - var props = {}; - - for (var p in v) { - if (unwantedProps.indexOf(p) < 0) props[p] = v[p]; - } - - return props; - }); - }; - 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, function (m, $1) { - return $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 s.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/`|'/g, "'"); - } - - function isArray(a) { - return a instanceof Array; - } - /** - * 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 (isArray(b[key])) { - a[key] = Object.assign([], b[key]); - continue; - } - - a[key] = b[key]; - } - } - } - - return o; - } - /** - * 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); - - var dropdownMethods = { - init: function init() { - this.DOM.dropdown = this.parseTemplate('dropdown', [this.settings]); - this.DOM.dropdown.content = this.DOM.dropdown.querySelector(this.settings.classNames.dropdownWrapperSelector); - }, - - /** - * shows the suggestions select box - * @param {String} value [optional, filter the whitelist by this value] - */ - show: function show(value) { - var _this = this; - - var _s = this.settings, - firstListItem, - firstListItemValue, - selection = window.getSelection(), - 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) 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.call(this, 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: 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: value - }); - } else { - if (value && allowNewTags && !this.state.editing.scope) { - this.suggestedListItems = [{ - value: value - }]; - } // hide suggestions list if no suggestion matched - else { - this.input.autocomplete.suggest.call(this); - this.dropdown.hide.call(this); - 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.call(this, noMatchListItem); - if (_s.dropdown.highlightFirst) this.dropdown.highlightOption.call(this, this.DOM.dropdown.content.children[0]); // 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.state.selection = { - anchorOffset: selection.anchorOffset, - anchorNode: selection.anchorNode - }; // 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(function () { - _this.dropdown.position.call(_this); - - _this.dropdown.render.call(_this); - }); - } // a delay is needed because of the previous delay reason. - // this event must be fired after the dropdown was rendered & positioned - - - setTimeout(function () { - _this.trigger("dropdown:show", _this.DOM.dropdown); - }); - }, - hide: function hide(force) { - var _this2 = this; - - var _this$DOM = this.DOM, - scope = _this$DOM.scope, - dropdown = _this$DOM.dropdown, - isManual = this.settings.dropdown.position == 'manual' && !force; // 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(function () { - _this2.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; - }, - render: function render() { - var _this3 = this; - - // 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; - 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.call(this, ddHeight); - - _s.dropdown.appendTarget.appendChild(this.DOM.dropdown); - - setTimeout(function () { - return _this3.DOM.dropdown.classList.remove(_s.classNames.dropdownInital); - }); - } - - return this; - }, - - /** - * - * @param {String/Array} HTMLContent - optional - */ - fill: function fill(HTMLContent) { - HTMLContent = typeof HTMLContent == 'string' ? HTMLContent : this.dropdown.createListHTML.call(this, HTMLContent || this.suggestedListItems); - this.DOM.dropdown.content.innerHTML = minify(HTMLContent); - }, - - /** - * fill data into the suggestions list - * (mainly used to update the list when removing tags, so they will be re-added to the list. not efficient) - */ - refilter: function refilter(value) { - value = value || this.state.dropdown.query || ''; - this.suggestedListItems = this.dropdown.filterListItems.call(this, value); - this.dropdown.fill.call(this); - if (!this.suggestedListItems.length) this.dropdown.hide.call(this); - this.trigger("dropdown:updated", this.DOM.dropdown); - }, - position: function position(ddHeight) { - if (this.settings.dropdown.position == 'manual') return; - var placeAbove, - rect, - top, - bottom, - left, - width, - parentsPositions, - ddElm = this.DOM.dropdown, - viewportHeight = document.documentElement.clientHeight, - viewportWidth = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0), - positionTo = viewportWidth > 480 ? this.settings.dropdown.position : 'all', - ddTarget = this.DOM[positionTo == 'input' ? 'input' : 'scope']; - ddHeight = ddHeight || ddElm.clientHeight; - - function getParentsPositions(p) { - var left = 0, - top = 0; - - while (p) { - left += p.offsetLeft || 0; - top += p.offsetTop || 0; - p = p.parentNode; - } - - return { - left: left, - top: top - }; - } - - if (!this.state.dropdown.visible) return; - - if (positionTo == 'text') { - rect = this.getCaretGlobalPosition(); - bottom = rect.bottom; - top = rect.top; - left = rect.left; - width = 'auto'; - } else { - parentsPositions = getParentsPositions(this.settings.dropdown.appendTarget); - rect = ddTarget.getBoundingClientRect(); - top = rect.top + 2 - parentsPositions.top; - bottom = rect.bottom - 1 - parentsPositions.top; - left = rect.left - parentsPositions.left; - width = rect.width + 'px'; - } - - top = Math.floor(top); - bottom = Math.ceil(bottom); - placeAbove = viewportHeight - rect.bottom < ddHeight; // 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 + window.pageYOffset) + "px" : "top: " + (bottom + window.pageYOffset) + "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: function binding() { - var 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), - 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') { - 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: function onKeyDown(e) { - // get the "active" element, and if there was none (yet) active, use first child - var activeListElm = this.DOM.dropdown.querySelector(this.settings.classNames.dropdownItemActiveSelector), - selectedElm = activeListElm; - - switch (e.key) { - case 'ArrowDown': - case 'ArrowUp': - case 'Down': // >IE11 - - case 'Up': - { - // >IE11 - e.preventDefault(); - var dropdownItems; - if (selectedElm) selectedElm = selectedElm[(e.key == 'ArrowUp' || e.key == 'Up' ? "previous" : "next") + "ElementSibling"]; // if no element was found, loop - - if (!selectedElm) { - dropdownItems = this.DOM.dropdown.content.children; - selectedElm = dropdownItems[e.key == 'ArrowUp' || e.key == 'Up' ? dropdownItems.length - 1 : 0]; - } - - this.dropdown.highlightOption.call(this, selectedElm, true); - break; - } - - case 'Escape': - case 'Esc': - // IE11 - this.dropdown.hide.call(this); - 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 tagifySuggestionIdx = selectedElm.getAttribute('tagifySuggestionIdx'), - data = tagifySuggestionIdx ? this.suggestedListItems[+tagifySuggestionIdx] : '', - value = this.dropdown.getMappedValue.call(this, data); - this.input.autocomplete.set.call(this, value); - return false; - } - - return true; - } - - case 'Enter': - { - e.preventDefault(); - this.dropdown.selectOption.call(this, activeListElm); - break; - } - - case 'Backspace': - { - if (this.settings.mode == 'mix' || this.state.editing.scope) return; - - var _value = this.state.inputText.trim(); - - 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: function onMouseOver(e) { - var ddItem = e.target.closest(this.settings.classNames.dropdownItemSelector); // event delegation check - - ddItem && this.dropdown.highlightOption.call(this, ddItem); - }, - onMouseLeave: function onMouseLeave(e) { - // de-highlight any previously highlighted option - this.dropdown.highlightOption.call(this); - }, - onClick: function onClick(e) { - var _this4 = this; - - if (e.button != 0 || e.target == this.DOM.dropdown || e.target == this.DOM.dropdown.content) return; // allow only mouse left-clicks - - var listItemElm = e.target.closest(this.settings.classNames.dropdownItemSelector); // temporary set the "actions" state to indicate to the main "blur" event it shouldn't run - - this.state.actions.selectOption = true; - setTimeout(function () { - return _this4.state.actions.selectOption = false; - }, 50); - this.settings.hooks.suggestionClick(e, { - tagify: this, - suggestionElm: listItemElm - }).then(function () { - if (listItemElm) _this4.dropdown.selectOption.call(_this4, listItemElm);else _this4.dropdown.hide.call(_this4); - }).catch(function (err) { - return err; - }); - }, - onScroll: function onScroll(e) { - var elm = e.target, - pos = elm.scrollTop / (elm.scrollHeight - elm.parentNode.clientHeight) * 100; - this.trigger("dropdown:scroll", { - percentage: Math.round(pos) - }); - } - } - }, - - /** - * 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: function 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.suggestedListItems[this.getNodeIndex(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.call(this); // 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 - */ - selectOption: function selectOption(elm) { - var _this5 = this; - - 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.call(this); - return; - } // 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 tagifySuggestionIdx = elm.getAttribute('tagifySuggestionIdx'), - tagData = this.suggestedListItems[+tagifySuggestionIdx]; - this.trigger("dropdown:select", { - data: tagData, - elm: elm - }); // above event must be triggered, regardless of anything else which might go wrong - - if (!tagifySuggestionIdx || !tagData) { - this.dropdown.hide.call(this); - return; - } - - if (this.state.editing) this.onEditTagDone(null, extend({ - __isValid: true - }, tagData)); // 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], clearOnSelect); - } // todo: consider not doing this on mix-mode - - setTimeout(function () { - _this5.DOM.input.focus(); - - _this5.toggleFocusClass(true); - }); - - if (closeOnSelect) { - return this.dropdown.hide.call(this); - } - - this.dropdown.refilter.call(this); - }, - selectAll: function selectAll() { - // having suggestedListItems with items messes with "normalizeTags" when wanting - // to add all tags - this.suggestedListItems.length = 0; - this.dropdown.hide.call(this); // 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(this.dropdown.filterListItems.call(this, ''), 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: function filterListItems(value, options) { - var _this6 = this; - - var _s = this.settings, - _sd = _s.dropdown, - options = options || {}, - list = [], - whitelist = _s.whitelist, - suggestionsCount = _sd.maxItems || Infinity, - searchKeys = _sd.searchKeys, - whitelistItem, - valueIsInWhitelist, - searchBy, - isDuplicate, - niddle, - i = 0; - - if (!value || !searchKeys.length) { - return (_s.duplicates ? whitelist : whitelist.filter(function (item) { - return !_this6.isTagDuplicate(isObject(item) ? item.value : item); - }) // don't include tags which have already been added. - ).slice(0, suggestionsCount); // respect "maxItems" dropdown setting - } - - niddle = _sd.caseSensitive ? "" + value : ("" + value).toLowerCase(); - - function stringHasAll(s, query) { - return query.toLowerCase().split(' ').every(function (q) { - return s.includes(q.toLowerCase()); - }); - } - - for (; i < whitelist.length; i++) { - whitelistItem = whitelist[i] instanceof Object ? whitelist[i] : { - value: whitelist[i] - }; //normalize value as an Object - - var itemWithoutSearchKeys = !Object.keys(whitelistItem).some(function (k) { - return searchKeys.includes(k); - }), - _searchKeys = itemWithoutSearchKeys ? ["value"] : searchKeys; - - if (_sd.fuzzySearch && !options.exact) { - searchBy = _searchKeys.reduce(function (values, k) { - return values + " " + (whitelistItem[k] || ""); - }, "").toLowerCase(); - - if (_sd.accentedSearch) { - searchBy = unaccent(searchBy); - niddle = unaccent(niddle); - } - - valueIsInWhitelist = stringHasAll(searchBy, niddle); - } else { - valueIsInWhitelist = _searchKeys.some(function (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(); - return options.exact ? v == niddle : v.indexOf(niddle) == 0; - }); - } - - isDuplicate = !_s.duplicates && this.isTagDuplicate(isObject(whitelistItem) ? whitelistItem.value : whitelistItem); // match for the value within each "whitelist" item - - if (valueIsInWhitelist && !isDuplicate && suggestionsCount--) list.push(whitelistItem); - if (suggestionsCount == 0) break; - } - - return list; - }, - - /** - * Returns the final value of a tag data (object) with regards to the "mapValueTo" dropdown setting - * @param {Object} tagData - * @returns - */ - getMappedValue: function 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} list [Array of Objects] - * @return {String} - */ - createListHTML: function createListHTML(optionsArr) { - var _this7 = this; - - return extend([], optionsArr).map(function (suggestion, idx) { - if (typeof suggestion == 'string' || typeof suggestion == 'number') suggestion = { - value: suggestion - }; - - var value = _this7.dropdown.getMappedValue.call(_this7, suggestion); - - suggestion.value = value && typeof value == 'string' ? escapeHTML(value) : value; - - var tagHTMLString = _this7.settings.templates.dropdownItem.call(_this7, suggestion); // make sure the sugestion index is present as attribute, to match the data when one is selected - - - tagHTMLString = tagHTMLString.replace(/\s*tagifySuggestionIdx=(["'])(.*?)\1/gmi, '') // remove the "tagifySuggestionIdx" attribute if for some reason was there - .replace('>', " tagifySuggestionIdx=\"".concat(idx, "\">")); // add "tagifySuggestionIdx" - - return tagHTMLString; - }).join(""); - } - }; - - 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, - // Flag - automatically adds the text which was inputed as a tag when blur event happens - duplicates: false, - // Flag - allow tuplicate 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, - // Flag - Only allow tags allowed in whitelist - keepInvalidTags: false, - // Flag - if true, do not remove tags which did not pass validation - 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 - backspace: true, - // false / true / "edit" - skipInvalid: false, - // If `true`, do not add invalid, temporary, tags before automatically removing them - 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: function 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 - mixMode: { - insertAfterTag: "\xA0" // 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', - tag: 'tagify__tag', - tagNoAnimation: 'tagify--noAnim', - tagInvalid: 'tagify--invalid', - tagNotAllowed: 'tagify--notAllowed', - inputInvalid: 'tagify__input--invalid', - tagX: 'tagify__tag__removeBtn', - tagText: 'tagify__tag-text', - dropdown: 'tagify__dropdown', - dropdownWrapper: 'tagify__dropdown__wrapper', - dropdownItem: 'tagify__dropdown__item', - dropdownItemActive: 'tagify__dropdown__item--active', - dropdownInital: 'tagify__dropdown--initial', - scopeLoading: 'tagify--loading', - tagLoading: 'tagify__tag--loading', - tagEditing: 'tagify__tag--editable', - tagFlash: 'tagify__tag--flash', - tagHide: 'tagify__tag--hide', - hasMaxTags: 'tagify--hasMaxTags', - hasNoTags: 'tagify--noTags', - empty: 'tagify--empty' - }, - dropdown: { - classname: '', - enabled: 2, - // minimum input characters needs to be typed for the suggestions dropdown to show - maxItems: 10, - searchKeys: ["value", "searchBy"], - fuzzySearch: true, - caseSensitive: false, - accentedSearch: true, - 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 one DOM has been loaded - - }, - hooks: { - beforeRemoveTag: function beforeRemoveTag() { - return Promise.resolve(); - }, - suggestionClick: function suggestionClick() { - return Promise.resolve(); - } - } - }; - - var templates = { - /** - * - * @param {DOM Object} input Original input DOm element - * @param {Object} settings Tagify instance settings Object - */ - wrapper: function wrapper(input, _s) { - return "\n \n "); - }, - tag: function tag(tagData) { - return "\n \n
\n ").concat(tagData[this.settings.tagTextProp] || tagData.value, "\n
\n
"); - }, - dropdown: function dropdown(settings) { - var _sd = settings.dropdown, - isManual = _sd.position == 'manual', - className = "".concat(settings.classNames.dropdown); - return "
\n
\n
"); - }, - dropdownItem: function dropdownItem(item) { - return "
").concat(item.value, "
"); - }, - 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(function (name) { - return target[op + 'EventListener'].call(target, name, cb); - }); - } // Pass EventTarget interface calls to DOM EventTarget object - - - return { - off: function off(events, cb) { - addRemove('remove', events, cb); - return this; - }, - on: function on(events, cb) { - if (cb && typeof cb == 'function') addRemove('add', events, cb); - return this; - }, - trigger: function trigger(eventName, data) { - var e; - 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 = extend({}, _typeof(data) === 'object' ? data : { - value: data - }); - eventData.tagify = this; // 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); - } - } - }; - } - - 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: function customBinding() { - var _this2 = this; - - this.customEventsList.forEach(function (name) { - _this2.on(name, _this2.settings.callbacks[name]); - }); - }, - binding: function binding() { - var 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 event should never be unbinded: - // IE cannot register "input" events on contenteditable elements, so the "keydown" should be used instead.. - this.DOM.input.addEventListener(this.isIE ? "keydown" : "input", _CB[this.isIE ? "onInputIE" : "onInput"].bind(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)], - blur: ['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)] - }; - - for (var eventName in _CBR) { - // make sure the focus/blur event is always regesitered (and never more than once) - if (eventName == 'blur' && !bindUnbind) continue; - - this.DOM[_CBR[eventName][0]][action](eventName, _CBR[eventName][1]); - } - }, - - /** - * DOM events callbacks - */ - callbacks: { - onFocusBlur: function onFocusBlur(e) { - var text = e.target ? this.trim(e.target.textContent) : '', - // a string - _s = this.settings, - 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, - shouldAddTags; - - if (type == 'blur') { - if (e.relatedTarget === this.DOM.scope) { - this.dropdown.hide.call(this); - this.DOM.input.focus(); - return; - } - - this.postUpdate(); - 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.call(this); // 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.mode != "select" - this.dropdown.show.call(this); - } - - return; - } else if (type == "blur") { - this.trigger("blur", eventData); - this.loading(false); - shouldAddTags = this.settings.mode == 'select' ? !this.value.length || this.value[0].value != text : 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.call(this); - }, - onKeydown: function onKeydown(e) { - var _this3 = this; - - var s = this.trim(e.target.textContent); - this.trigger("keydown", { - originalEvent: this.cloneEvent(e) - }); - /** - * ONLY FOR MIX-MODE: - */ - - if (this.settings.mode == 'mix') { - switch (e.key) { - case 'Left': - case 'ArrowLeft': - { - // when left arrow was pressed, raise 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), - isCaretAfterTag = sel.anchorNode.nodeType == 1 || !sel.anchorOffset && sel.anchorNode.previousElementSibling, - lastInputValue = decode(this.DOM.input.innerHTML), - lastTagElems = this.getTagElms(), - // isCaretInsideTag = sel.anchorNode.parentNode('.' + this.settings.classNames.tag), - tagElmToBeDeleted, - firstTextNodeBeforeTag; - - 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(function () { - _this3.placeCaretAfterNode(firstTextNodeBeforeTag); - - _this3.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[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) && !this.settings.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; - } // 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(this.setRangeAtStartEnd()); - } // 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 - - - setTimeout(function () { - var sel = document.getSelection(), - currentValue = decode(_this3.DOM.input.innerHTML), - prevElm = sel.anchorNode.previousElementSibling; // fixes #384, where the first and only tag will not get removed with backspace - - if (!isChromeAndroidBrowser && currentValue.length >= lastInputValue.length && prevElm && !prevElm.hasAttribute('readonly')) { - _this3.removeTags(prevElm); - - _this3.fixFirefoxLastTagNoCaret(); // the above "removeTag" methods removes the tag with a transition. Chrome adds a
element for some reason at this stage - - - if (_this3.DOM.input.children.length == 2 && _this3.DOM.input.children[1].tagName == "BR") { - _this3.DOM.input.innerHTML = ""; - _this3.value.length = 0; - return true; - } - } // 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 - - - _this3.value = [].map.call(lastTagElems, function (node, nodeIdx) { - var tagData = _this3.tagData(node); // since readonly cannot be removed (it's technically resurrected if removed somehow) - - - if (node.parentNode || tagData.readonly) return tagData;else _this3.trigger('remove', { - tag: node, - index: nodeIdx, - data: tagData - }); - }).filter(function (n) { - return n; - }); // remove empty items in the mapped array - }, 50); // 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 (!this.state.dropdown.visible || this.settings.dropdown.position == 'manual') { - if (s == "" || s.charCodeAt(0) == 8203) { - // 8203: ZERO WIDTH SPACE unicode - if (this.settings.backspace === true) this.removeTags();else if (this.settings.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( this.settings.mode == 'select' ) // issue #333 - if (!this.state.dropdown.visible) this.dropdown.show.call(this); - break; - - case 'ArrowRight': - { - var tagData = this.state.inputSuggestion || this.state.ddItemData; - - if (tagData && this.settings.autoComplete.rightKey) { - this.addTags([tagData], true); - return; - } - - break; - } - - case 'Tab': - { - var selectMode = this.settings.mode == 'select'; - if (s && !selectMode) e.preventDefault();else return true; - } - - case 'Enter': - if (this.state.dropdown.visible || e.keyCode == 229) 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(function () { - if (_this3.state.actions.selectOption) return; - - _this3.addTags(s, true); - }); - } - }, - onInput: function onInput(e) { - if (this.settings.mode == 'mix') return this.events.callbacks.onMixTagsInput.call(this, e); - var value = this.input.normalize.call(this), - showSuggestions = value.length >= this.settings.dropdown.enabled, - eventData = { - value: value, - inputElm: this.DOM.input - }; - eventData.isValid = this.validateTag({ - value: value - }); - this.trigger('input', eventData); // "input" event must be triggered at this point, before the dropdown is shown - // 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(); // fix caret position - - if (value.search(this.settings.delimiters) != -1) { - if (this.addTags(value)) { - this.input.set.call(this); // clear the input field's value - } - } else if (this.settings.dropdown.enabled >= 0) { - this.dropdown[showSuggestions ? "show" : "hide"].call(this, value); - } - }, - onMixTagsInput: function onMixTagsInput(e) { - var _this4 = this; - - var range, - 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, function (node) { - return _this4.tagData(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(function (item) { - if (item.readonly && !remainingTagsValues.includes(item.value)) fragment.appendChild(_this4.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(), function (node) { - return _this4.tagData(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.call(this); - 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(function () { - _this4.update({ - withoutChangeEvent: true - }); - - _this4.trigger("input", extend({}, _this4.state.tag, { - textContent: _this4.DOM.input.textContent - })); - - if (_this4.state.tag) _this4.dropdown[showSuggestions ? "show" : "hide"].call(_this4, _this4.state.tag.value); - }, 10); - }, - onInputIE: function 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); - }); - }, - onClickScope: function 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: this.tagData(tagElm), - originalEvent: this.cloneEvent(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.call(this);else if (_s.dropdown.enabled === 0 && _s.mode != 'mix') this.dropdown.show.call(this); - return; - } - } - - if (_s.mode == 'select') !this.state.dropdown.visible && this.dropdown.show.call(this); - }, - // special proccess is needed for pasted content in order to "clean" it - onPaste: function onPaste(e) { - var clipboardData, pastedData; - e.preventDefault(); - if (this.settings.readonly) return; // Get pasted data via clipboard API - - clipboardData = e.clipboardData || window.clipboardData; - pastedData = clipboardData.getData('Text'); - this.injectAtCaret(pastedData, window.getSelection().getRangeAt(0)); - if (this.settings.mode != 'mix') this.addTags(this.DOM.input.textContent, true); - }, - onEditTagInput: function onEditTagInput(editableElm, e) { - var tagElm = editableElm.closest('.' + this.settings.classNames.tag), - tagElmIdx = this.getNodeIndex(tagElm), - tagData = this.tagData(tagElm), - value = this.input.normalize.call(this, editableElm), - hasChanged = tagElm.innerHTML != tagElm.__tagifyTagData.__originalHTML, - isValid = this.validateTag(_defineProperty({}, this.settings.tagTextProp, value)); // the value could have been invalid in the first-place so make sure to re-validate it (via "addEmptyTag" method) - // 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 (value.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 = value; - this.dropdown.show.call(this, value); - } - - this.trigger("edit:input", { - tag: tagElm, - index: tagElmIdx, - data: extend({}, this.value[tagElmIdx], { - newValue: value - }), - originalEvent: this.cloneEvent(e) - }); - }, - onEditTagFocus: function onEditTagFocus(tagElm) { - this.state.editing = { - scope: tagElm, - input: tagElm.querySelector("[contenteditable]") - }; - }, - onEditTagBlur: function onEditTagBlur(editableElm) { - var _ref; - - 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), - originalData = this.tagData(tagElm).__originalData, - // pre-edit data - hasChanged = tagElm.innerHTML != tagElm.__tagifyTagData.__originalHTML, - isValid = this.validateTag(_defineProperty({}, _s.tagTextProp, textValue)), - newTagData; // this.DOM.input.focus() - - - if (!textValue) { - this.onEditTagDone(tagElm); - return; - } // if nothing changed revert back to how it was before editing - - - if (!hasChanged) { - this.onEditTagDone(tagElm, originalData); - return; - } - - newTagData = this.getWhitelistItem(textValue) || (_ref = {}, _defineProperty(_ref, _s.tagTextProp, textValue), _defineProperty(_ref, "value", textValue), _ref); - - _s.transformTag.call(this, newTagData, originalData); // MUST re-validate after tag transformation - // only validate the "tagTextProp" because is the only thing that metters for validation - - - isValid = this.validateTag(_defineProperty({}, _s.tagTextProp, newTagData[_s.tagTextProp])); - - 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; - } // tagElm.classList.toggle(_s.classNames.tagInvalid, true) - - - this.onEditTagDone(tagElm, newTagData); - }, - onEditTagkeydown: function onEditTagkeydown(e, tagElm) { - this.trigger("edit:keydown", { - originalEvent: this.cloneEvent(e) - }); - - switch (e.key) { - case 'Esc': - case 'Escape': - tagElm.innerHTML = tagElm.__tagifyTagData.__originalHTML; - - case 'Enter': - case 'Tab': - e.preventDefault(); - e.target.blur(); - } - }, - onDoubleClickScope: function onDoubleClickScope(e) { - var tagElm = e.target.closest('.' + this.settings.classNames.tag), - _s = this.settings, - isEditingTag, - isReadyOnlyTag; - if (!tagElm) 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: this.tagData(tagElm) - }); - } - } - }; - - /** - * @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 this; - } - - if (input.previousElementSibling && input.previousElementSibling.classList.contains('tagify')) { - console.warn('Tagify: ', 'input element is already Tagified', input); - return this; - } - - extend(this, EventDispatcher(this)); - this.isFirefox = typeof InstallTrigger !== 'undefined'; - this.isIE = window.document.documentMode; // https://developer.mozilla.org/en-US/docs/Web/API/Document/compatMode#Browser_compatibility - - this.applySettings(input, settings || {}); - this.state = { - inputText: '', - editing: 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); - this.getCSSVars(); - this.loadOriginalValues(); - this.events.customBinding.call(this); - this.events.binding.call(this); - input.autofocus && this.DOM.input.focus(); - } - - Tagify.prototype = { - dropdown: dropdownMethods, - TEXTS: { - empty: "empty", - exceed: "number of tags exceeded", - pattern: "pattern mismatch", - duplicate: "already exists", - notAllowed: "not allowed" - }, - customEventsList: ['change', 'add', 'remove', 'invalid', 'input', 'click', 'keydown', 'focus', 'blur', 'edit:input', 'edit:updated', 'edit:start', 'edit:keydown', 'dropdown:show', 'dropdown:hide', 'dropdown:select', 'dropdown:updated', 'dropdown:noMatch'], - trim: function trim(text) { - return this.settings.trim && text && typeof text == "string" ? text.trim() : text; - }, - // expose this handy utility function - parseHTML: parseHTML, - templates: templates, - parseTemplate: function parseTemplate(template, data) { - template = this.settings.templates[template] || template; - return this.parseHTML(template.apply(this, data)); - }, - applySettings: function applySettings(input, settings) { - DEFAULTS.templates = this.templates; - - var _s = this.settings = extend({}, DEFAULTS, settings); - - _s.readonly = input.hasAttribute('readonly'); // if "readonly" do not include an "input" element inside the Tags component - - _s.placeholder = input.getAttribute('placeholder') || _s.placeholder || ""; - _s.required = input.hasAttribute('required'); - - var _loop = function _loop(name) { - Object.defineProperty(_s.classNames, name + "Selector", { - get: function get() { - return "." + this[name].split(" ").join("."); - } - }); - }; - - for (var name in _s.classNames) { - _loop(name); - } - - if (this.isIE) _s.autoComplete = false; // IE goes crazy if this isn't false - - ["whitelist", "blacklist"].forEach(function (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 (this.settings.delimiters) { - try { - _s.delimiters = new RegExp(this.settings.delimiters, "g"); - } catch (e) {} - } // make sure the dropdown will be shown on "focus" and not only after typing something (in "select" mode) - - - if (_s.mode == 'select') _s.dropdown.enabled = 0; - _s.dropdown.appendTarget = settings.dropdown && settings.dropdown.appendTarget ? settings.dropdown.appendTarget : document.body; - }, - - /** - * Returns a string of HTML element attributes - * @param {Object} data [Tag data] - */ - getAttributes: function getAttributes(data) { - // only items which are objects have properties which can be used as attributes - if (Object.prototype.toString.call(data) != "[object Object]") return ''; - var keys = Object.keys(data), - s = "", - propName, - i; - - for (i = keys.length; i--;) { - propName = keys[i]; - if (propName != 'class' && data.hasOwnProperty(propName) && data[propName] !== undefined) s += " " + propName + (data[propName] !== undefined ? "=\"".concat(data[propName], "\"") : ""); - } - - return s; - }, - setStateSelection: function 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 the caret position relative to the viewport - * https://stackoverflow.com/q/58985076/104380 - * - * @returns {object} left, top distance in pixels - */ - getCaretGlobalPosition: function getCaretGlobalPosition() { - var sel = document.getSelection(); - - if (sel.rangeCount) { - var r = sel.getRangeAt(0); - var node = r.startContainer; - var offset = r.startOffset; - var 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 - }; - }, - - /** - * 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: function getCSSVars() { - var compStyle = getComputedStyle(this.DOM.scope, null); - - var getProp = function getProp(name) { - return compStyle.getPropertyValue('--' + name); - }; - - function seprateUnitFromValue(a) { - if (!a) return {}; - a = a.trim().split(' ')[0]; - var unit = a.split(/\d+/g).filter(function (n) { - return n; - }).pop().trim(), - value = +a.split(unit).filter(function (n) { - return n; - })[0].trim(); - return { - value: value, - unit: unit - }; - } - - this.CSSVars = { - tagHideTransition: function (_ref) { - var 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: function 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.scope = this.parseTemplate('wrapper', [input, this.settings]); - DOM.input = DOM.scope.querySelector(this.settings.classNames.inputSelector); - input.parentNode.insertBefore(DOM.scope, input); - } - - if (this.settings.dropdown.enabled >= 0) this.dropdown.init.call(this); - }, - - /** - * revert any changes made by this component - */ - destroy: function destroy() { - this.DOM.scope.parentNode.removeChild(this.DOM.scope); - this.dropdown.hide.call(this, true); - clearTimeout(this.dropdownHide__bindEventsTimeout); - }, - - /** - * if the original input had any values, add them as tags - */ - loadOriginalValues: function loadOriginalValues(value) { - var lastChild, - _s = this.settings; - value = value || (_s.mixMode.integrated ? this.DOM.input.textContent : this.DOM.originalInput.value); - - if (value) { - this.removeAllTags({ - withoutChangeEvent: true - }); - - if (_s.mode == 'mix') { - this.parseMixTags(value.trim()); - lastChild = this.DOM.input.lastChild; - 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).forEach(function (tag) { - return tag && tag.classList.add(_s.classNames.tagNoAnimation); - }); - } - } else this.postUpdate(); - - this.state.lastOriginalValueReported = _s.mixMode.integrated ? '' : this.DOM.originalInput.value; - this.state.loadedOriginalValues = true; - }, - cloneEvent: function cloneEvent(e) { - var clonedEvent = {}; - - for (var v in e) { - 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: function 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 specieif tag loading state on/off - * @param {Boolean} isLoading - */ - tagLoading: function 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: function toggleClass(className, force) { - if (typeof className == 'string') this.DOM.scope.classList.toggle(className, force); - }, - toggleFocusClass: function toggleFocusClass(force) { - this.toggleClass(this.settings.classNames.focus, !!force); - }, - triggerChangeEvent: triggerChangeEvent, - events: events, - fixFirefoxLastTagNoCaret: function fixFirefoxLastTagNoCaret() { - return; // seems to be fixed in newer version of FF, so retiring below code (for now) - }, - placeCaretAfterNode: function placeCaretAfterNode(node) { - if (!node || !node.parentNode) return; - var nextSibling = node.nextSibling, - sel = window.getSelection(), - range = sel.getRangeAt(0); - - if (sel.rangeCount) { - range.setStartBefore(nextSibling || node); - range.setEndBefore(nextSibling || node); - sel.removeAllRanges(); - sel.addRange(range); - } - }, - insertAfterTag: function 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; - }, - - /** - * Enters a tag into "edit" mode - * @param {Node} tagElm the tag element to edit. if nothing specified, use last last - */ - editTag: function editTag(tagElm, opts) { - var _this = this; - - tagElm = tagElm || this.getLastTag(); - opts = opts || {}; - this.dropdown.hide.call(this); - var _s = this.settings; - - function getEditableElm() { - return tagElm.querySelector(_s.classNames.tagTextSelector); - } - - var editableElm = getEditableElm(), - tagIdx = this.getNodeIndex(tagElm), - tagData = this.tagData(tagElm), - _CB = this.events.callbacks, - that = this, - isValid = true, - delayed_onEditTagBlur = function delayed_onEditTagBlur() { - setTimeout(function () { - return _CB.onEditTagBlur.call(that, getEditableElm()); - }); - }; - - if (!editableElm) { - console.warn('Cannot find element in Tag template: .', _s.classNames.tagTextSelector); - return; - } - - if (tagData instanceof Object && "editable" in tagData && !tagData.editable) return; - editableElm.setAttribute('contenteditable', true); - tagElm.classList.add(_s.classNames.tagEditing); // cache the original data, on the DOM node, before any modification ocurs, for possible revert - - this.tagData(tagElm, { - __originalData: extend({}, tagData), - __originalHTML: tagElm.innerHTML - }); - editableElm.addEventListener('focus', _CB.onEditTagFocus.bind(this, tagElm)); - editableElm.addEventListener('blur', delayed_onEditTagBlur); - editableElm.addEventListener('input', _CB.onEditTagInput.bind(this, editableElm)); - editableElm.addEventListener('keydown', function (e) { - return _CB.onEditTagkeydown.call(_this, e, tagElm); - }); - editableElm.focus(); - this.setRangeAtStartEnd(false, editableElm); - if (!opts.skipValidation) isValid = this.editTagToggleValidity(tagElm, tagData.value); - editableElm.originalIsValid = isValid; - this.trigger("edit:start", { - tag: tagElm, - index: tagIdx, - data: tagData, - isValid: isValid - }); - return this; - }, - editTagToggleValidity: function editTagToggleValidity(tagElm, value) { - var tagData = this.tagData(tagElm), - toggleState; - - if (!tagData) { - console.warn("tag has no data: ", tagElm, tagData); - return; - } - - toggleState = !!(tagData.__isValid && tagData.__isValid != true); //this.validateTag(tagData); - - tagElm.classList.toggle(this.settings.classNames.tagInvalid, toggleState); - return tagData.__isValid; - }, - onEditTagDone: function onEditTagDone(tagElm, tagData) { - tagElm = tagElm || this.state.editing.scope; - tagData = tagData || {}; - var eventData = { - tag: tagElm, - index: this.getNodeIndex(tagElm), - previousData: this.tagData(tagElm), - data: tagData - }; - this.trigger("edit:beforeUpdate", eventData); - this.state.editing = false; - delete tagData.__originalData; - delete tagData.__originalHTML; - - if (tagElm && tagData[this.settings.tagTextProp]) { - this.editTagToggleValidity(tagElm); - this.replaceTag(tagElm, tagData); - } else if (tagElm) this.removeTags(tagElm); - - this.trigger("edit:updated", eventData); - this.dropdown.hide.call(this); // 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: function 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 newTag = this.createTagElem(tagData); // update DOM - - tagElm.parentNode.replaceChild(newTag, tagElm); - this.updateValueByDOMTags(); - }, - - /** - * update "value" (Array of Objects) by traversing all valid tags - */ - updateValueByDOMTags: function updateValueByDOMTags() { - var _this2 = this; - - this.value.length = 0; - [].forEach.call(this.getTagElms(), function (node) { - if (node.classList.contains(_this2.settings.classNames.tagNotAllowed.split(' ')[0])) return; - - _this2.value.push(_this2.tagData(node)); - }); - this.update(); - }, - - /** 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: function setRangeAtStartEnd(start, node) { - start = typeof start == 'number' ? start : !!start; - node = node || this.DOM.input; - node = node.lastChild || node; - var sel = document.getSelection(); - - try { - if (sel.rangeCount >= 1) { - ['Start', 'End'].forEach(function (pos) { - return sel.getRangeAt(0)["set" + pos](node, start ? start : node.length); - }); - } - } catch (err) { - console.warn("Tagify: ", err); - } - }, - - /** - * 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: function injectAtCaret(injectedNode, range) { - range = range || this.state.selection.range; - if (!range) return; - if (typeof injectedNode == 'string') injectedNode = document.createTextNode(injectedNode); - range.deleteContents(); - range.insertNode(injectedNode); - this.setRangeAtStartEnd(false, injectedNode); - this.updateValueByDOMTags(); // updates internal "this.value" - - this.update(); // updates original input/textarea - - return this; - }, - - /** - * input bridge for accessing & setting - * @type {Object} - */ - input: { - set: function set() { - var s = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; - var 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); - }, - - /** - * Marks the tagify's input as "invalid" if the value did not pass "validateTag()" - */ - validate: function 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: function 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(function (n) { - return 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 - - if (this.settings.trim) v = v.replace(/^\s+/, ''); // trimLeft - - return v; - }, - - /** - * suggest the rest of the input's value (via CSS "::after" using "content:attr(...)") - * @param {String} s [description] - */ - autocomplete: { - suggest: function 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: function 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(); - } - - this.input.autocomplete.suggest.call(this); - this.dropdown.hide.call(this); - 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: function getTagIdx(tagData) { - return this.value.findIndex(function (item) { - return item.value == tagData.value; - }); - }, - getNodeIndex: function getNodeIndex(node) { - var index = 0; - if (node) while (node = node.previousElementSibling) { - index++; - } - return index; - }, - getTagElms: function getTagElms() { - for (var _len = arguments.length, classess = new Array(_len), _key = 0; _key < _len; _key++) { - classess[_key] = arguments[_key]; - } - - var classname = '.' + [].concat(_toConsumableArray(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: function getLastTag() { - var lastTag = this.DOM.scope.querySelectorAll("".concat(this.settings.classNames.tagSelector, ":not(.").concat(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: function tagData(tagElm, data, override) { - if (!tagElm) { - console.warn("tag elment 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} v [text value / tag data object] - * @return {Boolean} - */ - isTagDuplicate: function isTagDuplicate(value, caseSensitive) { - var _this3 = this; - - var duplications, - _s = this.settings; // duplications are irrelevant for this scenario - - if (_s.mode == 'select') return false; - duplications = this.value.reduce(function (acc, item) { - return sameStr(_this3.trim("" + value), item.value, caseSensitive || _s.dropdown.caseSensitive) ? acc + 1 : acc; - }, 0); - return duplications; - }, - getTagIndexByValue: function getTagIndexByValue(value) { - var _this4 = this; - - var indices = []; - this.getTagElms().forEach(function (tagElm, i) { - if (sameStr(_this4.trim(tagElm.textContent), value, _this4.settings.dropdown.caseSensitive)) indices.push(i); - }); - return indices; - }, - getTagElmByValue: function 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: function flashTag(tagElm) { - var _this5 = this; - - if (tagElm) { - tagElm.classList.add(this.settings.classNames.tagFlash); - setTimeout(function () { - tagElm.classList.remove(_this5.settings.classNames.tagFlash); - }, 100); - } - }, - - /** - * checks if text is in the blacklist - */ - isTagBlacklisted: function isTagBlacklisted(v) { - v = this.trim(v.toLowerCase()); - return this.settings.blacklist.filter(function (x) { - return ("" + x).toLowerCase() == v; - }).length; - }, - - /** - * checks if text is in the whitelist - */ - isTagWhitelisted: function 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: function getWhitelistItem(value, prop, whitelist) { - var result, - prop = prop || 'value', - _s = this.settings, - whitelist = whitelist || _s.whitelist; - whitelist.some(function (_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 maches 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: function 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; // if duplicates are not allowed and there is a duplicate - - if (!_s.duplicates && this.isTagDuplicate(v, this.state.editing)) 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: function getInvalidTagAttrs(tagData, validation) { - return { - "aria-invalid": true, - "class": "".concat(tagData.class || '', " ").concat(this.settings.classNames.tagNotAllowed).trim(), - "title": validation - }; - }, - hasMaxTags: function hasMaxTags() { - if (this.value.length >= this.settings.maxTags) return this.TEXTS.exceed; - return false; - }, - setReadonly: function setReadonly(isReadonly) { - var _s = this.settings; - document.activeElement.blur(); // exists possible edit-mode - - _s.readonly = isReadonly; - this.DOM.scope[(isReadonly ? 'set' : 'remove') + 'Attribute']('readonly', true); - - if (_s.mode == 'mix') { - this.DOM.input.contentEditable = !isReadonly; - } - }, - - /** - * 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: function normalizeTags(tagsItems) { - var _this6 = this; - - var _this$settings = this.settings, - whitelist = _this$settings.whitelist, - delimiters = _this$settings.delimiters, - mode = _this$settings.mode, - tagTextProp = _this$settings.tagTextProp, - enforceWhitelist = _this$settings.enforceWhitelist, - whitelistMatches = [], - whitelistWithProps = whitelist ? whitelist[0] instanceof Object : false, - isArray = tagsItems instanceof Array, - mapStringToCollection = function mapStringToCollection(s) { - return (s + "").split(delimiters).filter(function (n) { - return n; - }).map(function (v) { - var _ref2; - - return _ref2 = {}, _defineProperty(_ref2, tagTextProp, _this6.trim(v)), _defineProperty(_ref2, "value", _this6.trim(v)), _ref2; - }); - }; - - 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); - } // is is an Array of Strings, convert to an Array of Objects - else if (isArray) { - var _ref3; - - // flatten the 2D array - tagsItems = (_ref3 = []).concat.apply(_ref3, _toConsumableArray(tagsItems.map(function (item) { - return 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 - - - if (whitelistWithProps) { - tagsItems.forEach(function (item) { - var whitelistMatchesValues = whitelistMatches.map(function (a) { - return 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 = _this6.dropdown.filterListItems.call(_this6, item[tagTextProp], { - exact: true - }) // also filter out items which have already been matches in previous iterations - .filter(function (filteredItem) { - return !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 ? _this6.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' && !enforceWhitelist) { - 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: function parseMixTags(s) { - var _this7 = this; - - 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(function (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 = _this7.normalizeTags(preInterpolated)[0] || { - value: preInterpolated - }; - } - - if (!maxTagsReached && s2.length > 1 && (!enforceWhitelist || _this7.isTagWhitelisted(tagData.value)) && !(!duplicates && _this7.isTagDuplicate(tagData.value))) { - transformTag.call(_this7, tagData); // 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] = _this7.trim(tagData[textProp]); - tagElm = _this7.createTagElem(tagData); - tagsDataSet.push(tagData); - tagElm.classList.add(_this7.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) - - _this7.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(function (elm, idx) { - return _this7.tagData(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: function replaceTextWithNode(newWrapperNode, strToReplace) { - if (!this.state.tag && !strToReplace) return; - strToReplace = strToReplace || this.state.tag.prefix + this.state.tag.value; - var idx, - nodeToReplace, - 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); - 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 unwanetd 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: function selectTag(tagElm, tagData) { - if (this.settings.enforceWhitelist && !this.isTagWhitelisted(tagData.value)) return; - this.input.set.call(this, 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.bind(this)); - if (this.getLastTag()) this.replaceTag(this.getLastTag(), tagData);else this.appendTag(tagElm); - this.value[0] = tagData; - this.trigger('add', { - tag: tagElm, - data: tagData - }); - this.update(); - return [tagElm]; - }, - - /** - * add an empty "tag" element in an editable state - */ - addEmptyTag: function addEmptyTag(initialData) { - var tagData = extend({ - value: "" - }, initialData || {}), - tagElm = this.createTagElem(tagData); - this.tagData(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: function addTags(tagsItems, clearInput) { - var _this8 = this; - - var skipInvalid = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.settings.skipInvalid; - var tagElems = [], - _s = this.settings; - - if (!tagsItems || tagsItems.length == 0) { - // is mode is "select" clean all tagsargument of - if (_s.mode == 'select') this.removeAllTags(); - return tagElems; - } // converts Array/String/Object to an Array of Objects - - - tagsItems = this.normalizeTags(tagsItems); - - if (_s.mode == 'mix') { - return this.addMixTags(tagsItems); - } - - if (_s.mode == 'select') clearInput = false; - this.DOM.input.removeAttribute('style'); - tagsItems.forEach(function (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); - tagData.__isValid = _this8.hasMaxTags() || _this8.validateTag(tagData); - - _s.transformTag.call(_this8, 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, _this8.getInvalidTagAttrs(tagData, tagData.__isValid), { - __preInvalidData: originalData - }); - if (tagData.__isValid == _this8.TEXTS.duplicate) // mark, for a brief moment, the tag (this this one) which THIS CURRENT tag is a duplcate of - _this8.flashTag(_this8.getTagElmByValue(tagData.value)); - } ///////////////////////////////////////////////////// - - - if (tagData.readonly) tagElmParams["aria-readonly"] = true; // Create tag HTML element - - tagElm = _this8.createTagElem(extend({}, tagData, tagElmParams)); - tagElems.push(tagElm); // mode-select overrides - - if (_s.mode == 'select') { - return _this8.selectTag(tagElm, tagData); - } // add the tag to the component's DOM - - - _this8.appendTag(tagElm); - - if (tagData.__isValid && tagData.__isValid === true) { - // update state - _this8.value.push(tagData); - - _this8.update(); - - _this8.trigger('add', { - tag: tagElm, - index: _this8.value.length - 1, - data: tagData - }); - } else { - _this8.trigger("invalid", { - data: tagData, - index: _this8.value.length, - tag: tagElm, - message: tagData.__isValid - }); - - if (!_s.keepInvalidTags) // remove invalid tags (if "keepInvalidTags" is set to "false") - setTimeout(function () { - return _this8.removeTags(tagElm, true); - }, 1000); - } - - _this8.dropdown.position.call(_this8); // reposition the dropdown because the just-added tag might cause a new-line - - }); - - if (tagsItems.length && clearInput) { - this.input.set.call(this); - } - - this.dropdown.refilter.call(this); - 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: function addMixTags(tagsData) { - var _this9 = this; - - if (tagsData[0].prefix || this.state.tag) { - this.prefixedTextToTag(tagsData[0]); - return; - } - - if (typeof tagsData == 'string') tagsData = [{ - value: tagsData - }]; - var selection = !!this.state.selection, - // must be cast, not to use the reference which is changing - frag = document.createDocumentFragment(); - tagsData.forEach(function (tagData) { - var tagElm = _this9.createTagElem(tagData); - - frag.appendChild(tagElm); - - _this9.insertAfterTag(tagElm); - }); // 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(frag); - } // 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(frag); - 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} tagsItem [A string (single or multiple values with a delimiter), or an Array of Objects or just Array of Strings] - */ - prefixedTextToTag: function prefixedTextToTag(tagsItem) { - var _this10 = this; - - var _s = this.settings, - tagElm, - createdFromDelimiters = this.state.tag.delimiters; - - _s.transformTag.call(this, tagsItem); - - tagsItem.prefix = tagsItem.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(tagsItem); // 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(function () { - return tagElm.classList.add(_this10.settings.classNames.tagNoAnimation); - }, 300); - this.value.push(tagsItem); - this.update(); - - if (!createdFromDelimiters) { - var elm = this.insertAfterTag(tagElm) || tagElm; - this.placeCaretAfterNode(elm); - } - - this.state.tag = null; - this.trigger('add', extend({}, { - tag: tagElm - }, { - data: tagsItem - })); - return tagElm; - }, - - /** - * appened (validated) tag to the component's DOM scope - */ - appendTag: function appendTag(tagElm) { - var insertBeforeNode = this.DOM.scope.lastElementChild; - if (insertBeforeNode === this.DOM.input) this.DOM.scope.insertBefore(tagElm, insertBeforeNode);else this.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] - * @return {Object} [DOM element] - */ - createTagElem: function createTagElem(tagData) { - var tagElm, - templateData = extend({}, tagData, { - value: escapeHTML(tagData.value + "") - }); // if( this.settings.readonly ) - // tagData.readonly = true - - tagElm = this.parseTemplate('tag', [templateData]); // 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 misplaced inside the tag element (especcially affects "readonly" tags) - - removeTextChildNodes(tagElm); // while( tagElm.lastChild.nodeType == 3 ) - // tagElm.lastChild.parentNode.removeChild(tagElm.lastChild) - - this.tagData(tagElm, tagData); - return tagElm; - }, - - /** - * find all invalid tags and re-check them - */ - reCheckInvalidTags: function reCheckInvalidTags() { - var _this11 = this; - - var _s = this.settings, - selector = "".concat(_s.classNames.tagSelector).concat(_s.classNames.tagNotAllowedSelector), - tagElms = this.DOM.scope.querySelectorAll(selector); - [].forEach.call(tagElms, function (node) { - var tagData = _this11.tagData(node), - wasNodeDuplicate = node.getAttribute('title') == _this11.TEXTS.duplicate, - isNodeValid = _this11.validateTag(tagData) === true; // if this tag node was marked as a dulpicate, unmark. (might have been marked as "notAllowed" for other reasons) - - - if (wasNodeDuplicate && isNodeValid) { - if (tagData.__preInvalidData) tagData = tagData.__preInvalidData;else // start fresh - tagData = { - value: tagData.value - }; - - _this11.replaceTag(node, tagData); - } - }); - }, - - /** - * 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: function removeTags(tagElms, silent, tranDuration) { - var _this12 = this; - - var tagsToRemove; - 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(function (elms, tagElm) { - if (tagElm && typeof tagElm == 'string') tagElm = _this12.getTagElmByValue(tagElm); - if (tagElm) // 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: _this12.getTagIdx(_this12.tagData(tagElm)), - // this.getNodeIndex(tagElm); // this.getTagIndexByValue(tagElm.textContent) - data: _this12.tagData(tagElm, { - '__removed': true - }) - }); - return elms; - }, []); - tranDuration = typeof tranDuration == "number" ? tranDuration : this.CSSVars.tagHideTransition; - - if (this.settings.mode == 'select') { - tranDuration = 0; - this.input.set.call(this); - } // if only a single tag is to be removed - - - if (tagsToRemove.length == 1) { - if (tagsToRemove[0].node.classList.contains(this.settings.classNames.tagNotAllowed)) silent = true; - } - - if (!tagsToRemove.length) return; - this.settings.hooks.beforeRemoveTag(tagsToRemove, { - tagify: this - }).then(function () { - 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.call(this); - this.dropdown.position.call(this); - 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 now un-marked - - if (this.settings.keepInvalidTags) this.reCheckInvalidTags(); - } else if (this.settings.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(this.settings.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(_this12, tagsToRemove[0]);else tagsToRemove.forEach(removeNode.bind(_this12)); // update state regardless of animation - - if (!silent) { - tagsToRemove.forEach(function (tag) { - // remove "__removed" so the comparison in "getTagIdx" could work - var tagData = Object.assign({}, tag.data); // shallow clone - - delete tagData.__removed; - - var tagIdx = _this12.getTagIdx(tagData); - - if (tagIdx > -1) _this12.value.splice(tagIdx, 1); - }); // that.removeValueById(tagData.__uid) - - _this12.update(); // update the original input with the current value - - } - }).catch(function (reason) {}); - }, - removeAllTags: function removeAllTags(_ref4) { - var withoutChangeEvent = _ref4.withoutChangeEvent; - this.value = []; - if (this.settings.mode == 'mix') this.DOM.input.innerHTML = '';else Array.prototype.slice.call(this.getTagElms()).forEach(function (elm) { - return elm.parentNode.removeChild(elm); - }); - this.dropdown.position.call(this); - if (this.settings.mode == 'select') this.input.set.call(this); - this.update({ - withoutChangeEvent: withoutChangeEvent - }); - }, - postUpdate: function postUpdate() { - var classNames = this.settings.classNames, - hasValue = this.settings.mode == 'mix' ? this.settings.mixMode.integrated ? this.DOM.input.textContent : this.DOM.originalInput.value : this.value.length; - this.toggleClass(classNames.hasMaxTags, this.value.length >= this.settings.maxTags); - this.toggleClass(classNames.hasNoTags, !this.value.length); - this.toggleClass(classNames.empty, !hasValue); - }, - - /** - * update the origianl (hidden) input field's value - * see - https://stackoverflow.com/q/50957841/104380 - */ - update: function update(args) { - var inputElm = this.DOM.originalInput, - _ref5 = args || {}, - withoutChangeEvent = _ref5.withoutChangeEvent, - value = removeCollectionProp(this.value, ['__isValid', '__removed']); - - if (!this.settings.mixMode.integrated) { - inputElm.value = this.settings.mode == 'mix' ? this.getMixedTagsAsString(value) : value.length ? this.settings.originalInputValueFormat ? this.settings.originalInputValueFormat(value) : JSON.stringify(value) : ""; - } - - this.postUpdate(); - if (!withoutChangeEvent && this.state.loadedOriginalValues) this.triggerChangeEvent(); - }, - getMixedTagsAsString: function getMixedTagsAsString() { - var result = "", - that = this, - _interpolator = this.settings.mixTagsInterpolator; - - function iterateChildren(rootNode) { - rootNode.childNodes.forEach(function (node) { - if (node.nodeType == 1) { - if (node.classList.contains(that.settings.classNames.tag) && that.tagData(node)) { - if (that.tagData(node).__removed) return;else result += _interpolator[0] + JSON.stringify(node.__tagifyTagData) + _interpolator[1]; - return; - } - - if (node.tagName == 'BR' && (node.parentNode == that.DOM.input || node.parentNode.childNodes.length == 1)) { - result += "\r\n"; - } else if (node.tagName == 'DIV' || node.tagName == 'P') { - result += "\r\n"; - 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=t||self).Tagify=e()}(this,(function(){"use strict";function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}function e(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function i(t){return function(t){if(Array.isArray(t)){for(var e=0,i=new Array(t.length);e/g,">").replace(/"/g,""").replace(/`|'/g,"'")}function r(t){return t instanceof Array}function l(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 d(t,e,i){function s(t,e){for(var i in e)if(e.hasOwnProperty(i)){if(l(e[i])){l(t[i])?s(t[i],e[i]):t[i]=Object.assign({},e[i]);continue}if(r(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 c(t){return String.prototype.normalize?"string"==typeof t?t.normalize("NFD").replace(/[\u0300-\u036f]/g,""):void 0:t}var h=/(?=.*chrome)(?=.*android)/i.test(navigator.userAgent),g={init:function(){this.DOM.dropdown=this.parseTemplate("dropdown",[this.settings]),this.DOM.dropdown.content=this.DOM.dropdown.querySelector(this.settings.classNames.dropdownWrapperSelector)},show:function(t){var e,i,a,n=this,o=this.settings,r=window.getSelection(),d="mix"==o.mode&&!o.enforceWhitelist,c=!o.whitelist||!o.whitelist.length,h="manual"==o.dropdown.position;if(t=void 0===t?this.state.inputText:t,(!c||d||o.templates.dropdownItemNoMatch)&&!1!==o.dropdown.enable&&!this.state.isLoading){if(clearTimeout(this.dropdownHide__bindEventsTimeout),this.suggestedListItems=this.dropdown.filterListItems.call(this,t),t&&!this.suggestedListItems.length&&(this.trigger("dropdown:noMatch",t),o.templates.dropdownItemNoMatch&&(a=o.templates.dropdownItemNoMatch.call(this,{value:t}))),!a){if(this.suggestedListItems.length)t&&d&&!this.state.editing.scope&&!s(this.suggestedListItems[0].value,t)&&this.suggestedListItems.unshift({value:t});else{if(!t||!d||this.state.editing.scope)return this.input.autocomplete.suggest.call(this),void this.dropdown.hide.call(this);this.suggestedListItems=[{value:t}]}i=""+(l(e=this.suggestedListItems[0])?e.value:e),o.autoComplete&&i&&0==i.indexOf(t)&&this.input.autocomplete.suggest.call(this,e)}this.dropdown.fill.call(this,a),o.dropdown.highlightFirst&&this.dropdown.highlightOption.call(this,this.DOM.dropdown.content.children[0]),this.state.dropdown.visible||setTimeout(this.dropdown.events.binding.bind(this)),this.state.dropdown.visible=t||!0,this.state.dropdown.query=t,this.state.selection={anchorOffset:r.anchorOffset,anchorNode:r.anchorNode},h||setTimeout((function(){n.dropdown.position.call(n),n.dropdown.render.call(n)})),setTimeout((function(){n.trigger("dropdown:show",n.DOM.dropdown)}))}},hide:function(t){var e=this,i=this.DOM,s=i.scope,a=i.dropdown,n="manual"==this.settings.dropdown.position&&!t;if(a&&document.body.contains(a)&&!n)return window.removeEventListener("resize",this.dropdown.position),this.dropdown.events.binding.call(this,!1),s.setAttribute("aria-expanded",!1),a.parentNode.removeChild(a),setTimeout((function(){e.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",a),this},render:function(){var t,e,i,s=this,a=(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),n=this.settings;return this.DOM.scope.setAttribute("aria-expanded",!0),document.body.contains(this.DOM.dropdown)||(this.DOM.dropdown.classList.add(n.classNames.dropdownInital),this.dropdown.position.call(this,a),n.dropdown.appendTarget.appendChild(this.DOM.dropdown),setTimeout((function(){return s.DOM.dropdown.classList.remove(n.classNames.dropdownInital)}))),this},fill:function(t){var e;t="string"==typeof t?t:this.dropdown.createListHTML.call(this,t||this.suggestedListItems),this.DOM.dropdown.content.innerHTML=(e=t)?e.replace(/\>[\r\n ]+\<").replace(/(<.*?>)|\s+/g,(function(t,e){return e||" "})):""},refilter:function(t){t=t||this.state.dropdown.query||"",this.suggestedListItems=this.dropdown.filterListItems.call(this,t),this.dropdown.fill.call(this),this.suggestedListItems.length||this.dropdown.hide.call(this),this.trigger("dropdown:updated",this.DOM.dropdown)},position:function(t){if("manual"!=this.settings.dropdown.position){var e,i,s,a,n,o,r,l=this.DOM.dropdown,d=document.documentElement.clientHeight,c=Math.max(document.documentElement.clientWidth||0,window.innerWidth||0)>480?this.settings.dropdown.position:"all",h=this.DOM["input"==c?"input":"scope"];t=t||l.clientHeight,this.state.dropdown.visible&&("text"==c?(a=(i=this.getCaretGlobalPosition()).bottom,s=i.top,n=i.left,o="auto"):(r=function(t){for(var e=0,i=0;t;)e+=t.offsetLeft||0,i+=t.offsetTop||0,t=t.parentNode;return{left:e,top:i}}(this.settings.dropdown.appendTarget),s=(i=h.getBoundingClientRect()).top+2-r.top,a=i.bottom-1-r.top,n=i.left-r.left,o=i.width+"px"),s=Math.floor(s),a=Math.ceil(a),e=d-i.bottom0&&void 0!==arguments[0])||arguments[0],e=this.dropdown.events.callbacks,i=this.listeners.dropdown=this.listeners.dropdown||{position:this.dropdown.position.bind(this),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&&(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:function(t){var e=this.DOM.dropdown.querySelector(this.settings.classNames.dropdownItemActiveSelector),i=e;switch(t.key){case"ArrowDown":case"ArrowUp":case"Down":case"Up":var s;t.preventDefault(),i&&(i=i[("ArrowUp"==t.key||"Up"==t.key?"previous":"next")+"ElementSibling"]),i||(i=(s=this.DOM.dropdown.content.children)["ArrowUp"==t.key||"Up"==t.key?s.length-1:0]),this.dropdown.highlightOption.call(this,i,!0);break;case"Escape":case"Esc":this.dropdown.hide.call(this);break;case"ArrowRight":if(this.state.actions.ArrowLeft)return;case"Tab":if("mix"!=this.settings.mode&&i&&!this.settings.autoComplete.rightKey&&!this.state.editing){t.preventDefault();var a=i.getAttribute("tagifySuggestionIdx"),n=a?this.suggestedListItems[+a]:"",o=this.dropdown.getMappedValue.call(this,n);return this.input.autocomplete.set.call(this,o),!1}return!0;case"Enter":t.preventDefault(),this.dropdown.selectOption.call(this,e);break;case"Backspace":if("mix"==this.settings.mode||this.state.editing.scope)return;var r=this.state.inputText.trim();""!=r&&8203!=r.charCodeAt(0)||(!0===this.settings.backspace?this.removeTags():"edit"==this.settings.backspace&&setTimeout(this.editTag.bind(this),0))}},onMouseOver:function(t){var e=t.target.closest(this.settings.classNames.dropdownItemSelector);e&&this.dropdown.highlightOption.call(this,e)},onMouseLeave:function(t){this.dropdown.highlightOption.call(this)},onClick:function(t){var e=this;if(0==t.button&&t.target!=this.DOM.dropdown&&t.target!=this.DOM.dropdown.content){var i=t.target.closest(this.settings.classNames.dropdownItemSelector);this.state.actions.selectOption=!0,setTimeout((function(){return e.state.actions.selectOption=!1}),50),this.settings.hooks.suggestionClick(t,{tagify:this,suggestionElm:i}).then((function(){i?e.dropdown.selectOption.call(e,i):e.dropdown.hide.call(e)})).catch((function(t){return t}))}},onScroll:function(t){var e=t.target,i=e.scrollTop/(e.scrollHeight-e.parentNode.clientHeight)*100;this.trigger("dropdown:scroll",{percentage:Math.round(i)})}}},highlightOption:function(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.suggestedListItems[this.getNodeIndex(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.call(this))},selectOption:function(t){var e=this,i=this.settings.dropdown,s=i.clearOnSelect,a=i.closeOnSelect;if(!t)return this.addTags(this.state.inputText,!0),void(a&&this.dropdown.hide.call(this));var n=t.getAttribute("tagifySuggestionIdx"),o=this.suggestedListItems[+n];if(this.trigger("dropdown:select",{data:o,elm:t}),n&&o){if(this.state.editing?this.onEditTagDone(null,d({__isValid:!0},o)):this["mix"==this.settings.mode?"addMixTags":"addTags"]([o],s),setTimeout((function(){e.DOM.input.focus(),e.toggleFocusClass(!0)})),a)return this.dropdown.hide.call(this);this.dropdown.refilter.call(this)}else this.dropdown.hide.call(this)},selectAll:function(){return this.suggestedListItems.length=0,this.dropdown.hide.call(this),this.addTags(this.dropdown.filterListItems.call(this,""),!0),this},filterListItems:function(t,e){var i,s,a,n,o,r=this,d=this.settings,h=d.dropdown,g=(e=e||{},[]),u=d.whitelist,p=h.maxItems||1/0,f=h.searchKeys,m=0;if(!t||!f.length)return(d.duplicates?u:u.filter((function(t){return!r.isTagDuplicate(l(t)?t.value:t)}))).slice(0,p);function v(t,e){return e.toLowerCase().split(" ").every((function(e){return t.includes(e.toLowerCase())}))}for(o=h.caseSensitive?""+t:(""+t).toLowerCase();m",' tagifySuggestionIdx="'.concat(i,'">'))})).join("")}},u={delimiters:",",pattern:null,tagTextProp:"value",maxTags:1/0,callbacks:{},addTagOnBlur:!0,duplicates:!1,whitelist:[],blacklist:[],enforceWhitelist:!1,keepInvalidTags:!1,mixTagsAllowedAfter:/,|\.|\:|\s/,mixTagsInterpolator:["[[","]]"],backspace:!0,skipInvalid:!1,editTags:{clicks:2,keepInvalid:!0},transformTag:function(){},trim:!0,mixMode:{insertAfterTag:" "},autoComplete:{enabled:!0,rightKey:!1},classNames:{namespace:"tagify",mixMode:"tagify--mix",selectMode:"tagify--select",input:"tagify__input",focus:"tagify--focus",tag:"tagify__tag",tagNoAnimation:"tagify--noAnim",tagInvalid:"tagify--invalid",tagNotAllowed:"tagify--notAllowed",inputInvalid:"tagify__input--invalid",tagX:"tagify__tag__removeBtn",tagText:"tagify__tag-text",dropdown:"tagify__dropdown",dropdownWrapper:"tagify__dropdown__wrapper",dropdownItem:"tagify__dropdown__item",dropdownItemActive:"tagify__dropdown__item--active",dropdownInital:"tagify__dropdown--initial",scopeLoading:"tagify--loading",tagLoading:"tagify__tag--loading",tagEditing:"tagify__tag--editable",tagFlash:"tagify__tag--flash",tagHide:"tagify__tag--hide",hasMaxTags:"tagify--hasMaxTags",hasNoTags:"tagify--noTags",empty:"tagify--empty"},dropdown:{classname:"",enabled:2,maxItems:10,searchKeys:["value","searchBy"],fuzzySearch:!0,caseSensitive:!1,accentedSearch:!0,highlightFirst:!1,closeOnSelect:!0,clearOnSelect:!0,position:"all",appendTarget:null},hooks:{beforeRemoveTag:function(){return Promise.resolve()},suggestionClick:function(){return Promise.resolve()}}};var p={customBinding:function(){var t=this;this.customEventsList.forEach((function(e){t.on(e,t.settings.callbacks[e])}))},binding:function(){var t,e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],i=this.events.callbacks,s=e?"addEventListener":"removeEventListener";if(!this.state.mainEvents||!e)for(var a in this.state.mainEvents=e,e&&!this.listeners.main&&(this.DOM.input.addEventListener(this.isIE?"keydown":"input",i[this.isIE?"onInputIE":"onInput"].bind(this)),this.settings.isJQueryPlugin&&jQuery(this.DOM.originalInput).on("tagify.removeAllTags",this.removeAllTags.bind(this))),t=this.listeners.main=this.listeners.main||{focus:["input",i.onFocusBlur.bind(this)],blur:["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)]})("blur"!=a||e)&&this.DOM[t[a][0]][s](a,t[a][1])},callbacks:{onFocusBlur:function(t){var e=t.target?this.trim(t.target.textContent):"",i=this.settings,s=t.type,a=i.dropdown.enabled>=0,n={relatedTarget:t.relatedTarget},o=this.state.actions.selectOption&&(a||!i.dropdown.closeOnSelect),r=this.state.actions.addNew&&a;if("blur"==s){if(t.relatedTarget===this.DOM.scope)return this.dropdown.hide.call(this),void this.DOM.input.focus();this.postUpdate(),this.triggerChangeEvent()}if(!o&&!r)if(this.state.hasFocus="focus"==s&&+new Date,this.toggleFocusClass(this.state.hasFocus),"mix"!=i.mode){if("focus"==s)return this.trigger("focus",n),void(0===i.dropdown.enabled&&this.dropdown.show.call(this));"blur"==s&&(this.trigger("blur",n),this.loading(!1),("select"==this.settings.mode?!this.value.length||this.value[0].value!=e:e&&!this.state.actions.selectOption&&i.addTagOnBlur)&&this.addTags(e,!0)),this.DOM.input.removeAttribute("style"),this.dropdown.hide.call(this)}else"focus"==s?this.trigger("focus",n):"blur"==t.type&&(this.trigger("blur",n),this.loading(!1),this.dropdown.hide.call(this),this.state.dropdown.visible=void 0,this.setStateSelection())},onKeydown:function(t){var e=this,i=this.trim(t.target.textContent);if(this.trigger("keydown",{originalEvent:this.cloneEvent(t)}),"mix"==this.settings.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,o,r=document.getSelection(),l="Delete"==t.key&&r.anchorOffset==(r.anchorNode.length||0),d=1==r.anchorNode.nodeType||!r.anchorOffset&&r.anchorNode.previousElementSibling,c=a(this.DOM.input.innerHTML),g=this.getTagElms();if(h&&d)return o=n(d),d.hasAttribute("readonly")||d.remove(),this.DOM.input.focus(),void setTimeout((function(){e.placeCaretAfterNode(o),e.DOM.input.click()}));if("BR"==r.anchorNode.nodeName)return;if((l||d)&&1==r.anchorNode.nodeType?s=0==r.anchorOffset?l?g[0]:null:g[r.anchorOffset-1]:l?s=r.anchorNode.nextElementSibling:d&&(s=d),3==r.anchorNode.nodeType&&!r.anchorNode.nodeValue&&r.anchorNode.previousElementSibling&&t.preventDefault(),(d||l)&&!this.settings.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&&s&&s.hasAttribute("readonly"))return void this.placeCaretAfterNode(n(s));this.isFirefox&&1==r.anchorNode.nodeType&&0!=r.anchorOffset&&(this.removeTags(),this.placeCaretAfterNode(this.setRangeAtStartEnd())),setTimeout((function(){var t=document.getSelection(),i=a(e.DOM.input.innerHTML),s=t.anchorNode.previousElementSibling;if(!h&&i.length>=c.length&&s&&!s.hasAttribute("readonly")&&(e.removeTags(s),e.fixFirefoxLastTagNoCaret(),2==e.DOM.input.children.length&&"BR"==e.DOM.input.children[1].tagName))return e.DOM.input.innerHTML="",e.value.length=0,!0;e.value=[].map.call(g,(function(t,i){var s=e.tagData(t);if(t.parentNode||s.readonly)return s;e.trigger("remove",{tag:t,index:i,data:s})})).filter((function(t){return t}))}),50)}return!0}switch(t.key){case"Backspace":this.state.dropdown.visible&&"manual"!=this.settings.dropdown.position||""!=i&&8203!=i.charCodeAt(0)||(!0===this.settings.backspace?this.removeTags():"edit"==this.settings.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.call(this);break;case"ArrowRight":var u=this.state.inputSuggestion||this.state.ddItemData;if(u&&this.settings.autoComplete.rightKey)return void this.addTags([u],!0);break;case"Tab":var p="select"==this.settings.mode;if(!i||p)return!0;t.preventDefault();case"Enter":if(this.state.dropdown.visible||229==t.keyCode)return;t.preventDefault(),setTimeout((function(){e.state.actions.selectOption||e.addTags(i,!0)}))}},onInput:function(t){if("mix"==this.settings.mode)return this.events.callbacks.onMixTagsInput.call(this,t);var e=this.input.normalize.call(this),i=e.length>=this.settings.dropdown.enabled,s={value:e,inputElm:this.DOM.input};s.isValid=this.validateTag({value:e}),this.trigger("input",s),this.state.inputText!=e&&(this.input.set.call(this,e,!1),-1!=e.search(this.settings.delimiters)?this.addTags(e)&&this.input.set.call(this):this.settings.dropdown.enabled>=0&&this.dropdown[i?"show":"hide"].call(this,e))},onMixTagsInput:function(t){var e,i,s,a,n,o,r,l,c=this,g=this.settings,u=this.value.length,p=this.getTagElms(),f=document.createDocumentFragment(),m=window.getSelection().getRangeAt(0),v=[].map.call(p,(function(t){return c.tagData(t).value}));if("deleteContentBackward"==t.inputType&&h&&this.events.callbacks.onKeydown.call(this,{target:t.target,key:"Backspace"}),this.value.slice().forEach((function(t){t.readonly&&!v.includes(t.value)&&f.appendChild(c.createTagElem(t))})),f.childNodes.length&&(m.insertNode(f),this.setRangeAtStartEnd(!1,f.lastChild)),p.length!=u)return this.value=[].map.call(this.getTagElms(),(function(t){return c.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(g.pattern).length-1,(i=e.match(g.pattern))&&(a=e.slice(e.lastIndexOf(i[i.length-1]))),a){if(this.state.actions.ArrowLeft=!1,this.state.tag={prefix:a.match(g.pattern)[0],value:a.replace(g.pattern,"")},this.state.tag.baseOffset=o.baseOffset-this.state.tag.value.length,l=this.state.tag.value.match(g.delimiters))return this.state.tag.value=this.state.tag.value.replace(g.delimiters,""),this.state.tag.delimiters=l[0],this.addTags(this.state.tag.value,g.dropdown.clearOnSelect),void this.dropdown.hide.call(this);n=this.state.tag.value.length>=g.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||s500)?this.state.dropdown.visible?this.dropdown.hide.call(this):0===e.dropdown.enabled&&"mix"!=e.mode&&this.dropdown.show.call(this):"select"==e.mode&&!this.state.dropdown.visible&&this.dropdown.show.call(this));this.removeTags(t.target.parentNode)}else this.state.hasFocus||this.DOM.input.focus()},onPaste:function(t){var e;t.preventDefault(),this.settings.readonly||(e=(t.clipboardData||window.clipboardData).getData("Text"),this.injectAtCaret(e,window.getSelection().getRangeAt(0)),"mix"!=this.settings.mode&&this.addTags(this.DOM.input.textContent,!0))},onEditTagInput:function(t,i){var s=t.closest("."+this.settings.classNames.tag),a=this.getNodeIndex(s),n=this.tagData(s),o=this.input.normalize.call(this,t),r=s.innerHTML!=s.__tagifyTagData.__originalHTML,l=this.validateTag(e({},this.settings.tagTextProp,o));r||!0!==t.originalIsValid||(l=!0),s.classList.toggle(this.settings.classNames.tagInvalid,!0!==l),n.__isValid=l,s.title=!0===l?n.title||n.value:l,o.length>=this.settings.dropdown.enabled&&(this.state.editing&&(this.state.editing.value=o),this.dropdown.show.call(this,o)),this.trigger("edit:input",{tag:s,index:a,data:d({},this.value[a],{newValue:o}),originalEvent:this.cloneEvent(i)})},onEditTagFocus:function(t){this.state.editing={scope:t,input:t.querySelector("[contenteditable]")}},onEditTagBlur:function(t){var i;if(this.state.hasFocus||this.toggleFocusClass(),this.DOM.scope.contains(t)){var s,a=this.settings,n=t.closest("."+a.classNames.tag),o=this.input.normalize.call(this,t),r=this.tagData(n).__originalData,l=n.innerHTML!=n.__tagifyTagData.__originalHTML,d=this.validateTag(e({},a.tagTextProp,o));if(o)if(l){if(s=this.getWhitelistItem(o)||(e(i={},a.tagTextProp,o),e(i,"value",o),i),a.transformTag.call(this,s,r),!0!==(d=this.validateTag(e({},a.tagTextProp,s[a.tagTextProp])))){if(this.trigger("invalid",{data:s,tag:n,message:d}),a.editTags.keepInvalid)return;a.keepInvalidTags?s.__isValid=d:s=r}this.onEditTagDone(n,s)}else this.onEditTagDone(n,r);else this.onEditTagDone(n)}},onEditTagkeydown:function(t,e){switch(this.trigger("edit:keydown",{originalEvent:this.cloneEvent(t)}),t.key){case"Esc":case"Escape":e.innerHTML=e.__tagifyTagData.__originalHTML;case"Enter":case"Tab":t.preventDefault(),t.target.blur()}},onDoubleClickScope:function(t){var e,i,s=t.target.closest("."+this.settings.classNames.tag),a=this.settings;s&&(e=s.classList.contains(this.settings.classNames.tagEditing),i=s.hasAttribute("readonly"),"select"==a.mode||a.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)}))}}};function f(e,i){return e?e.previousElementSibling&&e.previousElementSibling.classList.contains("tagify")?(console.warn("Tagify: ","input element is already Tagified",e),this):(d(this,function(e){var i=document.createTextNode("");function s(t,e,s){s&&e.split(/\s+/g).forEach((function(e){return i[t+"EventListener"].call(i,e,s)}))}return{off:function(t,e){return s("remove",t,e),this},on:function(t,e){return e&&"function"==typeof e&&s("add",t,e),this},trigger:function(s,a){var n;if(s)if(e.settings.isJQueryPlugin)"remove"==s&&(s="removeTag"),jQuery(e.DOM.originalInput).triggerHandler(s,[a]);else{try{var o=d({},"object"===t(a)?a:{value:a});if(o.tagify=this,a instanceof Object)for(var r in a)a[r]instanceof HTMLElement&&(o[r]=a[r]);n=new CustomEvent(s,{detail:o})}catch(t){console.warn(t)}i.dispatchEvent(n)}}}}(this)),this.isFirefox="undefined"!=typeof InstallTrigger,this.isIE=window.document.documentMode,this.applySettings(e,i||{}),this.state={inputText:"",editing:!1,actions:{},mixMode:{},dropdown:{},flaggedTags:{}},this.value=[],this.listeners={},this.DOM={},this.build(e),this.getCSSVars(),this.loadOriginalValues(),this.events.customBinding.call(this),this.events.binding.call(this),void(e.autofocus&&this.DOM.input.focus())):(console.warn("Tagify: ","input element not found",e),this)}return f.prototype={dropdown:g,TEXTS:{empty:"empty",exceed:"number of tags exceeded",pattern:"pattern mismatch",duplicate:"already exists",notAllowed:"not allowed"},customEventsList:["change","add","remove","invalid","input","click","keydown","focus","blur","edit:input","edit:updated","edit:start","edit:keydown","dropdown:show","dropdown:hide","dropdown:select","dropdown:updated","dropdown:noMatch"],trim:function(t){return this.settings.trim&&t&&"string"==typeof t?t.trim():t},parseHTML:function(t){return(new DOMParser).parseFromString(t.trim(),"text/html").body.firstElementChild},templates:{wrapper:function(t,e){return'\n \n ')},tag:function(t){return'\n \n
\n ').concat(t[this.settings.tagTextProp]||t.value,"\n
\n
")},dropdown:function(t){var e=t.dropdown,i="manual"==e.position,s="".concat(t.classNames.dropdown);return'
\n
\n
')},dropdownItem:function(t){return"
').concat(t.value,"
")},dropdownItemNoMatch:null},parseTemplate:function(t,e){return t=this.settings.templates[t]||t,this.parseHTML(t.apply(this,e))},applySettings:function(t,e){u.templates=this.templates;var i=this.settings=d({},u,e);i.readonly=t.hasAttribute("readonly"),i.placeholder=t.getAttribute("placeholder")||i.placeholder||"",i.required=t.hasAttribute("required");var s=function(t){Object.defineProperty(i.classNames,t+"Selector",{get:function(){return"."+this[t].split(" ").join(".")}})};for(var a in i.classNames)s(a);if(this.isIE&&(i.autoComplete=!1),["whitelist","blacklist"].forEach((function(e){var s=t.getAttribute("data-"+e);s&&(s=s.split(i.delimiters))instanceof Array&&(i[e]=s)})),"autoComplete"in e&&!l(e.autoComplete)&&(i.autoComplete=u.autoComplete,i.autoComplete.enabled=e.autoComplete),"mix"==i.mode&&(i.autoComplete.rightKey=!0,i.delimiters=e.delimiters||null,i.tagTextProp&&!i.dropdown.searchKeys.includes(i.tagTextProp)&&i.dropdown.searchKeys.push(i.tagTextProp)),t.pattern)try{i.pattern=new RegExp(t.pattern)}catch(t){}if(this.settings.delimiters)try{i.delimiters=new RegExp(this.settings.delimiters,"g")}catch(t){}"select"==i.mode&&(i.dropdown.enabled=0),i.dropdown.appendTarget=e.dropdown&&e.dropdown.appendTarget?e.dropdown.appendTarget:document.body},getAttributes:function(t){if("[object Object]"!=Object.prototype.toString.call(t))return"";var e,i,s=Object.keys(t),a="";for(i=s.length;i--;)"class"!=(e=s[i])&&t.hasOwnProperty(e)&&void 0!==t[e]&&(a+=" "+e+(void 0!==t[e]?'="'.concat(t[e],'"'):""));return a},setStateSelection:function(){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},getCaretGlobalPosition:function(){var t=document.getSelection();if(t.rangeCount){var e,i,s=t.getRangeAt(0),a=s.startContainer,n=s.startOffset;if(n>0)return(i=document.createRange()).setStart(a,n-1),i.setEnd(a,n),{left:(e=i.getBoundingClientRect()).right,top:e.top,bottom:e.bottom};if(a.getBoundingClientRect)return a.getBoundingClientRect()}return{left:-9999,top:-9999}},getCSSVars:function(){var t,e=getComputedStyle(this.DOM.scope,null);this.CSSVars={tagHideTransition:function(t){var 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((function(t){return t})).pop().trim();return{value:+t.split(e).filter((function(t){return t}))[0].trim(),unit:e}}((t="tag-hide-transition",e.getPropertyValue("--"+t))))}},build:function(t){var e=this.DOM;this.settings.mixMode.integrated?(e.originalInput=null,e.scope=t,e.input=t):(e.originalInput=t,e.scope=this.parseTemplate("wrapper",[t,this.settings]),e.input=e.scope.querySelector(this.settings.classNames.inputSelector),t.parentNode.insertBefore(e.scope,t)),this.settings.dropdown.enabled>=0&&this.dropdown.init.call(this)},destroy:function(){this.DOM.scope.parentNode.removeChild(this.DOM.scope),this.dropdown.hide.call(this,!0),clearTimeout(this.dropdownHide__bindEventsTimeout)},loadOriginalValues:function(t){var e,i=this.settings;if(t=t||(i.mixMode.integrated?this.DOM.input.textContent:this.DOM.originalInput.value))if(this.removeAllTags({withoutChangeEvent:!0}),"mix"==i.mode)this.parseMixTags(t.trim()),(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).forEach((function(t){return t&&t.classList.add(i.classNames.tagNoAnimation)}))}else this.postUpdate();this.state.lastOriginalValueReported=i.mixMode.integrated?"":this.DOM.originalInput.value,this.state.loadedOriginalValues=!0},cloneEvent:function(t){var e={};for(var i in t)e[i]=t[i];return e},loading:function(t){return this.state.isLoading=t,this.DOM.scope.classList[t?"add":"remove"](this.settings.classNames.scopeLoading),this},tagLoading:function(t,e){return t&&t.classList[e?"add":"remove"](this.settings.classNames.tagLoading),this},toggleClass:function(t,e){"string"==typeof t&&this.DOM.scope.classList.toggle(t,e)},toggleFocusClass:function(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:p,fixFirefoxLastTagNoCaret:function(){},placeCaretAfterNode:function(t){if(t&&t.parentNode){var e=t.nextSibling,i=window.getSelection(),s=i.getRangeAt(0);i.rangeCount&&(s.setStartBefore(e||t),s.setEndBefore(e||t),i.removeAllRanges(),i.addRange(s))}},insertAfterTag:function(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},editTag:function(t,e){var i=this;t=t||this.getLastTag(),e=e||{},this.dropdown.hide.call(this);var s=this.settings;function a(){return t.querySelector(s.classNames.tagTextSelector)}var n=a(),o=this.getNodeIndex(t),r=this.tagData(t),l=this.events.callbacks,c=this,h=!0;if(n){if(!(r instanceof Object&&"editable"in r)||r.editable)return n.setAttribute("contenteditable",!0),t.classList.add(s.classNames.tagEditing),this.tagData(t,{__originalData:d({},r),__originalHTML:t.innerHTML}),n.addEventListener("focus",l.onEditTagFocus.bind(this,t)),n.addEventListener("blur",(function(){setTimeout((function(){return l.onEditTagBlur.call(c,a())}))})),n.addEventListener("input",l.onEditTagInput.bind(this,n)),n.addEventListener("keydown",(function(e){return l.onEditTagkeydown.call(i,e,t)})),n.focus(),this.setRangeAtStartEnd(!1,n),e.skipValidation||(h=this.editTagToggleValidity(t,r.value)),n.originalIsValid=h,this.trigger("edit:start",{tag:t,index:o,data:r,isValid:h}),this}else console.warn("Cannot find element in Tag template: .",s.classNames.tagTextSelector)},editTagToggleValidity:function(t,e){var i,s=this.tagData(t);if(s)return i=!(!s.__isValid||1==s.__isValid),t.classList.toggle(this.settings.classNames.tagInvalid,i),s.__isValid;console.warn("tag has no data: ",t,s)},onEditTagDone:function(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),this.state.editing=!1,delete e.__originalData,delete e.__originalHTML,t&&e[this.settings.tagTextProp]?(this.editTagToggleValidity(t),this.replaceTag(t,e)):t&&this.removeTags(t),this.trigger("edit:updated",i),this.dropdown.hide.call(this),this.settings.keepInvalidTags&&this.reCheckInvalidTags()},replaceTag:function(t,e){e&&e.value||(e=t.__tagifyTagData),e.__isValid&&1!=e.__isValid&&d(e,this.getInvalidTagAttrs(e,e.__isValid));var i=this.createTagElem(e);t.parentNode.replaceChild(i,t),this.updateValueByDOMTags()},updateValueByDOMTags:function(){var t=this;this.value.length=0,[].forEach.call(this.getTagElms(),(function(e){e.classList.contains(t.settings.classNames.tagNotAllowed.split(" ")[0])||t.value.push(t.tagData(e))})),this.update()},setRangeAtStartEnd:function(t,e){t="number"==typeof t?t:!!t,e=(e=e||this.DOM.input).lastChild||e;var i=document.getSelection();try{i.rangeCount>=1&&["Start","End"].forEach((function(s){return i.getRangeAt(0)["set"+s](e,t||e.length)}))}catch(t){console.warn("Tagify: ",t)}},injectAtCaret:function(t,e){if(e=e||this.state.selection.range)return"string"==typeof t&&(t=document.createTextNode(t)),e.deleteContents(),e.insertNode(t),this.setRangeAtStartEnd(!1,t),this.updateValueByDOMTags(),this.update(),this},input:{set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=this.settings.dropdown.closeOnSelect;this.state.inputText=t,e&&(this.DOM.input.innerHTML=o(""+t)),!t&&i&&this.dropdown.hide.bind(this),this.input.autocomplete.suggest.call(this),this.input.validate.call(this)},validate:function(){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:function(t){var e=t||this.DOM.input,i=[];e.childNodes.forEach((function(t){return 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.settings.trim&&(i=i.replace(/^\s+/,"")),i},autocomplete:{suggest:function(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:function(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()),this.input.autocomplete.suggest.call(this),this.dropdown.hide.call(this),!0)}}},getTagIdx:function(t){return this.value.findIndex((function(e){return e.value==t.value}))},getNodeIndex:function(t){var e=0;if(t)for(;t=t.previousElementSibling;)e++;return e},getTagElms:function(){for(var t=arguments.length,e=new Array(t),s=0;s=this.settings.maxTags&&this.TEXTS.exceed},setReadonly:function(t){var e=this.settings;document.activeElement.blur(),e.readonly=t,this.DOM.scope[(t?"set":"remove")+"Attribute"]("readonly",!0),"mix"==e.mode&&(this.DOM.input.contentEditable=!t)},normalizeTags:function(t){var s=this,a=this.settings,n=a.whitelist,o=a.delimiters,r=a.mode,l=a.tagTextProp,d=a.enforceWhitelist,c=[],h=!!n&&n[0]instanceof Object,g=t instanceof Array,u=function(t){return(t+"").split(o).filter((function(t){return t})).map((function(t){var i;return e(i={},l,s.trim(t)),e(i,"value",s.trim(t)),i}))};if("number"==typeof t&&(t=t.toString()),"string"==typeof t){if(!t.trim())return[];t=u(t)}else if(g){var p;t=(p=[]).concat.apply(p,i(t.map((function(t){return t.value?t:u(t)}))))}return h&&(t.forEach((function(t){var e=c.map((function(t){return t.value})),i=s.dropdown.filterListItems.call(s,t[l],{exact:!0}).filter((function(t){return!e.includes(t.value)})),a=i.length>1?s.getWhitelistItem(t[l],l,i):i[0];a&&a instanceof Object?c.push(a):"mix"==r||d||(null==t.value&&(t.value=t[l]),c.push(t))})),t=c),t},parseMixTags:function(t){var e=this,i=this.settings,s=i.mixTagsInterpolator,a=i.duplicates,n=i.transformTag,o=i.enforceWhitelist,r=i.maxTags,l=i.tagTextProp,d=[];return t=t.split(s[0]).map((function(t,i){var c,h,g,u=t.split(s[1]),p=u[0],f=d.length==r;try{if(p==+p)throw Error;h=JSON.parse(p)}catch(t){h=e.normalizeTags(p)[0]||{value:p}}if(f||!(u.length>1)||o&&!e.isTagWhitelisted(h.value)||!a&&e.isTagDuplicate(h.value)){if(t)return i?s[0]+t:t}else n.call(e,h),h[c=h[l]?l:"value"]=e.trim(h[c]),g=e.createTagElem(h),d.push(h),g.classList.add(e.settings.classNames.tagNoAnimation),u[0]=g.outerHTML,e.value.push(h);return u.join("")})).join(""),this.DOM.input.innerHTML=t,this.DOM.input.appendChild(document.createTextNode("")),this.DOM.input.normalize(),this.getTagElms().forEach((function(t,i){return e.tagData(t,d[i])})),this.update({withoutChangeEvent:!0}),t},replaceTextWithNode:function(t,e){if(this.state.tag||e){e=e||this.state.tag.prefix+this.state.tag.value;var i,s,a=window.getSelection(),n=a.anchorNode,o=this.state.tag.delimiters?this.state.tag.delimiters.length:0;return n.splitText(a.anchorOffset-o),i=n.nodeValue.lastIndexOf(e),s=n.splitText(i),t&&n.parentNode.replaceChild(t,s),!0}},selectTag:function(t,e){if(!this.settings.enforceWhitelist||this.isTagWhitelisted(e.value))return this.input.set.call(this,e.value,!0),this.state.actions.selectOption&&setTimeout(this.setRangeAtStartEnd.bind(this)),this.getLastTag()?this.replaceTag(this.getLastTag(),e):this.appendTag(t),this.value[0]=e,this.trigger("add",{tag:t,data:e}),this.update(),[t]},addEmptyTag:function(t){var e=d({value:""},t||{}),i=this.createTagElem(e);this.tagData(i,e),this.appendTag(i),this.editTag(i,{skipValidation:!0})},addTags:function(t,e){var i=this,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.settings.skipInvalid,a=[],n=this.settings;return t&&0!=t.length?(t=this.normalizeTags(t),"mix"==n.mode?this.addMixTags(t):("select"==n.mode&&(e=!1),this.DOM.input.removeAttribute("style"),t.forEach((function(t){var e,o={},r=Object.assign({},t,{value:t.value+""});if((t=Object.assign({},r)).__isValid=i.hasMaxTags()||i.validateTag(t),n.transformTag.call(i,t),!0!==t.__isValid){if(s)return;d(o,i.getInvalidTagAttrs(t,t.__isValid),{__preInvalidData:r}),t.__isValid==i.TEXTS.duplicate&&i.flashTag(i.getTagElmByValue(t.value))}if(t.readonly&&(o["aria-readonly"]=!0),e=i.createTagElem(d({},t,o)),a.push(e),"select"==n.mode)return i.selectTag(e,t);i.appendTag(e),t.__isValid&&!0===t.__isValid?(i.value.push(t),i.update(),i.trigger("add",{tag:e,index:i.value.length-1,data:t})):(i.trigger("invalid",{data:t,index:i.value.length,tag:e,message:t.__isValid}),n.keepInvalidTags||setTimeout((function(){return i.removeTags(e,!0)}),1e3)),i.dropdown.position.call(i)})),t.length&&e&&this.input.set.call(this),this.dropdown.refilter.call(this),a)):("select"==n.mode&&this.removeAllTags(),a)},addMixTags:function(t){var e=this;if(t[0].prefix||this.state.tag)this.prefixedTextToTag(t[0]);else{"string"==typeof t&&(t=[{value:t}]);var i=!!this.state.selection,s=document.createDocumentFragment();t.forEach((function(t){var i=e.createTagElem(t);s.appendChild(i),e.insertAfterTag(i)})),i?this.injectAtCaret(s):(this.DOM.input.focus(),(i=this.setStateSelection()).range.setStart(this.DOM.input,i.range.endOffset),i.range.setEnd(this.DOM.input,i.range.endOffset),this.DOM.input.appendChild(s),this.updateValueByDOMTags(),this.update())}},prefixedTextToTag:function(t){var e,i=this,s=this.settings,a=this.state.tag.delimiters;if(s.transformTag.call(this,t),t.prefix=t.prefix||this.state.tag?this.state.tag.prefix:(s.pattern.source||s.pattern)[0],e=this.createTagElem(t),this.replaceTextWithNode(e)||this.DOM.input.appendChild(e),setTimeout((function(){return e.classList.add(i.settings.classNames.tagNoAnimation)}),300),this.value.push(t),this.update(),!a){var n=this.insertAfterTag(e)||e;this.placeCaretAfterNode(n)}return this.state.tag=null,this.trigger("add",d({},{tag:e},{data:t})),e},appendTag:function(t){var e=this.DOM.scope.lastElementChild;e===this.DOM.input?this.DOM.scope.insertBefore(t,e):this.DOM.scope.appendChild(t)},createTagElem:function(t){var e,i=d({},t,{value:o(t.value+"")});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)}(e=this.parseTemplate("tag",[i])),this.tagData(e,t),e},reCheckInvalidTags:function(){var t=this,e=this.settings,i="".concat(e.classNames.tagSelector).concat(e.classNames.tagNotAllowedSelector),s=this.DOM.scope.querySelectorAll(i);[].forEach.call(s,(function(e){var i=t.tagData(e),s=e.getAttribute("title")==t.TEXTS.duplicate,a=!0===t.validateTag(i);s&&a&&(i=i.__preInvalidData?i.__preInvalidData:{value:i.value},t.replaceTag(e,i))}))},removeTags:function(t,e,i){var s,a=this;t=t&&t instanceof HTMLElement?[t]:t instanceof Array?t:t?[t]:[this.getLastTag()],s=t.reduce((function(t,e){return e&&"string"==typeof e&&(e=a.getTagElmByValue(e)),e&&t.push({node:e,idx:a.getTagIdx(a.tagData(e)),data:a.tagData(e,{__removed:!0})}),t}),[]),i="number"==typeof i?i:this.CSSVars.tagHideTransition,"select"==this.settings.mode&&(i=0,this.input.set.call(this)),1==s.length&&s[0].node.classList.contains(this.settings.classNames.tagNotAllowed)&&(e=!0),s.length&&this.settings.hooks.beforeRemoveTag(s,{tagify:this}).then((function(){function t(t){t.node.parentNode&&(t.node.parentNode.removeChild(t.node),e?this.settings.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.call(this),this.dropdown.position.call(this),this.DOM.input.normalize(),this.settings.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(this.settings.classNames.tagHide),setTimeout(t.bind(this),i,e)}.call(a,s[0]):s.forEach(t.bind(a)),e||(s.forEach((function(t){var e=Object.assign({},t.data);delete e.__removed;var i=a.getTagIdx(e);i>-1&&a.value.splice(i,1)})),a.update())})).catch((function(t){}))},removeAllTags:function(t){var e=t.withoutChangeEvent;this.value=[],"mix"==this.settings.mode?this.DOM.input.innerHTML="":Array.prototype.slice.call(this.getTagElms()).forEach((function(t){return t.parentNode.removeChild(t)})),this.dropdown.position.call(this),"select"==this.settings.mode&&this.input.set.call(this),this.update({withoutChangeEvent:e})},postUpdate:function(){var t=this.settings.classNames,e="mix"==this.settings.mode?this.settings.mixMode.integrated?this.DOM.input.textContent:this.DOM.originalInput.value:this.value.length;this.toggleClass(t.hasMaxTags,this.value.length>=this.settings.maxTags),this.toggleClass(t.hasNoTags,!this.value.length),this.toggleClass(t.empty,!e)},update:function(t){var e,i,s=this.DOM.originalInput,a=(t||{}).withoutChangeEvent,n=(e=this.value,i=["__isValid","__removed"],e.map((function(t){var e={};for(var s in t)i.indexOf(s)<0&&(e[s]=t[s]);return e})));this.settings.mixMode.integrated||(s.value="mix"==this.settings.mode?this.getMixedTagsAsString(n):n.length?this.settings.originalInputValueFormat?this.settings.originalInputValueFormat(n):JSON.stringify(n):""),this.postUpdate(),!a&&this.state.loadedOriginalValues&&this.triggerChangeEvent()},getMixedTagsAsString:function(){var t="",e=this,i=this.settings.mixTagsInterpolator;return function s(a){a.childNodes.forEach((function(a){if(1==a.nodeType){if(a.classList.contains(e.settings.classNames.tag)&&e.tagData(a)){if(e.tagData(a).__removed)return;return void(t+=i[0]+JSON.stringify(a.__tagifyTagData)+i[1])}"BR"!=a.tagName||a.parentNode!=e.DOM.input&&1!=a.parentNode.childNodes.length?"DIV"!=a.tagName&&"P"!=a.tagName||(t+="\r\n",s(a)):t+="\r\n"}else t+=a.textContent}))}(this.DOM.input),t}},f.prototype.removeTag=f.prototype.removeTags,f})); diff --git a/dist/tagify.polyfills.min.js b/dist/tagify.polyfills.min.js index 88ebf173..4de06f53 100644 --- a/dist/tagify.polyfills.min.js +++ b/dist/tagify.polyfills.min.js @@ -1,782 +1,3 @@ -(function (factory) { - typeof define === 'function' && define.amd ? define(factory) : - factory(); -}((function () { 'use strict'; - - // 1. String.prototype.trim polyfill - if (!"".trim) String.prototype.trim = function () { - return this.replace(/^[\s]+|[\s]+$/g, ''); - }; - - if (window.NodeList && !NodeList.prototype.forEach) { - NodeList.prototype.forEach = Array.prototype.forEach; - } - - if (!Array.prototype.findIndex) { - Object.defineProperty(Array.prototype, 'findIndex', { - value: function value(predicate) { - if (this == null) throw new TypeError('"this" is null or not defined'); - var o = Object(this), - len = o.length >>> 0; - - if (typeof predicate !== 'function') { - throw new TypeError('predicate must be a function'); - } - - var thisArg = arguments[1], - k = 0; - - while (k < len) { - var kValue = o[k]; - - if (predicate.call(thisArg, kValue, k, o)) { - return k; - } - - k++; - } - - return -1; - }, - configurable: true, - writable: true - }); - } - - if (!Array.prototype.includes) { - Array.prototype.includes = function (search) { - return !!~this.indexOf(search); - }; - } - - // Production steps of ECMA-262, Edition 5, 15.4.4.17 - // Reference: http://es5.github.io/#x15.4.4.17 - if (!Array.prototype.some) { - Array.prototype.some = function (fun, thisArg) { - - if (this == null) { - throw new TypeError('Array.prototype.some called on null or undefined'); - } - - if (typeof fun !== 'function') { - throw new TypeError(); - } - - var t = Object(this); - var len = t.length >>> 0; - - for (var i = 0; i < len; i++) { - if (i in t && fun.call(thisArg, t[i], i, t)) { - return true; - } - } - - return false; - }; - } - - if (!String.prototype.includes) { - String.prototype.includes = function (search, start) { - if (typeof start !== 'number') start = 0; - if (start + search.length > this.length) return false;else return this.indexOf(search, start) !== -1; - }; - } - - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill - // - if (typeof Object.assign != 'function') { - // Must be writable: true, enumerable: false, configurable: true - Object.defineProperty(Object, "assign", { - value: function assign(target, varArgs) { - // .length of function is 2 - if (target == null) { - // TypeError if undefined or null - throw new TypeError('Cannot convert undefined or null to object'); - } - - var to = Object(target); - - for (var index = 1; index < arguments.length; index++) { - var nextSource = arguments[index]; - - if (nextSource != null) { - // Skip over if undefined or null - for (var nextKey in nextSource) { - // Avoid bugs when hasOwnProperty is shadowed - if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { - to[nextKey] = nextSource[nextKey]; - } - } - } - } - - return to; - }, - writable: true, - configurable: true - }); - } - - // https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill - function CustomEventPolyfill(event, params) { - params = params || { - bubbles: false, - cancelable: false, - detail: undefined - }; - var evt = document.createEvent('CustomEvent'); - evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); - return evt; - } - - CustomEventPolyfill.prototype = window.Event.prototype; - - if (typeof window.CustomEvent !== "function") { - window.CustomEvent = CustomEventPolyfill; - } - - // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest - if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; - - if (!Element.prototype.closest) { - Element.prototype.closest = function (s) { - var el = this; - if (!document.documentElement.contains(el)) return null; - - do { - if (el.matches(s)) return el; - el = el.parentElement || el.parentNode; - } while (el !== null && el.nodeType === 1); - - return null; - }; - } - - // Avoid transformation text to link ie contentEditable mode - // https://stackoverflow.com/q/7556007/104380 - document.execCommand("AutoUrlDetect", false, false); - - /* - * classList.js: Cross-browser full element.classList implementation. - * 1.2.20171210 - * - * By Eli Grey, http://eligrey.com - * License: Dedicated to the public domain. - * See https://github.com/eligrey/classList.js/blob/master/LICENSE.md - */ - - /*global self, document, DOMException */ - - /*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js */ - if ("document" in self) { - // Full polyfill for browsers with no classList support - // Including IE < Edge missing SVGElement.classList - if (!("classList" in document.createElement("_")) || document.createElementNS && !("classList" in document.createElementNS("http://www.w3.org/2000/svg", "g"))) { - (function (view) { - - if (!('Element' in view)) return; - - var classListProp = "classList", - protoProp = "prototype", - elemCtrProto = view.Element[protoProp], - objCtr = Object, - strTrim = String[protoProp].trim || function () { - return this.replace(/^\s+|\s+$/g, ""); - }, - arrIndexOf = Array[protoProp].indexOf || function (item) { - var i = 0, - len = this.length; - - for (; i < len; i++) { - if (i in this && this[i] === item) { - return i; - } - } - - return -1; - } // Vendors: please allow content code to instantiate DOMExceptions - , - DOMEx = function DOMEx(type, message) { - this.name = type; - this.code = DOMException[type]; - this.message = message; - }, - checkTokenAndGetIndex = function checkTokenAndGetIndex(classList, token) { - if (token === "") { - throw new DOMEx("SYNTAX_ERR", "The token must not be empty."); - } - - if (/\s/.test(token)) { - throw new DOMEx("INVALID_CHARACTER_ERR", "The token must not contain space characters."); - } - - return arrIndexOf.call(classList, token); - }, - ClassList = function ClassList(elem) { - var trimmedClasses = strTrim.call(elem.getAttribute("class") || ""), - classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [], - i = 0, - len = classes.length; - - for (; i < len; i++) { - this.push(classes[i]); - } - - this._updateClassName = function () { - elem.setAttribute("class", this.toString()); - }; - }, - classListProto = ClassList[protoProp] = [], - classListGetter = function classListGetter() { - return new ClassList(this); - }; // Most DOMException implementations don't allow calling DOMException's toString() - // on non-DOMExceptions. Error's toString() is sufficient here. - - - DOMEx[protoProp] = Error[protoProp]; - - classListProto.item = function (i) { - return this[i] || null; - }; - - classListProto.contains = function (token) { - return ~checkTokenAndGetIndex(this, token + ""); - }; - - classListProto.add = function () { - var tokens = arguments, - i = 0, - l = tokens.length, - token, - updated = false; - - do { - token = tokens[i] + ""; - - if (!~checkTokenAndGetIndex(this, token)) { - this.push(token); - updated = true; - } - } while (++i < l); - - if (updated) { - this._updateClassName(); - } - }; - - classListProto.remove = function () { - var tokens = arguments, - i = 0, - l = tokens.length, - token, - updated = false, - index; - - do { - token = tokens[i] + ""; - index = checkTokenAndGetIndex(this, token); - - while (~index) { - this.splice(index, 1); - updated = true; - index = checkTokenAndGetIndex(this, token); - } - } while (++i < l); - - if (updated) { - this._updateClassName(); - } - }; - - classListProto.toggle = function (token, force) { - var result = this.contains(token), - method = result ? force !== true && "remove" : force !== false && "add"; - - if (method) { - this[method](token); - } - - if (force === true || force === false) { - return force; - } else { - return !result; - } - }; - - classListProto.replace = function (token, replacement_token) { - var index = checkTokenAndGetIndex(token + ""); - - if (~index) { - this.splice(index, 1, replacement_token); - - this._updateClassName(); - } - }; - - classListProto.toString = function () { - return this.join(" "); - }; - - if (objCtr.defineProperty) { - var classListPropDesc = { - get: classListGetter, - enumerable: true, - configurable: true - }; - - try { - objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); - } catch (ex) { - // IE 8 doesn't support enumerable:true - // adding undefined to fight this issue https://github.com/eligrey/classList.js/issues/36 - // modernie IE8-MSW7 machine has IE8 8.0.6001.18702 and is affected - if (ex.number === undefined || ex.number === -0x7FF5EC54) { - classListPropDesc.enumerable = false; - objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); - } - } - } else if (objCtr[protoProp].__defineGetter__) { - elemCtrProto.__defineGetter__(classListProp, classListGetter); - } - })(self); - } // There is full or partial native classList support, so just check if we need - // to normalize the add/remove and toggle APIs. - - - (function () { - - var testElement = document.createElement("_"); - testElement.classList.add("c1", "c2"); // Polyfill for IE 10/11 and Firefox <26, where classList.add and - // classList.remove exist but support only one argument at a time. - - if (!testElement.classList.contains("c2")) { - var createMethod = function createMethod(method) { - var original = DOMTokenList.prototype[method]; - - DOMTokenList.prototype[method] = function (token) { - var i, - len = arguments.length; - - for (i = 0; i < len; i++) { - token = arguments[i]; - original.call(this, token); - } - }; - }; - - createMethod('add'); - createMethod('remove'); - } - - testElement.classList.toggle("c3", false); // Polyfill for IE 10 and Firefox <24, where classList.toggle does not - // support the second argument. - - if (testElement.classList.contains("c3")) { - var _toggle = DOMTokenList.prototype.toggle; - - DOMTokenList.prototype.toggle = function (token, force) { - if (1 in arguments && !this.contains(token) === !force) { - return force; - } else { - return _toggle.call(this, token); - } - }; - } // replace() polyfill - - - if (!("replace" in document.createElement("_").classList)) { - DOMTokenList.prototype.replace = function (token, replacement_token) { - var tokens = this.toString().split(" "), - index = tokens.indexOf(token + ""); - - if (~index) { - tokens = tokens.slice(index); - this.remove.apply(this, tokens); - this.add(replacement_token); - this.add.apply(this, tokens.slice(1)); - } - }; - } - - testElement = null; - })(); - } - - function _typeof(obj) { - if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { - _typeof = function (obj) { - return typeof obj; - }; - } else { - _typeof = function (obj) { - return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; - }; - } - - return _typeof(obj); - } - - // https://github.com/taylorhakes/promise-polyfill - (function (global, factory) { - (typeof exports === "undefined" ? "undefined" : _typeof(exports)) === 'object' && typeof module !== 'undefined' ? factory() : typeof define === 'function' && define.amd ? define(factory) : factory(); - })(undefined, function () { - /** - * @this {Promise} - */ - - function finallyConstructor(callback) { - var constructor = this.constructor; - return this.then(function (value) { - // @ts-ignore - return constructor.resolve(callback()).then(function () { - return value; - }); - }, function (reason) { - // @ts-ignore - return constructor.resolve(callback()).then(function () { - // @ts-ignore - return constructor.reject(reason); - }); - }); - } - - function allSettled(arr) { - var P = this; - return new P(function (resolve, reject) { - if (!(arr && typeof arr.length !== 'undefined')) { - return reject(new TypeError(_typeof(arr) + ' ' + arr + ' is not iterable(cannot read property Symbol(Symbol.iterator))')); - } - - var args = Array.prototype.slice.call(arr); - if (args.length === 0) return resolve([]); - var remaining = args.length; - - function res(i, val) { - if (val && (_typeof(val) === 'object' || typeof val === 'function')) { - var then = val.then; - - if (typeof then === 'function') { - then.call(val, function (val) { - res(i, val); - }, function (e) { - args[i] = { - status: 'rejected', - reason: e - }; - - if (--remaining === 0) { - resolve(args); - } - }); - return; - } - } - - args[i] = { - status: 'fulfilled', - value: val - }; - - if (--remaining === 0) { - resolve(args); - } - } - - for (var i = 0; i < args.length; i++) { - res(i, args[i]); - } - }); - } // Store setTimeout reference so promise-polyfill will be unaffected by - // other code modifying setTimeout (like sinon.useFakeTimers()) - - - var setTimeoutFunc = setTimeout; - - function isArray(x) { - return Boolean(x && typeof x.length !== 'undefined'); - } - - function noop() {} // Polyfill for Function.prototype.bind - - - function bind(fn, thisArg) { - return function () { - fn.apply(thisArg, arguments); - }; - } - /** - * @constructor - * @param {Function} fn - */ - - - function Promise(fn) { - if (!(this instanceof Promise)) throw new TypeError('Promises must be constructed via new'); - if (typeof fn !== 'function') throw new TypeError('not a function'); - /** @type {!number} */ - - this._state = 0; - /** @type {!boolean} */ - - this._handled = false; - /** @type {Promise|undefined} */ - - this._value = undefined; - /** @type {!Array} */ - - this._deferreds = []; - doResolve(fn, this); - } - - function handle(self, deferred) { - while (self._state === 3) { - self = self._value; - } - - if (self._state === 0) { - self._deferreds.push(deferred); - - return; - } - - self._handled = true; - - Promise._immediateFn(function () { - var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; - - if (cb === null) { - (self._state === 1 ? resolve : reject)(deferred.promise, self._value); - return; - } - - var ret; - - try { - ret = cb(self._value); - } catch (e) { - reject(deferred.promise, e); - return; - } - - resolve(deferred.promise, ret); - }); - } - - function resolve(self, newValue) { - try { - // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure - if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.'); - - if (newValue && (_typeof(newValue) === 'object' || typeof newValue === 'function')) { - var then = newValue.then; - - if (newValue instanceof Promise) { - self._state = 3; - self._value = newValue; - finale(self); - return; - } else if (typeof then === 'function') { - doResolve(bind(then, newValue), self); - return; - } - } - - self._state = 1; - self._value = newValue; - finale(self); - } catch (e) { - reject(self, e); - } - } - - function reject(self, newValue) { - self._state = 2; - self._value = newValue; - finale(self); - } - - function finale(self) { - if (self._state === 2 && self._deferreds.length === 0) { - Promise._immediateFn(function () { - if (!self._handled) { - Promise._unhandledRejectionFn(self._value); - } - }); - } - - for (var i = 0, len = self._deferreds.length; i < len; i++) { - handle(self, self._deferreds[i]); - } - - self._deferreds = null; - } - /** - * @constructor - */ - - - function Handler(onFulfilled, onRejected, promise) { - this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; - this.onRejected = typeof onRejected === 'function' ? onRejected : null; - this.promise = promise; - } - /** - * Take a potentially misbehaving resolver function and make sure - * onFulfilled and onRejected are only called once. - * - * Makes no guarantees about asynchrony. - */ - - - function doResolve(fn, self) { - var done = false; - - try { - fn(function (value) { - if (done) return; - done = true; - resolve(self, value); - }, function (reason) { - if (done) return; - done = true; - reject(self, reason); - }); - } catch (ex) { - if (done) return; - done = true; - reject(self, ex); - } - } - - Promise.prototype['catch'] = function (onRejected) { - return this.then(null, onRejected); - }; - - Promise.prototype.then = function (onFulfilled, onRejected) { - // @ts-ignore - var prom = new this.constructor(noop); - handle(this, new Handler(onFulfilled, onRejected, prom)); - return prom; - }; - - Promise.prototype['finally'] = finallyConstructor; - - Promise.all = function (arr) { - return new Promise(function (resolve, reject) { - if (!isArray(arr)) { - return reject(new TypeError('Promise.all accepts an array')); - } - - var args = Array.prototype.slice.call(arr); - if (args.length === 0) return resolve([]); - var remaining = args.length; - - function res(i, val) { - try { - if (val && (_typeof(val) === 'object' || typeof val === 'function')) { - var then = val.then; - - if (typeof then === 'function') { - then.call(val, function (val) { - res(i, val); - }, reject); - return; - } - } - - args[i] = val; - - if (--remaining === 0) { - resolve(args); - } - } catch (ex) { - reject(ex); - } - } - - for (var i = 0; i < args.length; i++) { - res(i, args[i]); - } - }); - }; - - Promise.allSettled = allSettled; - - Promise.resolve = function (value) { - if (value && _typeof(value) === 'object' && value.constructor === Promise) { - return value; - } - - return new Promise(function (resolve) { - resolve(value); - }); - }; - - Promise.reject = function (value) { - return new Promise(function (resolve, reject) { - reject(value); - }); - }; - - Promise.race = function (arr) { - return new Promise(function (resolve, reject) { - if (!isArray(arr)) { - return reject(new TypeError('Promise.race accepts an array')); - } - - for (var i = 0, len = arr.length; i < len; i++) { - Promise.resolve(arr[i]).then(resolve, reject); - } - }); - }; // Use polyfill for setImmediate for performance gains - - - Promise._immediateFn = // @ts-ignore - typeof setImmediate === 'function' && function (fn) { - // @ts-ignore - setImmediate(fn); - } || function (fn) { - setTimeoutFunc(fn, 0); - }; - - Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) { - if (typeof console !== 'undefined' && console) { - console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console - } - }; - /** @suppress {undefinedVars} */ - - - var globalNS = function () { - // the only reliable means to get the global object is - // `Function('return this')()` - // However, this causes CSP violations in Chrome apps. - if (typeof self !== 'undefined') { - return self; - } - - if (typeof window !== 'undefined') { - return window; - } - - if (typeof global !== 'undefined') { - return global; - } - - throw new Error('unable to locate global object'); - }(); // Expose the polyfill if Promise is undefined or set to a - // non-function value. The latter can be due to a named HTMLElement - // being exposed by browsers for legacy reasons. - // https://github.com/taylorhakes/promise-polyfill/issues/114 - - - if (typeof globalNS['Promise'] !== 'function') { - globalNS['Promise'] = Promise; - } else if (!globalNS.Promise.prototype['finally']) { - globalNS.Promise.prototype['finally'] = finallyConstructor; - } else if (!globalNS.Promise.allSettled) { - globalNS.Promise.allSettled = allSettled; - } - }); - -}))); +!function(t){"function"==typeof define&&define.amd?define(t):t()}((function(){"use strict";function t(t,e){e=e||{bubbles:!1,cancelable:!1,detail:void 0};var n=document.createEvent("CustomEvent");return n.initCustomEvent(t,e.bubbles,e.cancelable,e.detail),n}function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var n;"".trim||(String.prototype.trim=function(){return this.replace(/^[\s]+|[\s]+$/g,"")}),window.NodeList&&!NodeList.prototype.forEach&&(NodeList.prototype.forEach=Array.prototype.forEach),Array.prototype.findIndex||Object.defineProperty(Array.prototype,"findIndex",{value:function(t){if(null==this)throw new TypeError('"this" is null or not defined');var e=Object(this),n=e.length>>>0;if("function"!=typeof t)throw new TypeError("predicate must be a function");for(var o=arguments[1],r=0;r>>0,r=0;rthis.length)&&-1!==this.indexOf(t,e)}),"function"!=typeof Object.assign&&Object.defineProperty(Object,"assign",{value:function(t,e){if(null==t)throw new TypeError("Cannot convert undefined or null to object");for(var n=Object(t),o=1;o