@yaireo/tagify
Advanced tools
Comparing version 2.3.0 to 2.5.0
"use strict"; | ||
/** | ||
* Tagify (v 2.2.10)- tags input component | ||
* Tagify (v 2.5.0)- tags input component | ||
* By Yair Even-Or (2016) | ||
@@ -48,2 +48,3 @@ * Don't sell this code. (c) | ||
this.state = {}; | ||
this.value = []; // tags' data | ||
@@ -131,3 +132,3 @@ // events' callbacks references will be stores here, so events could be unbinded | ||
DOM = this.DOM, | ||
template = "<tags class=\"tagify " + input.className + "\" " + (this.settings.readonly ? 'readonly' : '') + ">\n <div contenteditable data-placeholder=\"" + input.placeholder + "\" class=\"tagify__input\"></div>\n </tags>"; | ||
template = "<tags class=\"tagify " + (this.settings.mode ? "tagify--mix" : "") + " " + input.className + "\" " + (this.settings.readonly ? 'readonly' : '') + ">\n <div contenteditable data-placeholder=\"" + input.placeholder + "\" class=\"tagify__input\"></div>\n </tags>"; | ||
DOM.originalInput = input; | ||
@@ -153,2 +154,21 @@ DOM.scope = this.parseHTML(template); | ||
/** | ||
* If the original input had an values, add them as tags | ||
*/ | ||
loadOriginalValues: function loadOriginalValues() { | ||
var value = this.DOM.originalInput.value; // if the original input already had any value (tags) | ||
if (!value) return; | ||
try { | ||
value = JSON.parse(value); | ||
} catch (err) {} | ||
if (this.settings.mode == 'mix') { | ||
this.DOM.input.innerHTML = this.parseMixTags(value); | ||
} else this.addTags(value).forEach(function (tag) { | ||
tag && tag.classList.add('tagify--noAnim'); | ||
}); | ||
}, | ||
/** | ||
* Merge two objects into a new one | ||
@@ -238,3 +258,13 @@ * TEST: extend({}, {a:{foo:1}, b:[]}, {a:{bar:2}, b:[1], c:()=>{}}) | ||
var _CB = this.events.callbacks, | ||
// setup callback references so events could be removed later | ||
_CBR, | ||
action = bindUnbind ? 'addEventListener' : 'removeEventListener'; | ||
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) $(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 || { | ||
@@ -246,4 +276,3 @@ paste: ['input', _CB.onPaste.bind(this)], | ||
click: ['scope', _CB.onClickScope.bind(this)] | ||
}, | ||
action = bindUnbind ? 'addEventListener' : 'removeEventListener'; | ||
}; | ||
@@ -253,9 +282,2 @@ for (var eventName in _CBR) { | ||
} | ||
if (bindUnbind) { | ||
// 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) $(this.DOM.originalInput).on('tagify.removeAllTags', this.removeAllTags.bind(this)); | ||
} | ||
}, | ||
@@ -269,2 +291,3 @@ | ||
var s = e.target.textContent.trim(); | ||
if (this.settings.mode == 'mix') return; | ||
@@ -287,2 +310,3 @@ if (e.type == "focus") { | ||
lastTag; | ||
if (this.settings.mode == 'mix') return; | ||
@@ -305,2 +329,3 @@ if (e.key == 'Backspace' && (s == "" || s.charCodeAt(0) == 8203)) { | ||
showSuggestions = value.length >= this.settings.dropdown.enabled; | ||
if (this.settings.mode == 'mix') return this.events.callbacks.onMixTagsInput.call(this, e); | ||
@@ -325,2 +350,26 @@ if (!value) { | ||
}, | ||
onMixTagsInput: function onMixTagsInput(e) { | ||
var sel, | ||
range, | ||
split, | ||
tag, | ||
patternLen = this.settings.pattern.length; | ||
this.state.tag = null; | ||
if (window.getSelection) { | ||
sel = window.getSelection(); | ||
if (sel.rangeCount > 0) { | ||
range = sel.getRangeAt(0).cloneRange(); | ||
range.collapse(true); | ||
range.setStart(window.getSelection().focusNode, 0); | ||
split = range.toString().split(/,|\.|\s/); // ["foo", "bar", "@a"] | ||
tag = split[split.length - 1]; | ||
tag = this.state.tag = tag.substr(0, patternLen) == this.settings.pattern && tag.length > patternLen ? tag.slice(patternLen) : null; | ||
} | ||
} | ||
this.dropdown[tag ? "show" : "hide"].call(this, tag); | ||
}, | ||
onInputIE: function onInputIE(e) { | ||
@@ -344,19 +393,2 @@ var _this = this; // for the "e.target.textContent" to be changed, the browser requires a small delay | ||
/** | ||
* If the original input had an values, add them as tags | ||
*/ | ||
loadOriginalValues: function loadOriginalValues() { | ||
var value = this.DOM.originalInput.value; // if the original input already had any value (tags) | ||
if (!value) return; | ||
try { | ||
value = JSON.parse(value); | ||
} catch (err) {} | ||
this.addTags(value).forEach(function (tag) { | ||
tag && tag.classList.add('tagify--noAnim'); | ||
}); | ||
}, | ||
/** | ||
* input bridge for accessing & setting | ||
@@ -377,8 +409,10 @@ * @type {Object} | ||
// https://stackoverflow.com/a/3866442/104380 | ||
setRangeAtEnd: function setRangeAtEnd() { | ||
setRangeAtStartEnd: function setRangeAtStartEnd() { | ||
var start = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; | ||
var node = arguments[1]; | ||
var range, selection; | ||
if (!document.createRange) return; | ||
range = document.createRange(); | ||
range.selectNodeContents(this.DOM.input); | ||
range.collapse(false); | ||
range.selectNodeContents(node || this.DOM.input); | ||
range.collapse(start); | ||
selection = window.getSelection(); | ||
@@ -425,3 +459,3 @@ selection.removeAllRanges(); | ||
this.dropdown.hide.call(this); | ||
this.input.setRangeAtEnd.call(this); | ||
this.input.setRangeAtStartEnd.call(this); | ||
} // if( suggestion && this.addTags(this.input.value + suggestion).length ){ | ||
@@ -544,3 +578,3 @@ // this.input.set.call(this); | ||
return tagsItems.split(this.settings.delimiters).filter(function (n) { | ||
tagsItems = tagsItems.split(this.settings.delimiters).filter(function (n) { | ||
return n; | ||
@@ -552,5 +586,3 @@ }).map(function (v) { | ||
}); | ||
} | ||
if (tagsItems instanceof Array) return tagsItems.map(function (v) { | ||
} else if (tagsItems instanceof Array) tagsItems = tagsItems.map(function (v) { | ||
return { | ||
@@ -561,2 +593,3 @@ value: v.trim() | ||
if (whitelistWithProps) { | ||
@@ -569,9 +602,66 @@ tagsItems.forEach(function (tag) { | ||
if (matchObj[0]) temp.push(matchObj[0]); // set the Array (with the found Object) as the new value | ||
else temp.push(tag); | ||
else if (_this3.settings.mode != 'mix') temp.push(tag); | ||
}); | ||
return temp; | ||
} | ||
return tagsItems; | ||
}, | ||
parseMixTags: function parseMixTags(s) { | ||
var _this4 = this; | ||
var htmlString = ''; | ||
s = s.split(this.settings.pattern); // this.DOM.scope.innerHTML | ||
htmlString = s.shift() + s.map(function (part) { | ||
var tagElm, i, normalizedTag; | ||
if (!part) return ''; | ||
for (i in part) { | ||
if (part[i].match(/,|\.| /)) { | ||
normalizedTag = _this4.normalizeTags.call(_this4, part.substr(0, i))[0]; | ||
if (normalizedTag) tagElm = _this4.createTagElem(normalizedTag);else i = 0; // a tag was found but was not in the whitelist, so reset the "i" index | ||
break; | ||
} | ||
} | ||
return tagElm ? tagElm.outerHTML + part.slice(i) : _this4.settings.pattern + part; | ||
}).join(''); | ||
return htmlString; | ||
}, | ||
/** | ||
* Add a tag where it might be beside textNodes | ||
*/ | ||
addMixTag: function addMixTag(tagData) { | ||
if (!tagData) return; | ||
var sel = window.getSelection(), | ||
node = sel.focusNode, | ||
nodeText = node.textContent, | ||
wrapElm = document.createDocumentFragment(), | ||
tagElm = this.createTagElem(tagData), | ||
textNodeBefore, | ||
textNodeAfter, | ||
range, | ||
parrernLen = this.settings.pattern.length; | ||
if (sel.rangeCount > 0) { | ||
range = sel.getRangeAt(0).cloneRange(); | ||
range.collapse(true); | ||
range.setStart(node, 0); | ||
textNodeBefore = range.toString().slice(0, -this.state.tag.length - parrernLen); | ||
textNodeAfter = nodeText.slice(textNodeBefore.length + this.state.tag.length + parrernLen, nodeText.length); | ||
textNodeBefore = document.createTextNode(textNodeBefore); | ||
textNodeAfter = document.createTextNode(textNodeAfter.trim() ? textNodeAfter : " \u200B"); | ||
wrapElm.appendChild(textNodeBefore); | ||
wrapElm.appendChild(tagElm); | ||
wrapElm.appendChild(textNodeAfter); | ||
} | ||
node.parentNode.replaceChild(wrapElm, node); | ||
this.input.setRangeAtStartEnd.call(this, true, textNodeAfter); | ||
}, | ||
/** | ||
* add a "tag" element to the "tags" component | ||
@@ -583,15 +673,16 @@ * @param {String/Array} tagsItems [A string (single or multiple values with a delimiter), or an Array of Objects or just Array of Strings] | ||
addTags: function addTags(tagsItems, clearInput) { | ||
var _this4 = this; | ||
var _this5 = this; | ||
var tagElems = []; | ||
tagsItems = this.normalizeTags.call(this, tagsItems); | ||
if (this.settings.mode == 'mix') return this.addMixTag(tagsItems[0]); | ||
this.DOM.input.removeAttribute('style'); | ||
tagsItems = this.normalizeTags.call(this, tagsItems); | ||
tagsItems.forEach(function (tagData) { | ||
var tagValidation, tagElm; | ||
if (typeof _this4.settings.transformTag === 'function') { | ||
tagData.value = _this4.settings.transformTag.call(_this4, tagData.value) || tagData.value; | ||
if (typeof _this5.settings.transformTag === 'function') { | ||
tagData.value = _this5.settings.transformTag.call(_this5, tagData.value) || tagData.value; | ||
} | ||
tagValidation = _this4.validateTag.call(_this4, tagData.value); | ||
tagValidation = _this5.validateTag.call(_this5, tagData.value); | ||
@@ -602,7 +693,7 @@ if (tagValidation !== true) { | ||
_this4.markTagByValue(tagData.value); | ||
_this5.markTagByValue(tagData.value); | ||
_this4.trigger("invalid", { | ||
_this5.trigger("invalid", { | ||
value: tagData.value, | ||
index: _this4.value.length, | ||
index: _this5.value.length, | ||
message: tagValidation | ||
@@ -613,21 +704,21 @@ }); | ||
tagElm = _this4.createTagElem(tagData); | ||
tagElm = _this5.createTagElem(tagData); | ||
tagElems.push(tagElm); // add the tag to the component's DOM | ||
appendTag.call(_this4, tagElm); | ||
appendTag.call(_this5, tagElm); | ||
if (tagValidation === true) { | ||
// update state | ||
_this4.value.push(tagData); | ||
_this5.value.push(tagData); | ||
_this4.update(); | ||
_this5.update(); | ||
_this4.trigger('add', _this4.extend({}, { | ||
index: _this4.value.length, | ||
_this5.trigger('add', _this5.extend({}, { | ||
index: _this5.value.length, | ||
tag: tagElm | ||
}, tagData)); | ||
} else if (!_this4.settings.keepInvalidTags) { | ||
} else if (!_this5.settings.keepInvalidTags) { | ||
// remove invalid tags (if "keepInvalidTags" is set to "false") | ||
setTimeout(function () { | ||
_this4.removeTag(tagElm, true); | ||
_this5.removeTag(tagElm, true); | ||
}, 1000); | ||
@@ -653,2 +744,5 @@ } | ||
}, | ||
minify: function minify(html) { | ||
return html.replace(new RegExp("\>[\r\n ]+\<", "g"), "><"); | ||
}, | ||
@@ -663,3 +757,3 @@ /** | ||
v = this.escapeHtml(tagData.value), | ||
template = "<tag title='" + v + "'>\n <x title=''></x><div><span>" + v + "</span></div>\n </tag>"; | ||
template = "<tag title='" + v + "' contenteditable='false'>\n <x title=''></x><div><span>" + v + "</span></div>\n </tag>"; | ||
@@ -684,2 +778,3 @@ if (typeof this.settings.tagTemplate === "function") { | ||
template = this.minify(template); | ||
tagElm = this.parseHTML(template); // add any attribuets, if exists | ||
@@ -871,6 +966,6 @@ | ||
onClick: function onClick(e) { | ||
var _this5 = this; | ||
var _this6 = this; | ||
var onClickOutside = function onClickOutside() { | ||
return _this5.dropdown.hide.call(_this5); | ||
return _this6.dropdown.hide.call(_this6); | ||
}, | ||
@@ -887,4 +982,4 @@ listItemElm; | ||
if (listItemElm) { | ||
this.input.set.call(this); | ||
this.addTags(listItemElm.textContent); | ||
this.addTags(listItemElm.textContent, true); | ||
this.dropdown.hide.call(this); | ||
} // clicked outside the dropdown, so just close it | ||
@@ -891,0 +986,0 @@ else onClickOutside(); |
@@ -1,1 +0,1 @@ | ||
"use strict";!function(a){function n(t,e){if(!t)return console.warn("Tagify: ","invalid input element ",t),this;if(this.settings=this.extend({},this.DEFAULTS,e),this.settings.readonly=t.hasAttribute("readonly"),this.isIE&&(this.settings.autoComplete=!1),t.pattern)try{this.settings.pattern=new RegExp(t.pattern)}catch(t){}if(this.settings&&this.settings.delimiters)try{this.settings.delimiters=new RegExp("["+this.settings.delimiters+"]","g")}catch(t){}this.value=[],this.listeners={},this.DOM={},this.extend(this,new this.EventDispatcher(this)),this.build(t),this.loadOriginalValues(),this.events.customBinding.call(this),this.events.binding.call(this)}a.fn.tagify=function(){var i=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};return this.each(function(){var t,e=a(this);if(e.data("tagify"))return this;i.isJQueryPlugin=!0,t=new n(e[0],i),e.data("tagify",t)})},n.prototype={isIE:window.document.documentMode,TEXTS:{empty:"empty",exceed:"number of tags exceeded",pattern:"pattern mismatch",duplicate:"already exists",notAllowed:"not allowed"},DEFAULTS:{delimiters:",",pattern:null,maxTags:1/0,callbacks:{},addTagOnBlur:!0,duplicates:!1,whitelist:[],blacklist:[],enforceWhitelist:!1,keepInvalidTags:!1,autoComplete:!0,dropdown:{classname:"",enabled:2,maxItems:10,itemTemplate:""}},customEventsList:["add","remove","invalid"],parseHTML:function(t){return(new DOMParser).parseFromString(t.trim(),"text/html").body.firstElementChild},escapeHtml:function(t){var e=document.createTextNode(t),i=document.createElement("p");return i.appendChild(e),i.innerHTML},build:function(t){var e=this.DOM,i='<tags class="tagify '+t.className+'" '+(this.settings.readonly?"readonly":"")+'>\n <div contenteditable data-placeholder="'+t.placeholder+'" class="tagify__input"></div>\n </tags>';e.originalInput=t,e.scope=this.parseHTML(i),e.input=e.scope.querySelector("[contenteditable]"),t.parentNode.insertBefore(e.scope,t),0<=this.settings.dropdown.enabled&&this.settings.whitelist.length&&this.dropdown.init.call(this),t.autofocus&&e.input.focus()},destroy:function(){this.DOM.scope.parentNode.removeChild(this.DOM.scope)},extend:function(t,e,i){function n(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 s(t,e){for(var i in e)e.hasOwnProperty(i)&&(n(e[i])?n(t[i])?s(t[i],e[i]):t[i]=Object.assign({},e[i]):t[i]=e[i])}return t instanceof Object||(t={}),s(t,e),i&&s(t,i),t},EventDispatcher:function(n){var s=document.createTextNode("");this.off=function(t,e){return e&&s.removeEventListener.call(s,t,e),this},this.on=function(t,e){return e&&s.addEventListener.call(s,t,e),this},this.trigger=function(t,e){var i;if(t)if(n.settings.isJQueryPlugin)a(n.DOM.originalInput).triggerHandler(t,[e]);else{try{i=new CustomEvent(t,{detail:e})}catch(t){console.warn(t)}s.dispatchEvent(i)}}},events:{customBinding:function(){var e=this;this.customEventsList.forEach(function(t){e.on(t,e.settings.callbacks[t])})},binding:function(){var t=!(0<arguments.length&&void 0!==arguments[0])||arguments[0],e=this.events.callbacks,i=this.listeners.main=this.listeners.main||{paste:["input",e.onPaste.bind(this)],focus:["input",e.onFocusBlur.bind(this)],blur:["input",e.onFocusBlur.bind(this)],keydown:["input",e.onKeydown.bind(this)],click:["scope",e.onClickScope.bind(this)]},n=t?"addEventListener":"removeEventListener";for(var s in i)this.DOM[i[s][0]][n](s,i[s][1]);t&&(this.DOM.input.addEventListener(this.isIE?"keydown":"input",e[this.isIE?"onInputIE":"onInput"].bind(this)),this.settings.isJQueryPlugin&&a(this.DOM.originalInput).on("tagify.removeAllTags",this.removeAllTags.bind(this)))},callbacks:{onFocusBlur:function(t){var e=t.target.textContent.trim();"focus"==t.type?0===this.settings.dropdown.enabled&&this.dropdown.show.call(this):"blur"==t.type&&e?this.settings.addTagOnBlur&&this.addTags(e,!0).length:(this.DOM.input.removeAttribute("style"),this.dropdown.hide.call(this))},onKeydown:function(t){var e,i=t.target.textContent;"Backspace"!=t.key||""!=i&&8203!=i.charCodeAt(0)?"Escape"==t.key||"Esc"==t.key?(this.input.set.call(this),t.target.blur()):"Enter"==t.key?(t.preventDefault(),this.addTags(this.input.value,!0)):"ArrowRight"==t.key&&this.input.autocomplete.set.call(this):(e=(e=this.DOM.scope.querySelectorAll("tag:not(.tagify--hide)"))[e.length-1],this.removeTag(e))},onInput:function(t){var e=this.input.normalize.call(this),i=e.length>=this.settings.dropdown.enabled;e?this.input.value!=e&&(this.input.set.call(this,e,!1),-1!=e.search(this.settings.delimiters)?this.addTags(e).length&&this.input.set.call(this):0<=this.settings.dropdown.enabled&&this.dropdown[i?"show":"hide"].call(this,e)):this.input.set.call(this,"")},onInputIE:function(t){var e=this;setTimeout(function(){e.events.callbacks.onInput.call(e,t)})},onPaste:function(t){},onClickScope:function(t){"TAGS"==t.target.tagName?this.DOM.input.focus():"X"==t.target.tagName&&this.removeTag(t.target.parentNode)}}},loadOriginalValues:function(){var t=this.DOM.originalInput.value;if(t){try{t=JSON.parse(t)}catch(t){}this.addTags(t).forEach(function(t){t&&t.classList.add("tagify--noAnim")})}},input:{value:"",set:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:"",e=!(1<arguments.length&&void 0!==arguments[1])||arguments[1];this.input.value=t,e&&(this.DOM.input.innerHTML=t),t||this.dropdown.hide.call(this),t.length<2&&this.input.autocomplete.suggest.call(this,""),this.input.validate.call(this)},setRangeAtEnd:function(){var t,e;document.createRange&&((t=document.createRange()).selectNodeContents(this.DOM.input),t.collapse(!1),(e=window.getSelection()).removeAllRanges(),e.addRange(t))},validate:function(){var t=!this.input.value||this.validateTag.call(this,this.input.value);this.DOM.input.classList.toggle("tagify__input--invalid",!0!==t)},normalize:function(){for(var t=this.DOM.input.cloneNode(!0),e=t.textContent.replace(/\s/g," ");t.firstElementChild;)e+=t.firstElementChild.textContent,t.removeChild(t.firstElementChild);return e.replace(/^\s+/,"")},autocomplete:{suggest:function(t){t&&this.input.value?this.DOM.input.setAttribute("data-suggest",t.substring(this.input.value.length)):this.DOM.input.removeAttribute("data-suggest")},set:function(t){var e=this.DOM.input.getAttribute("data-suggest"),i=t||(e?this.input.value+e:null);i&&(this.input.set.call(this,i),this.input.autocomplete.suggest.call(this,""),this.dropdown.hide.call(this),this.input.setRangeAtEnd.call(this))}}},getNodeIndex:function(t){for(var e=0;t=t.previousSibling;)3==t.nodeType&&/^\s*$/.test(t.data)||e++;return e},isTagDuplicate:function(e){return this.value.findIndex(function(t){return e.trim().toLowerCase()===t.value.toLowerCase()})},getTagIndexByValue:function(i){var n=[];return this.DOM.scope.querySelectorAll("tag").forEach(function(t,e){t.textContent.trim().toLowerCase()==i.toLowerCase()&&n.push(e)}),n},getTagElmByValue:function(t){var e=this.getTagIndexByValue(t)[0];return this.DOM.scope.querySelectorAll("tag")[e]},markTagByValue:function(t,e){return!!(e=e||this.getTagElmByValue(t))&&(e.classList.add("tagify--mark"),setTimeout(function(){e.classList.remove("tagify--mark")},100),e)},isTagBlacklisted:function(e){return e=e.split(" "),this.settings.blacklist.filter(function(t){return-1!=e.indexOf(t)}).length},isTagWhitelisted:function(e){return this.settings.whitelist.some(function(t){if((t.value?t.value:t).toLowerCase()===e.toLowerCase())return!0})},validateTag:function(t){var e=t.trim(),i=this.value.length>=this.settings.maxTags,n=!0;return e?i?n=this.TEXTS.exceed:this.settings.pattern&&!this.settings.pattern.test(e)?n=this.TEXTS.pattern:this.settings.duplicates||-1===this.isTagDuplicate(e)?(this.isTagBlacklisted(e)||this.settings.enforceWhitelist&&!this.isTagWhitelisted(e))&&(n=this.TEXTS.notAllowed):n=this.TEXTS.duplicate:n=this.TEXTS.empty,n},normalizeTags:function(t){var i=this,e=this.settings.whitelist[0]instanceof Object,n=t instanceof Array&&t[0]instanceof Object&&"value"in t[0],s=[];return n?t:"string"==typeof t?t.trim()?t.split(this.settings.delimiters).filter(function(t){return t}).map(function(t){return{value:t.trim()}}):[]:t instanceof Array?t.map(function(t){return{value:t.trim()}}):e?(t.forEach(function(e){var t=i.settings.whitelist.filter(function(t){return t.value.toLowerCase()==e.value.toLowerCase()});t[0]?s.push(t[0]):s.push(e)}),s):void 0},addTags:function(t,e){var n=this,s=[];return this.DOM.input.removeAttribute("style"),(t=this.normalizeTags.call(this,t)).forEach(function(t){var e,i;"function"==typeof n.settings.transformTag&&(t.value=n.settings.transformTag.call(n,t.value)||t.value),!0!==(e=n.validateTag.call(n,t.value))&&(t.class=t.class?t.class+" tagify--notAllowed":"tagify--notAllowed",t.title=e,n.markTagByValue(t.value),n.trigger("invalid",{value:t.value,index:n.value.length,message:e})),i=n.createTagElem(t),s.push(i),function(t){var e=this.DOM.scope.lastElementChild;e===this.DOM.input?this.DOM.scope.insertBefore(t,e):this.DOM.scope.appendChild(t)}.call(n,i),!0===e?(n.value.push(t),n.update(),n.trigger("add",n.extend({},{index:n.value.length,tag:i},t))):n.settings.keepInvalidTags||setTimeout(function(){n.removeTag(i,!0)},1e3)}),t.length&&e&&this.input.set.call(this),s},createTagElem:function(t){var e,i=this.escapeHtml(t.value),n="<tag title='"+i+"'>\n <x title=''></x><div><span>"+i+"</span></div>\n </tag>";if("function"==typeof this.settings.tagTemplate)try{n=this.settings.tagTemplate(i,t)}catch(t){}return function(t,e){var i,n=Object.keys(e);for(i=n.length;i--;){var s=n[i];if(!e.hasOwnProperty(s))return;t.setAttribute(s,e[s])}}(e=this.parseHTML(n),t),e},removeTag:function(t,e){var i=2<arguments.length&&void 0!==arguments[2]?arguments[2]:250;if(t){"string"==typeof t&&(t=this.getTagElmByValue(t));var n,s=this.getTagIndexByValue(t.textContent);i&&10<i?(t.style.width=parseFloat(window.getComputedStyle(t).width)+"px",document.body.clientTop,t.classList.add("tagify--hide"),setTimeout(a,400)):a(),e||(n=this.value.splice(s,1)[0],this.update(),this.trigger("remove",this.extend({},{index:s,tag:t},n)))}function a(){t.parentNode.removeChild(t)}},removeAllTags:function(){this.value=[],this.update(),Array.prototype.slice.call(this.DOM.scope.querySelectorAll("tag")).forEach(function(t){return t.parentNode.removeChild(t)})},update:function(){this.DOM.originalInput.value=JSON.stringify(this.value)},dropdown:{init:function(){this.DOM.dropdown=this.dropdown.build.call(this)},build:function(){var t='<div class="'+("tagify__dropdown "+this.settings.dropdown.classname).trim()+'"></div>';return this.parseHTML(t)},show:function(t){var e,i;this.settings.whitelist.length&&(e=t?this.dropdown.filterListItems.call(this,t):this.settings.whitelist.slice(0),i=this.dropdown.createListHTML.call(this,e),this.settings.autoComplete&&this.input.autocomplete.suggest.call(this,e.length?e[0].value:""),this.DOM.dropdown.innerHTML=i,this.dropdown.position.call(this),!this.DOM.dropdown.parentNode!=document.body&&(document.body.appendChild(this.DOM.dropdown),this.events.binding.call(this,!1),this.dropdown.events.binding.call(this)))},hide:function(){this.DOM.dropdown&&this.DOM.dropdown.parentNode==document.body&&(document.body.removeChild(this.DOM.dropdown),window.removeEventListener("resize",this.dropdown.position),this.dropdown.events.binding.call(this,!1),this.events.binding.call(this))},position:function(){var t=this.DOM.scope.getBoundingClientRect();this.DOM.dropdown.style.cssText="left: "+(t.left+window.pageXOffset)+"px; top: "+(t.top+t.height-1+window.pageYOffset)+"px; width: "+t.width+"px"},events:{binding:function(){var t=!(0<arguments.length&&void 0!==arguments[0])||arguments[0],e=this.listeners.dropdown=this.listeners.dropdown||{position:this.dropdown.position.bind(this),onKeyDown:this.dropdown.events.callbacks.onKeyDown.bind(this),onMouseOver:this.dropdown.events.callbacks.onMouseOver.bind(this),onClick:this.dropdown.events.callbacks.onClick.bind(this)},i=t?"addEventListener":"removeEventListener";window[i]("resize",e.position),window[i]("keydown",e.onKeyDown),window[i]("mousedown",e.onClick),this.DOM.dropdown[i]("mouseover",e.onMouseOver)},callbacks:{onKeyDown:function(t){var e=this.DOM.dropdown.querySelectorAll("[class$='--active']")[0],i="";switch(t.key){case"ArrowDown":case"ArrowUp":case"Down":case"Up":t.preventDefault(),e&&(e=e[("ArrowUp"==t.key||"Up"==t.key?"previous":"next")+"ElementSibling"]),e||(e=this.DOM.dropdown.children["ArrowUp"==t.key||"Up"==t.key?this.DOM.dropdown.children.length-1:0]),this.dropdown.highlightOption.call(this,e,!0);break;case"Escape":case"Esc":this.dropdown.hide.call(this);break;case"Enter":t.preventDefault(),i=e?e.textContent:this.input.value,this.addTags(i,!0),this.dropdown.hide.call(this);break;case"ArrowRight":case"Tab":return t.preventDefault(),this.input.autocomplete.set.call(this,e?e.textContent:null),!1}},onMouseOver:function(t){t.target.className.includes("__item")&&this.dropdown.highlightOption.call(this,t.target)},onClick:function(t){var e,i=this,n=function(){return i.dropdown.hide.call(i)};if(0==t.button){if(t.target==document.documentElement)return n();(e=[t.target,t.target.parentNode].filter(function(t){return t.className.includes("tagify__dropdown__item")})[0])?(this.input.set.call(this),this.addTags(e.textContent)):n()}}}},highlightOption:function(t,e){if(t){var i="tagify__dropdown__item--active";[].forEach.call(this.DOM.dropdown.querySelectorAll("[class$='--active']"),function(t){return t.classList.remove(i)}),t.classList.add(i),e&&(t.parentNode.scrollTop=t.clientHeight+t.offsetTop-t.parentNode.clientHeight)}},filterListItems:function(t){if(!t)return"";for(var e,i=[],n=this.settings.whitelist,s=this.settings.dropdown.maxItems||1/0,a=0;a<n.length&&(0==(e=n[a]instanceof Object?n[a]:{value:n[a]}).value.toLowerCase().replace(/\s/g,"").indexOf(t.toLowerCase().replace(/\s/g,""))&&-1==this.isTagDuplicate(e.value)&&s--&&i.push(e),0!=s);a++);return i},createListHTML:function(t){var e=this.settings.dropdown.itemTemplate||function(t){return"<div class='tagify__dropdown__item "+(t.class?t.class:"")+"' "+function(t){var e,i=Object.keys(t),n="";for(e=i.length;e--;){var s=i[e];if("class"!=s&&!t.hasOwnProperty(s))return;n+=" "+s+(t[s]?"="+t[s]:"")}return n}(t)+">"+t.value+"</div>"};return t.map(e).join("")}}}}(jQuery); | ||
"use strict";!function(a){function n(t,e){if(!t)return console.warn("Tagify: ","invalid input element ",t),this;if(this.settings=this.extend({},this.DEFAULTS,e),this.settings.readonly=t.hasAttribute("readonly"),this.isIE&&(this.settings.autoComplete=!1),t.pattern)try{this.settings.pattern=new RegExp(t.pattern)}catch(t){}if(this.settings&&this.settings.delimiters)try{this.settings.delimiters=new RegExp("["+this.settings.delimiters+"]","g")}catch(t){}this.state={},this.value=[],this.listeners={},this.DOM={},this.extend(this,new this.EventDispatcher(this)),this.build(t),this.loadOriginalValues(),this.events.customBinding.call(this),this.events.binding.call(this)}a.fn.tagify=function(){var i=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};return this.each(function(){var t,e=a(this);if(e.data("tagify"))return this;i.isJQueryPlugin=!0,t=new n(e[0],i),e.data("tagify",t)})},n.prototype={isIE:window.document.documentMode,TEXTS:{empty:"empty",exceed:"number of tags exceeded",pattern:"pattern mismatch",duplicate:"already exists",notAllowed:"not allowed"},DEFAULTS:{delimiters:",",pattern:null,maxTags:1/0,callbacks:{},addTagOnBlur:!0,duplicates:!1,whitelist:[],blacklist:[],enforceWhitelist:!1,keepInvalidTags:!1,autoComplete:!0,dropdown:{classname:"",enabled:2,maxItems:10,itemTemplate:""}},customEventsList:["add","remove","invalid"],parseHTML:function(t){return(new DOMParser).parseFromString(t.trim(),"text/html").body.firstElementChild},escapeHtml:function(t){var e=document.createTextNode(t),i=document.createElement("p");return i.appendChild(e),i.innerHTML},build:function(t){var e=this.DOM,i='<tags class="tagify '+(this.settings.mode?"tagify--mix":"")+" "+t.className+'" '+(this.settings.readonly?"readonly":"")+'>\n <div contenteditable data-placeholder="'+t.placeholder+'" class="tagify__input"></div>\n </tags>';e.originalInput=t,e.scope=this.parseHTML(i),e.input=e.scope.querySelector("[contenteditable]"),t.parentNode.insertBefore(e.scope,t),0<=this.settings.dropdown.enabled&&this.settings.whitelist.length&&this.dropdown.init.call(this),t.autofocus&&e.input.focus()},destroy:function(){this.DOM.scope.parentNode.removeChild(this.DOM.scope)},loadOriginalValues:function(){var t=this.DOM.originalInput.value;if(t){try{t=JSON.parse(t)}catch(t){}"mix"==this.settings.mode?this.DOM.input.innerHTML=this.parseMixTags(t):this.addTags(t).forEach(function(t){t&&t.classList.add("tagify--noAnim")})}},extend:function(t,e,i){function n(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 s(t,e){for(var i in e)e.hasOwnProperty(i)&&(n(e[i])?n(t[i])?s(t[i],e[i]):t[i]=Object.assign({},e[i]):t[i]=e[i])}return t instanceof Object||(t={}),s(t,e),i&&s(t,i),t},EventDispatcher:function(n){var s=document.createTextNode("");this.off=function(t,e){return e&&s.removeEventListener.call(s,t,e),this},this.on=function(t,e){return e&&s.addEventListener.call(s,t,e),this},this.trigger=function(t,e){var i;if(t)if(n.settings.isJQueryPlugin)a(n.DOM.originalInput).triggerHandler(t,[e]);else{try{i=new CustomEvent(t,{detail:e})}catch(t){console.warn(t)}s.dispatchEvent(i)}}},events:{customBinding:function(){var e=this;this.customEventsList.forEach(function(t){e.on(t,e.settings.callbacks[t])})},binding:function(){var t,e=!(0<arguments.length&&void 0!==arguments[0])||arguments[0],i=this.events.callbacks,n=e?"addEventListener":"removeEventListener";for(var s in e&&!this.listeners.main&&(this.DOM.input.addEventListener(this.isIE?"keydown":"input",i[this.isIE?"onInputIE":"onInput"].bind(this)),this.settings.isJQueryPlugin&&a(this.DOM.originalInput).on("tagify.removeAllTags",this.removeAllTags.bind(this))),t=this.listeners.main=this.listeners.main||{paste:["input",i.onPaste.bind(this)],focus:["input",i.onFocusBlur.bind(this)],blur:["input",i.onFocusBlur.bind(this)],keydown:["input",i.onKeydown.bind(this)],click:["scope",i.onClickScope.bind(this)]})this.DOM[t[s][0]][n](s,t[s][1])},callbacks:{onFocusBlur:function(t){var e=t.target.textContent.trim();"mix"!=this.settings.mode&&("focus"==t.type?0===this.settings.dropdown.enabled&&this.dropdown.show.call(this):"blur"==t.type&&e?this.settings.addTagOnBlur&&this.addTags(e,!0).length:(this.DOM.input.removeAttribute("style"),this.dropdown.hide.call(this)))},onKeydown:function(t){var e,i=t.target.textContent;"mix"!=this.settings.mode&&("Backspace"!=t.key||""!=i&&8203!=i.charCodeAt(0)?"Escape"==t.key||"Esc"==t.key?(this.input.set.call(this),t.target.blur()):"Enter"==t.key?(t.preventDefault(),this.addTags(this.input.value,!0)):"ArrowRight"==t.key&&this.input.autocomplete.set.call(this):(e=(e=this.DOM.scope.querySelectorAll("tag:not(.tagify--hide)"))[e.length-1],this.removeTag(e)))},onInput:function(t){var e=this.input.normalize.call(this),i=e.length>=this.settings.dropdown.enabled;if("mix"==this.settings.mode)return this.events.callbacks.onMixTagsInput.call(this,t);e?this.input.value!=e&&(this.input.set.call(this,e,!1),-1!=e.search(this.settings.delimiters)?this.addTags(e).length&&this.input.set.call(this):0<=this.settings.dropdown.enabled&&this.dropdown[i?"show":"hide"].call(this,e)):this.input.set.call(this,"")},onMixTagsInput:function(t){var e,i,n,s,a=this.settings.pattern.length;this.state.tag=null,window.getSelection&&0<(e=window.getSelection()).rangeCount&&((i=e.getRangeAt(0).cloneRange()).collapse(!0),i.setStart(window.getSelection().focusNode,0),s=(n=i.toString().split(/,|\.|\s/))[n.length-1],s=this.state.tag=s.substr(0,a)==this.settings.pattern&&s.length>a?s.slice(a):null),this.dropdown[s?"show":"hide"].call(this,s)},onInputIE:function(t){var e=this;setTimeout(function(){e.events.callbacks.onInput.call(e,t)})},onPaste:function(t){},onClickScope:function(t){"TAGS"==t.target.tagName?this.DOM.input.focus():"X"==t.target.tagName&&this.removeTag(t.target.parentNode)}}},input:{value:"",set:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:"",e=!(1<arguments.length&&void 0!==arguments[1])||arguments[1];this.input.value=t,e&&(this.DOM.input.innerHTML=t),t||this.dropdown.hide.call(this),t.length<2&&this.input.autocomplete.suggest.call(this,""),this.input.validate.call(this)},setRangeAtStartEnd:function(){var t,e,i=0<arguments.length&&void 0!==arguments[0]&&arguments[0],n=arguments[1];document.createRange&&((t=document.createRange()).selectNodeContents(n||this.DOM.input),t.collapse(i),(e=window.getSelection()).removeAllRanges(),e.addRange(t))},validate:function(){var t=!this.input.value||this.validateTag.call(this,this.input.value);this.DOM.input.classList.toggle("tagify__input--invalid",!0!==t)},normalize:function(){for(var t=this.DOM.input.cloneNode(!0),e=t.textContent.replace(/\s/g," ");t.firstElementChild;)e+=t.firstElementChild.textContent,t.removeChild(t.firstElementChild);return e.replace(/^\s+/,"")},autocomplete:{suggest:function(t){t&&this.input.value?this.DOM.input.setAttribute("data-suggest",t.substring(this.input.value.length)):this.DOM.input.removeAttribute("data-suggest")},set:function(t){var e=this.DOM.input.getAttribute("data-suggest"),i=t||(e?this.input.value+e:null);i&&(this.input.set.call(this,i),this.input.autocomplete.suggest.call(this,""),this.dropdown.hide.call(this),this.input.setRangeAtStartEnd.call(this))}}},getNodeIndex:function(t){for(var e=0;t=t.previousSibling;)3==t.nodeType&&/^\s*$/.test(t.data)||e++;return e},isTagDuplicate:function(e){return this.value.findIndex(function(t){return e.trim().toLowerCase()===t.value.toLowerCase()})},getTagIndexByValue:function(i){var n=[];return this.DOM.scope.querySelectorAll("tag").forEach(function(t,e){t.textContent.trim().toLowerCase()==i.toLowerCase()&&n.push(e)}),n},getTagElmByValue:function(t){var e=this.getTagIndexByValue(t)[0];return this.DOM.scope.querySelectorAll("tag")[e]},markTagByValue:function(t,e){return!!(e=e||this.getTagElmByValue(t))&&(e.classList.add("tagify--mark"),setTimeout(function(){e.classList.remove("tagify--mark")},100),e)},isTagBlacklisted:function(e){return e=e.split(" "),this.settings.blacklist.filter(function(t){return-1!=e.indexOf(t)}).length},isTagWhitelisted:function(e){return this.settings.whitelist.some(function(t){if((t.value?t.value:t).toLowerCase()===e.toLowerCase())return!0})},validateTag:function(t){var e=t.trim(),i=this.value.length>=this.settings.maxTags,n=!0;return e?i?n=this.TEXTS.exceed:this.settings.pattern&&!this.settings.pattern.test(e)?n=this.TEXTS.pattern:this.settings.duplicates||-1===this.isTagDuplicate(e)?(this.isTagBlacklisted(e)||this.settings.enforceWhitelist&&!this.isTagWhitelisted(e))&&(n=this.TEXTS.notAllowed):n=this.TEXTS.duplicate:n=this.TEXTS.empty,n},normalizeTags:function(t){var i=this,e=this.settings.whitelist[0]instanceof Object,n=t instanceof Array&&t[0]instanceof Object&&"value"in t[0],s=[];if(n)return t;if("string"==typeof t){if(!t.trim())return[];t=t.split(this.settings.delimiters).filter(function(t){return t}).map(function(t){return{value:t.trim()}})}else t instanceof Array&&(t=t.map(function(t){return{value:t.trim()}}));return e?(t.forEach(function(e){var t=i.settings.whitelist.filter(function(t){return t.value.toLowerCase()==e.value.toLowerCase()});t[0]?s.push(t[0]):"mix"!=i.settings.mode&&s.push(e)}),s):t},parseMixTags:function(t){var s=this;return(t=t.split(this.settings.pattern)).shift()+t.map(function(t){var e,i,n;if(!t)return"";for(i in t)if(t[i].match(/,|\.| /)){(n=s.normalizeTags.call(s,t.substr(0,i))[0])?e=s.createTagElem(n):i=0;break}return e?e.outerHTML+t.slice(i):s.settings.pattern+t}).join("")},addMixTag:function(t){if(t){var e,i,n,s=window.getSelection(),a=s.focusNode,o=a.textContent,r=document.createDocumentFragment(),l=this.createTagElem(t),h=this.settings.pattern.length;0<s.rangeCount&&((n=s.getRangeAt(0).cloneRange()).collapse(!0),n.setStart(a,0),e=n.toString().slice(0,-this.state.tag.length-h),i=o.slice(e.length+this.state.tag.length+h,o.length),e=document.createTextNode(e),i=document.createTextNode(i.trim()?i:" "),r.appendChild(e),r.appendChild(l),r.appendChild(i)),a.parentNode.replaceChild(r,a),this.input.setRangeAtStartEnd.call(this,!0,i)}},addTags:function(t,e){var n=this,s=[];if(t=this.normalizeTags.call(this,t),"mix"==this.settings.mode)return this.addMixTag(t[0]);return this.DOM.input.removeAttribute("style"),t.forEach(function(t){var e,i;"function"==typeof n.settings.transformTag&&(t.value=n.settings.transformTag.call(n,t.value)||t.value),!0!==(e=n.validateTag.call(n,t.value))&&(t.class=t.class?t.class+" tagify--notAllowed":"tagify--notAllowed",t.title=e,n.markTagByValue(t.value),n.trigger("invalid",{value:t.value,index:n.value.length,message:e})),i=n.createTagElem(t),s.push(i),function(t){var e=this.DOM.scope.lastElementChild;e===this.DOM.input?this.DOM.scope.insertBefore(t,e):this.DOM.scope.appendChild(t)}.call(n,i),!0===e?(n.value.push(t),n.update(),n.trigger("add",n.extend({},{index:n.value.length,tag:i},t))):n.settings.keepInvalidTags||setTimeout(function(){n.removeTag(i,!0)},1e3)}),t.length&&e&&this.input.set.call(this),s},minify:function(t){return t.replace(new RegExp(">[\r\n ]+<","g"),"><")},createTagElem:function(t){var e,i=this.escapeHtml(t.value),n="<tag title='"+i+"' contenteditable='false'>\n <x title=''></x><div><span>"+i+"</span></div>\n </tag>";if("function"==typeof this.settings.tagTemplate)try{n=this.settings.tagTemplate(i,t)}catch(t){}return n=this.minify(n),function(t,e){var i,n=Object.keys(e);for(i=n.length;i--;){var s=n[i];if(!e.hasOwnProperty(s))return;t.setAttribute(s,e[s])}}(e=this.parseHTML(n),t),e},removeTag:function(t,e){var i=2<arguments.length&&void 0!==arguments[2]?arguments[2]:250;if(t){"string"==typeof t&&(t=this.getTagElmByValue(t));var n,s=this.getTagIndexByValue(t.textContent);i&&10<i?(t.style.width=parseFloat(window.getComputedStyle(t).width)+"px",document.body.clientTop,t.classList.add("tagify--hide"),setTimeout(a,400)):a(),e||(n=this.value.splice(s,1)[0],this.update(),this.trigger("remove",this.extend({},{index:s,tag:t},n)))}function a(){t.parentNode.removeChild(t)}},removeAllTags:function(){this.value=[],this.update(),Array.prototype.slice.call(this.DOM.scope.querySelectorAll("tag")).forEach(function(t){return t.parentNode.removeChild(t)})},update:function(){this.DOM.originalInput.value=JSON.stringify(this.value)},dropdown:{init:function(){this.DOM.dropdown=this.dropdown.build.call(this)},build:function(){var t='<div class="'+("tagify__dropdown "+this.settings.dropdown.classname).trim()+'"></div>';return this.parseHTML(t)},show:function(t){var e,i;this.settings.whitelist.length&&(e=t?this.dropdown.filterListItems.call(this,t):this.settings.whitelist.slice(0),i=this.dropdown.createListHTML.call(this,e),this.settings.autoComplete&&this.input.autocomplete.suggest.call(this,e.length?e[0].value:""),this.DOM.dropdown.innerHTML=i,this.dropdown.position.call(this),!this.DOM.dropdown.parentNode!=document.body&&(document.body.appendChild(this.DOM.dropdown),this.events.binding.call(this,!1),this.dropdown.events.binding.call(this)))},hide:function(){this.DOM.dropdown&&this.DOM.dropdown.parentNode==document.body&&(document.body.removeChild(this.DOM.dropdown),window.removeEventListener("resize",this.dropdown.position),this.dropdown.events.binding.call(this,!1),this.events.binding.call(this))},position:function(){var t=this.DOM.scope.getBoundingClientRect();this.DOM.dropdown.style.cssText="left: "+(t.left+window.pageXOffset)+"px; top: "+(t.top+t.height-1+window.pageYOffset)+"px; width: "+t.width+"px"},events:{binding:function(){var t=!(0<arguments.length&&void 0!==arguments[0])||arguments[0],e=this.listeners.dropdown=this.listeners.dropdown||{position:this.dropdown.position.bind(this),onKeyDown:this.dropdown.events.callbacks.onKeyDown.bind(this),onMouseOver:this.dropdown.events.callbacks.onMouseOver.bind(this),onClick:this.dropdown.events.callbacks.onClick.bind(this)},i=t?"addEventListener":"removeEventListener";window[i]("resize",e.position),window[i]("keydown",e.onKeyDown),window[i]("mousedown",e.onClick),this.DOM.dropdown[i]("mouseover",e.onMouseOver)},callbacks:{onKeyDown:function(t){var e=this.DOM.dropdown.querySelectorAll("[class$='--active']")[0],i="";switch(t.key){case"ArrowDown":case"ArrowUp":case"Down":case"Up":t.preventDefault(),e&&(e=e[("ArrowUp"==t.key||"Up"==t.key?"previous":"next")+"ElementSibling"]),e||(e=this.DOM.dropdown.children["ArrowUp"==t.key||"Up"==t.key?this.DOM.dropdown.children.length-1:0]),this.dropdown.highlightOption.call(this,e,!0);break;case"Escape":case"Esc":this.dropdown.hide.call(this);break;case"Enter":t.preventDefault(),i=e?e.textContent:this.input.value,this.addTags(i,!0),this.dropdown.hide.call(this);break;case"ArrowRight":case"Tab":return t.preventDefault(),this.input.autocomplete.set.call(this,e?e.textContent:null),!1}},onMouseOver:function(t){t.target.className.includes("__item")&&this.dropdown.highlightOption.call(this,t.target)},onClick:function(t){var e,i=this,n=function(){return i.dropdown.hide.call(i)};if(0==t.button){if(t.target==document.documentElement)return n();(e=[t.target,t.target.parentNode].filter(function(t){return t.className.includes("tagify__dropdown__item")})[0])?(this.addTags(e.textContent,!0),this.dropdown.hide.call(this)):n()}}}},highlightOption:function(t,e){if(t){var i="tagify__dropdown__item--active";[].forEach.call(this.DOM.dropdown.querySelectorAll("[class$='--active']"),function(t){return t.classList.remove(i)}),t.classList.add(i),e&&(t.parentNode.scrollTop=t.clientHeight+t.offsetTop-t.parentNode.clientHeight)}},filterListItems:function(t){if(!t)return"";for(var e,i=[],n=this.settings.whitelist,s=this.settings.dropdown.maxItems||1/0,a=0;a<n.length&&(0==(e=n[a]instanceof Object?n[a]:{value:n[a]}).value.toLowerCase().replace(/\s/g,"").indexOf(t.toLowerCase().replace(/\s/g,""))&&-1==this.isTagDuplicate(e.value)&&s--&&i.push(e),0!=s);a++);return i},createListHTML:function(t){var e=this.settings.dropdown.itemTemplate||function(t){return"<div class='tagify__dropdown__item "+(t.class?t.class:"")+"' "+function(t){var e,i=Object.keys(t),n="";for(e=i.length;e--;){var s=i[e];if("class"!=s&&!t.hasOwnProperty(s))return;n+=" "+s+(t[s]?"="+t[s]:"")}return n}(t)+">"+t.value+"</div>"};return t.map(e).join("")}}}}(jQuery); |
/** | ||
* Tagify (v 2.2.10)- tags input component | ||
* Tagify (v 2.5.0)- tags input component | ||
* By Yair Even-Or (2016) | ||
@@ -40,2 +40,3 @@ * Don't sell this code. (c) | ||
this.state = {}; | ||
this.value = []; // tags' data | ||
@@ -123,3 +124,3 @@ // events' callbacks references will be stores here, so events could be unbinded | ||
DOM = this.DOM, | ||
template = "<tags class=\"tagify " + input.className + "\" " + (this.settings.readonly ? 'readonly' : '') + ">\n <div contenteditable data-placeholder=\"" + input.placeholder + "\" class=\"tagify__input\"></div>\n </tags>"; | ||
template = "<tags class=\"tagify " + (this.settings.mode ? "tagify--mix" : "") + " " + input.className + "\" " + (this.settings.readonly ? 'readonly' : '') + ">\n <div contenteditable data-placeholder=\"" + input.placeholder + "\" class=\"tagify__input\"></div>\n </tags>"; | ||
DOM.originalInput = input; | ||
@@ -145,2 +146,21 @@ DOM.scope = this.parseHTML(template); | ||
/** | ||
* If the original input had an values, add them as tags | ||
*/ | ||
loadOriginalValues: function loadOriginalValues() { | ||
var value = this.DOM.originalInput.value; // if the original input already had any value (tags) | ||
if (!value) return; | ||
try { | ||
value = JSON.parse(value); | ||
} catch (err) {} | ||
if (this.settings.mode == 'mix') { | ||
this.DOM.input.innerHTML = this.parseMixTags(value); | ||
} else this.addTags(value).forEach(function (tag) { | ||
tag && tag.classList.add('tagify--noAnim'); | ||
}); | ||
}, | ||
/** | ||
* Merge two objects into a new one | ||
@@ -230,3 +250,13 @@ * TEST: extend({}, {a:{foo:1}, b:[]}, {a:{bar:2}, b:[1], c:()=>{}}) | ||
var _CB = this.events.callbacks, | ||
// setup callback references so events could be removed later | ||
_CBR, | ||
action = bindUnbind ? 'addEventListener' : 'removeEventListener'; | ||
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) $(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 || { | ||
@@ -238,4 +268,3 @@ paste: ['input', _CB.onPaste.bind(this)], | ||
click: ['scope', _CB.onClickScope.bind(this)] | ||
}, | ||
action = bindUnbind ? 'addEventListener' : 'removeEventListener'; | ||
}; | ||
@@ -245,9 +274,2 @@ for (var eventName in _CBR) { | ||
} | ||
if (bindUnbind) { | ||
// 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) $(this.DOM.originalInput).on('tagify.removeAllTags', this.removeAllTags.bind(this)); | ||
} | ||
}, | ||
@@ -261,2 +283,3 @@ | ||
var s = e.target.textContent.trim(); | ||
if (this.settings.mode == 'mix') return; | ||
@@ -279,2 +302,3 @@ if (e.type == "focus") { | ||
lastTag; | ||
if (this.settings.mode == 'mix') return; | ||
@@ -297,2 +321,3 @@ if (e.key == 'Backspace' && (s == "" || s.charCodeAt(0) == 8203)) { | ||
showSuggestions = value.length >= this.settings.dropdown.enabled; | ||
if (this.settings.mode == 'mix') return this.events.callbacks.onMixTagsInput.call(this, e); | ||
@@ -317,2 +342,26 @@ if (!value) { | ||
}, | ||
onMixTagsInput: function onMixTagsInput(e) { | ||
var sel, | ||
range, | ||
split, | ||
tag, | ||
patternLen = this.settings.pattern.length; | ||
this.state.tag = null; | ||
if (window.getSelection) { | ||
sel = window.getSelection(); | ||
if (sel.rangeCount > 0) { | ||
range = sel.getRangeAt(0).cloneRange(); | ||
range.collapse(true); | ||
range.setStart(window.getSelection().focusNode, 0); | ||
split = range.toString().split(/,|\.|\s/); // ["foo", "bar", "@a"] | ||
tag = split[split.length - 1]; | ||
tag = this.state.tag = tag.substr(0, patternLen) == this.settings.pattern && tag.length > patternLen ? tag.slice(patternLen) : null; | ||
} | ||
} | ||
this.dropdown[tag ? "show" : "hide"].call(this, tag); | ||
}, | ||
onInputIE: function onInputIE(e) { | ||
@@ -336,19 +385,2 @@ var _this = this; // for the "e.target.textContent" to be changed, the browser requires a small delay | ||
/** | ||
* If the original input had an values, add them as tags | ||
*/ | ||
loadOriginalValues: function loadOriginalValues() { | ||
var value = this.DOM.originalInput.value; // if the original input already had any value (tags) | ||
if (!value) return; | ||
try { | ||
value = JSON.parse(value); | ||
} catch (err) {} | ||
this.addTags(value).forEach(function (tag) { | ||
tag && tag.classList.add('tagify--noAnim'); | ||
}); | ||
}, | ||
/** | ||
* input bridge for accessing & setting | ||
@@ -369,8 +401,10 @@ * @type {Object} | ||
// https://stackoverflow.com/a/3866442/104380 | ||
setRangeAtEnd: function setRangeAtEnd() { | ||
setRangeAtStartEnd: function setRangeAtStartEnd() { | ||
var start = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; | ||
var node = arguments[1]; | ||
var range, selection; | ||
if (!document.createRange) return; | ||
range = document.createRange(); | ||
range.selectNodeContents(this.DOM.input); | ||
range.collapse(false); | ||
range.selectNodeContents(node || this.DOM.input); | ||
range.collapse(start); | ||
selection = window.getSelection(); | ||
@@ -417,3 +451,3 @@ selection.removeAllRanges(); | ||
this.dropdown.hide.call(this); | ||
this.input.setRangeAtEnd.call(this); | ||
this.input.setRangeAtStartEnd.call(this); | ||
} // if( suggestion && this.addTags(this.input.value + suggestion).length ){ | ||
@@ -536,3 +570,3 @@ // this.input.set.call(this); | ||
return tagsItems.split(this.settings.delimiters).filter(function (n) { | ||
tagsItems = tagsItems.split(this.settings.delimiters).filter(function (n) { | ||
return n; | ||
@@ -544,5 +578,3 @@ }).map(function (v) { | ||
}); | ||
} | ||
if (tagsItems instanceof Array) return tagsItems.map(function (v) { | ||
} else if (tagsItems instanceof Array) tagsItems = tagsItems.map(function (v) { | ||
return { | ||
@@ -553,2 +585,3 @@ value: v.trim() | ||
if (whitelistWithProps) { | ||
@@ -561,9 +594,66 @@ tagsItems.forEach(function (tag) { | ||
if (matchObj[0]) temp.push(matchObj[0]); // set the Array (with the found Object) as the new value | ||
else temp.push(tag); | ||
else if (_this3.settings.mode != 'mix') temp.push(tag); | ||
}); | ||
return temp; | ||
} | ||
return tagsItems; | ||
}, | ||
parseMixTags: function parseMixTags(s) { | ||
var _this4 = this; | ||
var htmlString = ''; | ||
s = s.split(this.settings.pattern); // this.DOM.scope.innerHTML | ||
htmlString = s.shift() + s.map(function (part) { | ||
var tagElm, i, normalizedTag; | ||
if (!part) return ''; | ||
for (i in part) { | ||
if (part[i].match(/,|\.| /)) { | ||
normalizedTag = _this4.normalizeTags.call(_this4, part.substr(0, i))[0]; | ||
if (normalizedTag) tagElm = _this4.createTagElem(normalizedTag);else i = 0; // a tag was found but was not in the whitelist, so reset the "i" index | ||
break; | ||
} | ||
} | ||
return tagElm ? tagElm.outerHTML + part.slice(i) : _this4.settings.pattern + part; | ||
}).join(''); | ||
return htmlString; | ||
}, | ||
/** | ||
* Add a tag where it might be beside textNodes | ||
*/ | ||
addMixTag: function addMixTag(tagData) { | ||
if (!tagData) return; | ||
var sel = window.getSelection(), | ||
node = sel.focusNode, | ||
nodeText = node.textContent, | ||
wrapElm = document.createDocumentFragment(), | ||
tagElm = this.createTagElem(tagData), | ||
textNodeBefore, | ||
textNodeAfter, | ||
range, | ||
parrernLen = this.settings.pattern.length; | ||
if (sel.rangeCount > 0) { | ||
range = sel.getRangeAt(0).cloneRange(); | ||
range.collapse(true); | ||
range.setStart(node, 0); | ||
textNodeBefore = range.toString().slice(0, -this.state.tag.length - parrernLen); | ||
textNodeAfter = nodeText.slice(textNodeBefore.length + this.state.tag.length + parrernLen, nodeText.length); | ||
textNodeBefore = document.createTextNode(textNodeBefore); | ||
textNodeAfter = document.createTextNode(textNodeAfter.trim() ? textNodeAfter : " \u200B"); | ||
wrapElm.appendChild(textNodeBefore); | ||
wrapElm.appendChild(tagElm); | ||
wrapElm.appendChild(textNodeAfter); | ||
} | ||
node.parentNode.replaceChild(wrapElm, node); | ||
this.input.setRangeAtStartEnd.call(this, true, textNodeAfter); | ||
}, | ||
/** | ||
* add a "tag" element to the "tags" component | ||
@@ -575,15 +665,16 @@ * @param {String/Array} tagsItems [A string (single or multiple values with a delimiter), or an Array of Objects or just Array of Strings] | ||
addTags: function addTags(tagsItems, clearInput) { | ||
var _this4 = this; | ||
var _this5 = this; | ||
var tagElems = []; | ||
tagsItems = this.normalizeTags.call(this, tagsItems); | ||
if (this.settings.mode == 'mix') return this.addMixTag(tagsItems[0]); | ||
this.DOM.input.removeAttribute('style'); | ||
tagsItems = this.normalizeTags.call(this, tagsItems); | ||
tagsItems.forEach(function (tagData) { | ||
var tagValidation, tagElm; | ||
if (typeof _this4.settings.transformTag === 'function') { | ||
tagData.value = _this4.settings.transformTag.call(_this4, tagData.value) || tagData.value; | ||
if (typeof _this5.settings.transformTag === 'function') { | ||
tagData.value = _this5.settings.transformTag.call(_this5, tagData.value) || tagData.value; | ||
} | ||
tagValidation = _this4.validateTag.call(_this4, tagData.value); | ||
tagValidation = _this5.validateTag.call(_this5, tagData.value); | ||
@@ -594,7 +685,7 @@ if (tagValidation !== true) { | ||
_this4.markTagByValue(tagData.value); | ||
_this5.markTagByValue(tagData.value); | ||
_this4.trigger("invalid", { | ||
_this5.trigger("invalid", { | ||
value: tagData.value, | ||
index: _this4.value.length, | ||
index: _this5.value.length, | ||
message: tagValidation | ||
@@ -605,21 +696,21 @@ }); | ||
tagElm = _this4.createTagElem(tagData); | ||
tagElm = _this5.createTagElem(tagData); | ||
tagElems.push(tagElm); // add the tag to the component's DOM | ||
appendTag.call(_this4, tagElm); | ||
appendTag.call(_this5, tagElm); | ||
if (tagValidation === true) { | ||
// update state | ||
_this4.value.push(tagData); | ||
_this5.value.push(tagData); | ||
_this4.update(); | ||
_this5.update(); | ||
_this4.trigger('add', _this4.extend({}, { | ||
index: _this4.value.length, | ||
_this5.trigger('add', _this5.extend({}, { | ||
index: _this5.value.length, | ||
tag: tagElm | ||
}, tagData)); | ||
} else if (!_this4.settings.keepInvalidTags) { | ||
} else if (!_this5.settings.keepInvalidTags) { | ||
// remove invalid tags (if "keepInvalidTags" is set to "false") | ||
setTimeout(function () { | ||
_this4.removeTag(tagElm, true); | ||
_this5.removeTag(tagElm, true); | ||
}, 1000); | ||
@@ -645,2 +736,5 @@ } | ||
}, | ||
minify: function minify(html) { | ||
return html.replace(new RegExp("\>[\r\n ]+\<", "g"), "><"); | ||
}, | ||
@@ -655,3 +749,3 @@ /** | ||
v = this.escapeHtml(tagData.value), | ||
template = "<tag title='" + v + "'>\n <x title=''></x><div><span>" + v + "</span></div>\n </tag>"; | ||
template = "<tag title='" + v + "' contenteditable='false'>\n <x title=''></x><div><span>" + v + "</span></div>\n </tag>"; | ||
@@ -676,2 +770,3 @@ if (typeof this.settings.tagTemplate === "function") { | ||
template = this.minify(template); | ||
tagElm = this.parseHTML(template); // add any attribuets, if exists | ||
@@ -863,6 +958,6 @@ | ||
onClick: function onClick(e) { | ||
var _this5 = this; | ||
var _this6 = this; | ||
var onClickOutside = function onClickOutside() { | ||
return _this5.dropdown.hide.call(_this5); | ||
return _this6.dropdown.hide.call(_this6); | ||
}, | ||
@@ -879,4 +974,4 @@ listItemElm; | ||
if (listItemElm) { | ||
this.input.set.call(this); | ||
this.addTags(listItemElm.textContent); | ||
this.addTags(listItemElm.textContent, true); | ||
this.dropdown.hide.call(this); | ||
} // clicked outside the dropdown, so just close it | ||
@@ -883,0 +978,0 @@ else onClickOutside(); |
@@ -1,1 +0,1 @@ | ||
!function(t,e){"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?module.exports=e():t.Tagify=e()}(this,function(){"use strict";function t(t,e){if(!t)return console.warn("Tagify: ","invalid input element ",t),this;if(this.settings=this.extend({},this.DEFAULTS,e),this.settings.readonly=t.hasAttribute("readonly"),this.isIE&&(this.settings.autoComplete=!1),t.pattern)try{this.settings.pattern=new RegExp(t.pattern)}catch(t){}if(this.settings&&this.settings.delimiters)try{this.settings.delimiters=new RegExp("["+this.settings.delimiters+"]","g")}catch(t){}this.value=[],this.listeners={},this.DOM={},this.extend(this,new this.EventDispatcher(this)),this.build(t),this.loadOriginalValues(),this.events.customBinding.call(this),this.events.binding.call(this)}return t.prototype={isIE:window.document.documentMode,TEXTS:{empty:"empty",exceed:"number of tags exceeded",pattern:"pattern mismatch",duplicate:"already exists",notAllowed:"not allowed"},DEFAULTS:{delimiters:",",pattern:null,maxTags:1/0,callbacks:{},addTagOnBlur:!0,duplicates:!1,whitelist:[],blacklist:[],enforceWhitelist:!1,keepInvalidTags:!1,autoComplete:!0,dropdown:{classname:"",enabled:2,maxItems:10,itemTemplate:""}},customEventsList:["add","remove","invalid"],parseHTML:function(t){return(new DOMParser).parseFromString(t.trim(),"text/html").body.firstElementChild},escapeHtml:function(t){var e=document.createTextNode(t),i=document.createElement("p");return i.appendChild(e),i.innerHTML},build:function(t){var e=this.DOM,i='<tags class="tagify '+t.className+'" '+(this.settings.readonly?"readonly":"")+'>\n <div contenteditable data-placeholder="'+t.placeholder+'" class="tagify__input"></div>\n </tags>';e.originalInput=t,e.scope=this.parseHTML(i),e.input=e.scope.querySelector("[contenteditable]"),t.parentNode.insertBefore(e.scope,t),0<=this.settings.dropdown.enabled&&this.settings.whitelist.length&&this.dropdown.init.call(this),t.autofocus&&e.input.focus()},destroy:function(){this.DOM.scope.parentNode.removeChild(this.DOM.scope)},extend:function(t,e,i){function n(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 s(t,e){for(var i in e)e.hasOwnProperty(i)&&(n(e[i])?n(t[i])?s(t[i],e[i]):t[i]=Object.assign({},e[i]):t[i]=e[i])}return t instanceof Object||(t={}),s(t,e),i&&s(t,i),t},EventDispatcher:function(n){var s=document.createTextNode("");this.off=function(t,e){return e&&s.removeEventListener.call(s,t,e),this},this.on=function(t,e){return e&&s.addEventListener.call(s,t,e),this},this.trigger=function(t,e){var i;if(t)if(n.settings.isJQueryPlugin)$(n.DOM.originalInput).triggerHandler(t,[e]);else{try{i=new CustomEvent(t,{detail:e})}catch(t){console.warn(t)}s.dispatchEvent(i)}}},events:{customBinding:function(){var e=this;this.customEventsList.forEach(function(t){e.on(t,e.settings.callbacks[t])})},binding:function(){var t=!(0<arguments.length&&void 0!==arguments[0])||arguments[0],e=this.events.callbacks,i=this.listeners.main=this.listeners.main||{paste:["input",e.onPaste.bind(this)],focus:["input",e.onFocusBlur.bind(this)],blur:["input",e.onFocusBlur.bind(this)],keydown:["input",e.onKeydown.bind(this)],click:["scope",e.onClickScope.bind(this)]},n=t?"addEventListener":"removeEventListener";for(var s in i)this.DOM[i[s][0]][n](s,i[s][1]);t&&(this.DOM.input.addEventListener(this.isIE?"keydown":"input",e[this.isIE?"onInputIE":"onInput"].bind(this)),this.settings.isJQueryPlugin&&$(this.DOM.originalInput).on("tagify.removeAllTags",this.removeAllTags.bind(this)))},callbacks:{onFocusBlur:function(t){var e=t.target.textContent.trim();"focus"==t.type?0===this.settings.dropdown.enabled&&this.dropdown.show.call(this):"blur"==t.type&&e?this.settings.addTagOnBlur&&this.addTags(e,!0).length:(this.DOM.input.removeAttribute("style"),this.dropdown.hide.call(this))},onKeydown:function(t){var e,i=t.target.textContent;"Backspace"!=t.key||""!=i&&8203!=i.charCodeAt(0)?"Escape"==t.key||"Esc"==t.key?(this.input.set.call(this),t.target.blur()):"Enter"==t.key?(t.preventDefault(),this.addTags(this.input.value,!0)):"ArrowRight"==t.key&&this.input.autocomplete.set.call(this):(e=(e=this.DOM.scope.querySelectorAll("tag:not(.tagify--hide)"))[e.length-1],this.removeTag(e))},onInput:function(t){var e=this.input.normalize.call(this),i=e.length>=this.settings.dropdown.enabled;e?this.input.value!=e&&(this.input.set.call(this,e,!1),-1!=e.search(this.settings.delimiters)?this.addTags(e).length&&this.input.set.call(this):0<=this.settings.dropdown.enabled&&this.dropdown[i?"show":"hide"].call(this,e)):this.input.set.call(this,"")},onInputIE:function(t){var e=this;setTimeout(function(){e.events.callbacks.onInput.call(e,t)})},onPaste:function(t){},onClickScope:function(t){"TAGS"==t.target.tagName?this.DOM.input.focus():"X"==t.target.tagName&&this.removeTag(t.target.parentNode)}}},loadOriginalValues:function(){var t=this.DOM.originalInput.value;if(t){try{t=JSON.parse(t)}catch(t){}this.addTags(t).forEach(function(t){t&&t.classList.add("tagify--noAnim")})}},input:{value:"",set:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:"",e=!(1<arguments.length&&void 0!==arguments[1])||arguments[1];this.input.value=t,e&&(this.DOM.input.innerHTML=t),t||this.dropdown.hide.call(this),t.length<2&&this.input.autocomplete.suggest.call(this,""),this.input.validate.call(this)},setRangeAtEnd:function(){var t,e;document.createRange&&((t=document.createRange()).selectNodeContents(this.DOM.input),t.collapse(!1),(e=window.getSelection()).removeAllRanges(),e.addRange(t))},validate:function(){var t=!this.input.value||this.validateTag.call(this,this.input.value);this.DOM.input.classList.toggle("tagify__input--invalid",!0!==t)},normalize:function(){for(var t=this.DOM.input.cloneNode(!0),e=t.textContent.replace(/\s/g," ");t.firstElementChild;)e+=t.firstElementChild.textContent,t.removeChild(t.firstElementChild);return e.replace(/^\s+/,"")},autocomplete:{suggest:function(t){t&&this.input.value?this.DOM.input.setAttribute("data-suggest",t.substring(this.input.value.length)):this.DOM.input.removeAttribute("data-suggest")},set:function(t){var e=this.DOM.input.getAttribute("data-suggest"),i=t||(e?this.input.value+e:null);i&&(this.input.set.call(this,i),this.input.autocomplete.suggest.call(this,""),this.dropdown.hide.call(this),this.input.setRangeAtEnd.call(this))}}},getNodeIndex:function(t){for(var e=0;t=t.previousSibling;)3==t.nodeType&&/^\s*$/.test(t.data)||e++;return e},isTagDuplicate:function(e){return this.value.findIndex(function(t){return e.trim().toLowerCase()===t.value.toLowerCase()})},getTagIndexByValue:function(i){var n=[];return this.DOM.scope.querySelectorAll("tag").forEach(function(t,e){t.textContent.trim().toLowerCase()==i.toLowerCase()&&n.push(e)}),n},getTagElmByValue:function(t){var e=this.getTagIndexByValue(t)[0];return this.DOM.scope.querySelectorAll("tag")[e]},markTagByValue:function(t,e){return!!(e=e||this.getTagElmByValue(t))&&(e.classList.add("tagify--mark"),setTimeout(function(){e.classList.remove("tagify--mark")},100),e)},isTagBlacklisted:function(e){return e=e.split(" "),this.settings.blacklist.filter(function(t){return-1!=e.indexOf(t)}).length},isTagWhitelisted:function(e){return this.settings.whitelist.some(function(t){if((t.value?t.value:t).toLowerCase()===e.toLowerCase())return!0})},validateTag:function(t){var e=t.trim(),i=this.value.length>=this.settings.maxTags,n=!0;return e?i?n=this.TEXTS.exceed:this.settings.pattern&&!this.settings.pattern.test(e)?n=this.TEXTS.pattern:this.settings.duplicates||-1===this.isTagDuplicate(e)?(this.isTagBlacklisted(e)||this.settings.enforceWhitelist&&!this.isTagWhitelisted(e))&&(n=this.TEXTS.notAllowed):n=this.TEXTS.duplicate:n=this.TEXTS.empty,n},normalizeTags:function(t){var i=this,e=this.settings.whitelist[0]instanceof Object,n=t instanceof Array&&t[0]instanceof Object&&"value"in t[0],s=[];return n?t:"string"==typeof t?t.trim()?t.split(this.settings.delimiters).filter(function(t){return t}).map(function(t){return{value:t.trim()}}):[]:t instanceof Array?t.map(function(t){return{value:t.trim()}}):e?(t.forEach(function(e){var t=i.settings.whitelist.filter(function(t){return t.value.toLowerCase()==e.value.toLowerCase()});t[0]?s.push(t[0]):s.push(e)}),s):void 0},addTags:function(t,e){var n=this,s=[];return this.DOM.input.removeAttribute("style"),(t=this.normalizeTags.call(this,t)).forEach(function(t){var e,i;"function"==typeof n.settings.transformTag&&(t.value=n.settings.transformTag.call(n,t.value)||t.value),!0!==(e=n.validateTag.call(n,t.value))&&(t.class=t.class?t.class+" tagify--notAllowed":"tagify--notAllowed",t.title=e,n.markTagByValue(t.value),n.trigger("invalid",{value:t.value,index:n.value.length,message:e})),i=n.createTagElem(t),s.push(i),function(t){var e=this.DOM.scope.lastElementChild;e===this.DOM.input?this.DOM.scope.insertBefore(t,e):this.DOM.scope.appendChild(t)}.call(n,i),!0===e?(n.value.push(t),n.update(),n.trigger("add",n.extend({},{index:n.value.length,tag:i},t))):n.settings.keepInvalidTags||setTimeout(function(){n.removeTag(i,!0)},1e3)}),t.length&&e&&this.input.set.call(this),s},createTagElem:function(t){var e,i=this.escapeHtml(t.value),n="<tag title='"+i+"'>\n <x title=''></x><div><span>"+i+"</span></div>\n </tag>";if("function"==typeof this.settings.tagTemplate)try{n=this.settings.tagTemplate(i,t)}catch(t){}return function(t,e){var i,n=Object.keys(e);for(i=n.length;i--;){var s=n[i];if(!e.hasOwnProperty(s))return;t.setAttribute(s,e[s])}}(e=this.parseHTML(n),t),e},removeTag:function(t,e){var i=2<arguments.length&&void 0!==arguments[2]?arguments[2]:250;if(t){"string"==typeof t&&(t=this.getTagElmByValue(t));var n,s=this.getTagIndexByValue(t.textContent);i&&10<i?(t.style.width=parseFloat(window.getComputedStyle(t).width)+"px",document.body.clientTop,t.classList.add("tagify--hide"),setTimeout(a,400)):a(),e||(n=this.value.splice(s,1)[0],this.update(),this.trigger("remove",this.extend({},{index:s,tag:t},n)))}function a(){t.parentNode.removeChild(t)}},removeAllTags:function(){this.value=[],this.update(),Array.prototype.slice.call(this.DOM.scope.querySelectorAll("tag")).forEach(function(t){return t.parentNode.removeChild(t)})},update:function(){this.DOM.originalInput.value=JSON.stringify(this.value)},dropdown:{init:function(){this.DOM.dropdown=this.dropdown.build.call(this)},build:function(){var t='<div class="'+("tagify__dropdown "+this.settings.dropdown.classname).trim()+'"></div>';return this.parseHTML(t)},show:function(t){var e,i;this.settings.whitelist.length&&(e=t?this.dropdown.filterListItems.call(this,t):this.settings.whitelist.slice(0),i=this.dropdown.createListHTML.call(this,e),this.settings.autoComplete&&this.input.autocomplete.suggest.call(this,e.length?e[0].value:""),this.DOM.dropdown.innerHTML=i,this.dropdown.position.call(this),!this.DOM.dropdown.parentNode!=document.body&&(document.body.appendChild(this.DOM.dropdown),this.events.binding.call(this,!1),this.dropdown.events.binding.call(this)))},hide:function(){this.DOM.dropdown&&this.DOM.dropdown.parentNode==document.body&&(document.body.removeChild(this.DOM.dropdown),window.removeEventListener("resize",this.dropdown.position),this.dropdown.events.binding.call(this,!1),this.events.binding.call(this))},position:function(){var t=this.DOM.scope.getBoundingClientRect();this.DOM.dropdown.style.cssText="left: "+(t.left+window.pageXOffset)+"px; top: "+(t.top+t.height-1+window.pageYOffset)+"px; width: "+t.width+"px"},events:{binding:function(){var t=!(0<arguments.length&&void 0!==arguments[0])||arguments[0],e=this.listeners.dropdown=this.listeners.dropdown||{position:this.dropdown.position.bind(this),onKeyDown:this.dropdown.events.callbacks.onKeyDown.bind(this),onMouseOver:this.dropdown.events.callbacks.onMouseOver.bind(this),onClick:this.dropdown.events.callbacks.onClick.bind(this)},i=t?"addEventListener":"removeEventListener";window[i]("resize",e.position),window[i]("keydown",e.onKeyDown),window[i]("mousedown",e.onClick),this.DOM.dropdown[i]("mouseover",e.onMouseOver)},callbacks:{onKeyDown:function(t){var e=this.DOM.dropdown.querySelectorAll("[class$='--active']")[0],i="";switch(t.key){case"ArrowDown":case"ArrowUp":case"Down":case"Up":t.preventDefault(),e&&(e=e[("ArrowUp"==t.key||"Up"==t.key?"previous":"next")+"ElementSibling"]),e||(e=this.DOM.dropdown.children["ArrowUp"==t.key||"Up"==t.key?this.DOM.dropdown.children.length-1:0]),this.dropdown.highlightOption.call(this,e,!0);break;case"Escape":case"Esc":this.dropdown.hide.call(this);break;case"Enter":t.preventDefault(),i=e?e.textContent:this.input.value,this.addTags(i,!0),this.dropdown.hide.call(this);break;case"ArrowRight":case"Tab":return t.preventDefault(),this.input.autocomplete.set.call(this,e?e.textContent:null),!1}},onMouseOver:function(t){t.target.className.includes("__item")&&this.dropdown.highlightOption.call(this,t.target)},onClick:function(t){var e,i=this,n=function(){return i.dropdown.hide.call(i)};if(0==t.button){if(t.target==document.documentElement)return n();(e=[t.target,t.target.parentNode].filter(function(t){return t.className.includes("tagify__dropdown__item")})[0])?(this.input.set.call(this),this.addTags(e.textContent)):n()}}}},highlightOption:function(t,e){if(t){var i="tagify__dropdown__item--active";[].forEach.call(this.DOM.dropdown.querySelectorAll("[class$='--active']"),function(t){return t.classList.remove(i)}),t.classList.add(i),e&&(t.parentNode.scrollTop=t.clientHeight+t.offsetTop-t.parentNode.clientHeight)}},filterListItems:function(t){if(!t)return"";for(var e,i=[],n=this.settings.whitelist,s=this.settings.dropdown.maxItems||1/0,a=0;a<n.length&&(0==(e=n[a]instanceof Object?n[a]:{value:n[a]}).value.toLowerCase().replace(/\s/g,"").indexOf(t.toLowerCase().replace(/\s/g,""))&&-1==this.isTagDuplicate(e.value)&&s--&&i.push(e),0!=s);a++);return i},createListHTML:function(t){var e=this.settings.dropdown.itemTemplate||function(t){return"<div class='tagify__dropdown__item "+(t.class?t.class:"")+"' "+function(t){var e,i=Object.keys(t),n="";for(e=i.length;e--;){var s=i[e];if("class"!=s&&!t.hasOwnProperty(s))return;n+=" "+s+(t[s]?"="+t[s]:"")}return n}(t)+">"+t.value+"</div>"};return t.map(e).join("")}}},t}); | ||
!function(t,e){"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?module.exports=e():t.Tagify=e()}(this,function(){"use strict";function t(t,e){if(!t)return console.warn("Tagify: ","invalid input element ",t),this;if(this.settings=this.extend({},this.DEFAULTS,e),this.settings.readonly=t.hasAttribute("readonly"),this.isIE&&(this.settings.autoComplete=!1),t.pattern)try{this.settings.pattern=new RegExp(t.pattern)}catch(t){}if(this.settings&&this.settings.delimiters)try{this.settings.delimiters=new RegExp("["+this.settings.delimiters+"]","g")}catch(t){}this.state={},this.value=[],this.listeners={},this.DOM={},this.extend(this,new this.EventDispatcher(this)),this.build(t),this.loadOriginalValues(),this.events.customBinding.call(this),this.events.binding.call(this)}return t.prototype={isIE:window.document.documentMode,TEXTS:{empty:"empty",exceed:"number of tags exceeded",pattern:"pattern mismatch",duplicate:"already exists",notAllowed:"not allowed"},DEFAULTS:{delimiters:",",pattern:null,maxTags:1/0,callbacks:{},addTagOnBlur:!0,duplicates:!1,whitelist:[],blacklist:[],enforceWhitelist:!1,keepInvalidTags:!1,autoComplete:!0,dropdown:{classname:"",enabled:2,maxItems:10,itemTemplate:""}},customEventsList:["add","remove","invalid"],parseHTML:function(t){return(new DOMParser).parseFromString(t.trim(),"text/html").body.firstElementChild},escapeHtml:function(t){var e=document.createTextNode(t),i=document.createElement("p");return i.appendChild(e),i.innerHTML},build:function(t){var e=this.DOM,i='<tags class="tagify '+(this.settings.mode?"tagify--mix":"")+" "+t.className+'" '+(this.settings.readonly?"readonly":"")+'>\n <div contenteditable data-placeholder="'+t.placeholder+'" class="tagify__input"></div>\n </tags>';e.originalInput=t,e.scope=this.parseHTML(i),e.input=e.scope.querySelector("[contenteditable]"),t.parentNode.insertBefore(e.scope,t),0<=this.settings.dropdown.enabled&&this.settings.whitelist.length&&this.dropdown.init.call(this),t.autofocus&&e.input.focus()},destroy:function(){this.DOM.scope.parentNode.removeChild(this.DOM.scope)},loadOriginalValues:function(){var t=this.DOM.originalInput.value;if(t){try{t=JSON.parse(t)}catch(t){}"mix"==this.settings.mode?this.DOM.input.innerHTML=this.parseMixTags(t):this.addTags(t).forEach(function(t){t&&t.classList.add("tagify--noAnim")})}},extend:function(t,e,i){function n(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 s(t,e){for(var i in e)e.hasOwnProperty(i)&&(n(e[i])?n(t[i])?s(t[i],e[i]):t[i]=Object.assign({},e[i]):t[i]=e[i])}return t instanceof Object||(t={}),s(t,e),i&&s(t,i),t},EventDispatcher:function(n){var s=document.createTextNode("");this.off=function(t,e){return e&&s.removeEventListener.call(s,t,e),this},this.on=function(t,e){return e&&s.addEventListener.call(s,t,e),this},this.trigger=function(t,e){var i;if(t)if(n.settings.isJQueryPlugin)$(n.DOM.originalInput).triggerHandler(t,[e]);else{try{i=new CustomEvent(t,{detail:e})}catch(t){console.warn(t)}s.dispatchEvent(i)}}},events:{customBinding:function(){var e=this;this.customEventsList.forEach(function(t){e.on(t,e.settings.callbacks[t])})},binding:function(){var t,e=!(0<arguments.length&&void 0!==arguments[0])||arguments[0],i=this.events.callbacks,n=e?"addEventListener":"removeEventListener";for(var s in e&&!this.listeners.main&&(this.DOM.input.addEventListener(this.isIE?"keydown":"input",i[this.isIE?"onInputIE":"onInput"].bind(this)),this.settings.isJQueryPlugin&&$(this.DOM.originalInput).on("tagify.removeAllTags",this.removeAllTags.bind(this))),t=this.listeners.main=this.listeners.main||{paste:["input",i.onPaste.bind(this)],focus:["input",i.onFocusBlur.bind(this)],blur:["input",i.onFocusBlur.bind(this)],keydown:["input",i.onKeydown.bind(this)],click:["scope",i.onClickScope.bind(this)]})this.DOM[t[s][0]][n](s,t[s][1])},callbacks:{onFocusBlur:function(t){var e=t.target.textContent.trim();"mix"!=this.settings.mode&&("focus"==t.type?0===this.settings.dropdown.enabled&&this.dropdown.show.call(this):"blur"==t.type&&e?this.settings.addTagOnBlur&&this.addTags(e,!0).length:(this.DOM.input.removeAttribute("style"),this.dropdown.hide.call(this)))},onKeydown:function(t){var e,i=t.target.textContent;"mix"!=this.settings.mode&&("Backspace"!=t.key||""!=i&&8203!=i.charCodeAt(0)?"Escape"==t.key||"Esc"==t.key?(this.input.set.call(this),t.target.blur()):"Enter"==t.key?(t.preventDefault(),this.addTags(this.input.value,!0)):"ArrowRight"==t.key&&this.input.autocomplete.set.call(this):(e=(e=this.DOM.scope.querySelectorAll("tag:not(.tagify--hide)"))[e.length-1],this.removeTag(e)))},onInput:function(t){var e=this.input.normalize.call(this),i=e.length>=this.settings.dropdown.enabled;if("mix"==this.settings.mode)return this.events.callbacks.onMixTagsInput.call(this,t);e?this.input.value!=e&&(this.input.set.call(this,e,!1),-1!=e.search(this.settings.delimiters)?this.addTags(e).length&&this.input.set.call(this):0<=this.settings.dropdown.enabled&&this.dropdown[i?"show":"hide"].call(this,e)):this.input.set.call(this,"")},onMixTagsInput:function(t){var e,i,n,s,a=this.settings.pattern.length;this.state.tag=null,window.getSelection&&0<(e=window.getSelection()).rangeCount&&((i=e.getRangeAt(0).cloneRange()).collapse(!0),i.setStart(window.getSelection().focusNode,0),s=(n=i.toString().split(/,|\.|\s/))[n.length-1],s=this.state.tag=s.substr(0,a)==this.settings.pattern&&s.length>a?s.slice(a):null),this.dropdown[s?"show":"hide"].call(this,s)},onInputIE:function(t){var e=this;setTimeout(function(){e.events.callbacks.onInput.call(e,t)})},onPaste:function(t){},onClickScope:function(t){"TAGS"==t.target.tagName?this.DOM.input.focus():"X"==t.target.tagName&&this.removeTag(t.target.parentNode)}}},input:{value:"",set:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:"",e=!(1<arguments.length&&void 0!==arguments[1])||arguments[1];this.input.value=t,e&&(this.DOM.input.innerHTML=t),t||this.dropdown.hide.call(this),t.length<2&&this.input.autocomplete.suggest.call(this,""),this.input.validate.call(this)},setRangeAtStartEnd:function(){var t,e,i=0<arguments.length&&void 0!==arguments[0]&&arguments[0],n=arguments[1];document.createRange&&((t=document.createRange()).selectNodeContents(n||this.DOM.input),t.collapse(i),(e=window.getSelection()).removeAllRanges(),e.addRange(t))},validate:function(){var t=!this.input.value||this.validateTag.call(this,this.input.value);this.DOM.input.classList.toggle("tagify__input--invalid",!0!==t)},normalize:function(){for(var t=this.DOM.input.cloneNode(!0),e=t.textContent.replace(/\s/g," ");t.firstElementChild;)e+=t.firstElementChild.textContent,t.removeChild(t.firstElementChild);return e.replace(/^\s+/,"")},autocomplete:{suggest:function(t){t&&this.input.value?this.DOM.input.setAttribute("data-suggest",t.substring(this.input.value.length)):this.DOM.input.removeAttribute("data-suggest")},set:function(t){var e=this.DOM.input.getAttribute("data-suggest"),i=t||(e?this.input.value+e:null);i&&(this.input.set.call(this,i),this.input.autocomplete.suggest.call(this,""),this.dropdown.hide.call(this),this.input.setRangeAtStartEnd.call(this))}}},getNodeIndex:function(t){for(var e=0;t=t.previousSibling;)3==t.nodeType&&/^\s*$/.test(t.data)||e++;return e},isTagDuplicate:function(e){return this.value.findIndex(function(t){return e.trim().toLowerCase()===t.value.toLowerCase()})},getTagIndexByValue:function(i){var n=[];return this.DOM.scope.querySelectorAll("tag").forEach(function(t,e){t.textContent.trim().toLowerCase()==i.toLowerCase()&&n.push(e)}),n},getTagElmByValue:function(t){var e=this.getTagIndexByValue(t)[0];return this.DOM.scope.querySelectorAll("tag")[e]},markTagByValue:function(t,e){return!!(e=e||this.getTagElmByValue(t))&&(e.classList.add("tagify--mark"),setTimeout(function(){e.classList.remove("tagify--mark")},100),e)},isTagBlacklisted:function(e){return e=e.split(" "),this.settings.blacklist.filter(function(t){return-1!=e.indexOf(t)}).length},isTagWhitelisted:function(e){return this.settings.whitelist.some(function(t){if((t.value?t.value:t).toLowerCase()===e.toLowerCase())return!0})},validateTag:function(t){var e=t.trim(),i=this.value.length>=this.settings.maxTags,n=!0;return e?i?n=this.TEXTS.exceed:this.settings.pattern&&!this.settings.pattern.test(e)?n=this.TEXTS.pattern:this.settings.duplicates||-1===this.isTagDuplicate(e)?(this.isTagBlacklisted(e)||this.settings.enforceWhitelist&&!this.isTagWhitelisted(e))&&(n=this.TEXTS.notAllowed):n=this.TEXTS.duplicate:n=this.TEXTS.empty,n},normalizeTags:function(t){var i=this,e=this.settings.whitelist[0]instanceof Object,n=t instanceof Array&&t[0]instanceof Object&&"value"in t[0],s=[];if(n)return t;if("string"==typeof t){if(!t.trim())return[];t=t.split(this.settings.delimiters).filter(function(t){return t}).map(function(t){return{value:t.trim()}})}else t instanceof Array&&(t=t.map(function(t){return{value:t.trim()}}));return e?(t.forEach(function(e){var t=i.settings.whitelist.filter(function(t){return t.value.toLowerCase()==e.value.toLowerCase()});t[0]?s.push(t[0]):"mix"!=i.settings.mode&&s.push(e)}),s):t},parseMixTags:function(t){var s=this;return(t=t.split(this.settings.pattern)).shift()+t.map(function(t){var e,i,n;if(!t)return"";for(i in t)if(t[i].match(/,|\.| /)){(n=s.normalizeTags.call(s,t.substr(0,i))[0])?e=s.createTagElem(n):i=0;break}return e?e.outerHTML+t.slice(i):s.settings.pattern+t}).join("")},addMixTag:function(t){if(t){var e,i,n,s=window.getSelection(),a=s.focusNode,o=a.textContent,r=document.createDocumentFragment(),l=this.createTagElem(t),h=this.settings.pattern.length;0<s.rangeCount&&((n=s.getRangeAt(0).cloneRange()).collapse(!0),n.setStart(a,0),e=n.toString().slice(0,-this.state.tag.length-h),i=o.slice(e.length+this.state.tag.length+h,o.length),e=document.createTextNode(e),i=document.createTextNode(i.trim()?i:" "),r.appendChild(e),r.appendChild(l),r.appendChild(i)),a.parentNode.replaceChild(r,a),this.input.setRangeAtStartEnd.call(this,!0,i)}},addTags:function(t,e){var n=this,s=[];if(t=this.normalizeTags.call(this,t),"mix"==this.settings.mode)return this.addMixTag(t[0]);return this.DOM.input.removeAttribute("style"),t.forEach(function(t){var e,i;"function"==typeof n.settings.transformTag&&(t.value=n.settings.transformTag.call(n,t.value)||t.value),!0!==(e=n.validateTag.call(n,t.value))&&(t.class=t.class?t.class+" tagify--notAllowed":"tagify--notAllowed",t.title=e,n.markTagByValue(t.value),n.trigger("invalid",{value:t.value,index:n.value.length,message:e})),i=n.createTagElem(t),s.push(i),function(t){var e=this.DOM.scope.lastElementChild;e===this.DOM.input?this.DOM.scope.insertBefore(t,e):this.DOM.scope.appendChild(t)}.call(n,i),!0===e?(n.value.push(t),n.update(),n.trigger("add",n.extend({},{index:n.value.length,tag:i},t))):n.settings.keepInvalidTags||setTimeout(function(){n.removeTag(i,!0)},1e3)}),t.length&&e&&this.input.set.call(this),s},minify:function(t){return t.replace(new RegExp(">[\r\n ]+<","g"),"><")},createTagElem:function(t){var e,i=this.escapeHtml(t.value),n="<tag title='"+i+"' contenteditable='false'>\n <x title=''></x><div><span>"+i+"</span></div>\n </tag>";if("function"==typeof this.settings.tagTemplate)try{n=this.settings.tagTemplate(i,t)}catch(t){}return n=this.minify(n),function(t,e){var i,n=Object.keys(e);for(i=n.length;i--;){var s=n[i];if(!e.hasOwnProperty(s))return;t.setAttribute(s,e[s])}}(e=this.parseHTML(n),t),e},removeTag:function(t,e){var i=2<arguments.length&&void 0!==arguments[2]?arguments[2]:250;if(t){"string"==typeof t&&(t=this.getTagElmByValue(t));var n,s=this.getTagIndexByValue(t.textContent);i&&10<i?(t.style.width=parseFloat(window.getComputedStyle(t).width)+"px",document.body.clientTop,t.classList.add("tagify--hide"),setTimeout(a,400)):a(),e||(n=this.value.splice(s,1)[0],this.update(),this.trigger("remove",this.extend({},{index:s,tag:t},n)))}function a(){t.parentNode.removeChild(t)}},removeAllTags:function(){this.value=[],this.update(),Array.prototype.slice.call(this.DOM.scope.querySelectorAll("tag")).forEach(function(t){return t.parentNode.removeChild(t)})},update:function(){this.DOM.originalInput.value=JSON.stringify(this.value)},dropdown:{init:function(){this.DOM.dropdown=this.dropdown.build.call(this)},build:function(){var t='<div class="'+("tagify__dropdown "+this.settings.dropdown.classname).trim()+'"></div>';return this.parseHTML(t)},show:function(t){var e,i;this.settings.whitelist.length&&(e=t?this.dropdown.filterListItems.call(this,t):this.settings.whitelist.slice(0),i=this.dropdown.createListHTML.call(this,e),this.settings.autoComplete&&this.input.autocomplete.suggest.call(this,e.length?e[0].value:""),this.DOM.dropdown.innerHTML=i,this.dropdown.position.call(this),!this.DOM.dropdown.parentNode!=document.body&&(document.body.appendChild(this.DOM.dropdown),this.events.binding.call(this,!1),this.dropdown.events.binding.call(this)))},hide:function(){this.DOM.dropdown&&this.DOM.dropdown.parentNode==document.body&&(document.body.removeChild(this.DOM.dropdown),window.removeEventListener("resize",this.dropdown.position),this.dropdown.events.binding.call(this,!1),this.events.binding.call(this))},position:function(){var t=this.DOM.scope.getBoundingClientRect();this.DOM.dropdown.style.cssText="left: "+(t.left+window.pageXOffset)+"px; top: "+(t.top+t.height-1+window.pageYOffset)+"px; width: "+t.width+"px"},events:{binding:function(){var t=!(0<arguments.length&&void 0!==arguments[0])||arguments[0],e=this.listeners.dropdown=this.listeners.dropdown||{position:this.dropdown.position.bind(this),onKeyDown:this.dropdown.events.callbacks.onKeyDown.bind(this),onMouseOver:this.dropdown.events.callbacks.onMouseOver.bind(this),onClick:this.dropdown.events.callbacks.onClick.bind(this)},i=t?"addEventListener":"removeEventListener";window[i]("resize",e.position),window[i]("keydown",e.onKeyDown),window[i]("mousedown",e.onClick),this.DOM.dropdown[i]("mouseover",e.onMouseOver)},callbacks:{onKeyDown:function(t){var e=this.DOM.dropdown.querySelectorAll("[class$='--active']")[0],i="";switch(t.key){case"ArrowDown":case"ArrowUp":case"Down":case"Up":t.preventDefault(),e&&(e=e[("ArrowUp"==t.key||"Up"==t.key?"previous":"next")+"ElementSibling"]),e||(e=this.DOM.dropdown.children["ArrowUp"==t.key||"Up"==t.key?this.DOM.dropdown.children.length-1:0]),this.dropdown.highlightOption.call(this,e,!0);break;case"Escape":case"Esc":this.dropdown.hide.call(this);break;case"Enter":t.preventDefault(),i=e?e.textContent:this.input.value,this.addTags(i,!0),this.dropdown.hide.call(this);break;case"ArrowRight":case"Tab":return t.preventDefault(),this.input.autocomplete.set.call(this,e?e.textContent:null),!1}},onMouseOver:function(t){t.target.className.includes("__item")&&this.dropdown.highlightOption.call(this,t.target)},onClick:function(t){var e,i=this,n=function(){return i.dropdown.hide.call(i)};if(0==t.button){if(t.target==document.documentElement)return n();(e=[t.target,t.target.parentNode].filter(function(t){return t.className.includes("tagify__dropdown__item")})[0])?(this.addTags(e.textContent,!0),this.dropdown.hide.call(this)):n()}}}},highlightOption:function(t,e){if(t){var i="tagify__dropdown__item--active";[].forEach.call(this.DOM.dropdown.querySelectorAll("[class$='--active']"),function(t){return t.classList.remove(i)}),t.classList.add(i),e&&(t.parentNode.scrollTop=t.clientHeight+t.offsetTop-t.parentNode.clientHeight)}},filterListItems:function(t){if(!t)return"";for(var e,i=[],n=this.settings.whitelist,s=this.settings.dropdown.maxItems||1/0,a=0;a<n.length&&(0==(e=n[a]instanceof Object?n[a]:{value:n[a]}).value.toLowerCase().replace(/\s/g,"").indexOf(t.toLowerCase().replace(/\s/g,""))&&-1==this.isTagDuplicate(e.value)&&s--&&i.push(e),0!=s);a++);return i},createListHTML:function(t){var e=this.settings.dropdown.itemTemplate||function(t){return"<div class='tagify__dropdown__item "+(t.class?t.class:"")+"' "+function(t){var e,i=Object.keys(t),n="";for(e=i.length;e--;){var s=i[e];if("class"!=s&&!t.hasOwnProperty(s))return;n+=" "+s+(t[s]?"="+t[s]:"")}return n}(t)+">"+t.value+"</div>"};return t.map(e).join("")}}},t}); |
{ | ||
"name": "@yaireo/tagify", | ||
"version": "2.3.0", | ||
"version": "2.5.0", | ||
"homepage": "https://github.com/yairEO/tagify", | ||
@@ -5,0 +5,0 @@ "description": "Want to simply convert an input field into a tags element, in a easy customizable way, with good performance and smallest code footprint? You are in the right place my friend.", |
176
README.md
@@ -15,8 +15,31 @@ [Tagify](https://yaireo.github.io/tagify) - lightweight input "tags" script | ||
--> | ||
<p align="center"> | ||
<img src="https://raw.githubusercontent.com/yairEO/tagify/master/mix.gif" /> | ||
</p> | ||
<p align="center"> | ||
<img src="https://raw.githubusercontent.com/yairEO/tagify/master/demo.gif" /> | ||
</p> | ||
![alt tag](https://raw.githubusercontent.com/yairEO/tagify/master/demo.gif) | ||
Transforms an input field or a textarea into a *Tags* component, in an easy, customizable way, | ||
with great performance and tiny code footprint. | ||
## [Documentation & Demos](https://yaireo.github.io/tagify) | ||
## Table of contents | ||
<!--ts--> | ||
* [Installation](#installation) | ||
* [Selling points](#selling-points) | ||
* [What can Tagify do](#what-can-tagify-do) | ||
* [Building the project](#building-the-project) | ||
* [Adding tags dynamically](#adding-tags-dynamically) | ||
* [Suggestions selectbox](#suggestions-selectbox) | ||
* [jQuery version](#jquery-version) | ||
* [Methods](#methods) | ||
* [Events](#events) | ||
* [Settings](#settings) | ||
<!--te--> | ||
## Installation | ||
npm i @yaireo/tagify --save | ||
@@ -27,6 +50,6 @@ | ||
## [Documentation & Demos](https://yaireo.github.io/tagify) | ||
var tagify = new Tagify(...) | ||
## Selling points | ||
* JS at `~13kb` (`4kb` GZIP) (less than *900* easily understandable lines of code) | ||
* JS minified `~16kb` (`~4kb` GZIP) | ||
* SCSS file is `~6kb` of well-crafted flexible code | ||
@@ -41,2 +64,3 @@ * Easily change direction to RTL via the SCSS file only | ||
* Can be applied on input & textarea elements | ||
* Supports mix content (text and tags together) | ||
* Supports whitelist | ||
@@ -63,139 +87,14 @@ * Supports blacklists | ||
## Basic usage | ||
Lets say this is your markup, and you already have a value set on the input (which was pre-filled by data from the server): | ||
### Adding tags dynamically | ||
```html | ||
<input name='tags' placeholder='write some tags' value='foo, bar,buzz'> | ||
<textarea name='tags' pattern=".{3,}" placeholder='write some tags'>foo, bar,buzz</textarea> | ||
``` | ||
What you need to do to convert that nice input into "tags" is simply select your input/textarea and run `tagify()`: | ||
```javascript | ||
// vanilla component | ||
var input = document.querySelector('input[name=tags]'), | ||
tagify = new Tagify( input ); | ||
// with settings passed | ||
tagify = new Tagify( input, { | ||
duplicates: true, | ||
whitelist: ['foo', 'bar'], | ||
callbacks: { | ||
add : onAddTag // calls an imaginary "onAddTag" function when a tag is added | ||
} | ||
}); | ||
// listen to custom tags' events such as 'add' or 'remove' (see full list below). | ||
// listeners are chainable | ||
tagify.on('remove', onTagRemoved) | ||
.on('add', onTagAdded); | ||
.on('invalid', onInvaildTag) | ||
function onTagRemoved(e){ | ||
console.log(e, e.detail); | ||
// remove listener after first tag removal | ||
tagify.off('remove', onTagRemoved); | ||
} | ||
function onTagAdded(e){ | ||
// do whatever | ||
} | ||
function onInvaildTag(e){ | ||
// e.detail ... | ||
} | ||
``` | ||
The value of the Tagify component can be accessed like so: | ||
```javascript | ||
var tagify = new Tagify(...); | ||
console.log( tagify.value ) | ||
// [{"value":"tag1"}, {"value":"tag2"}, ...] | ||
tagify.addTags(["banana", "orange", "apple"]) | ||
// The original input's value is a String representing Array of Objects. | ||
// To parse it, use: `JSON.parse(input.value)` | ||
console.log( input.value ) | ||
// "[{value:"tag1"}, {value:"tag2"}, ...]" | ||
``` | ||
// or add tags with pre-defined propeties | ||
If the Tags were added with custom properties, the *value* output might look something like this: | ||
```javascript | ||
tagify.value | ||
// [{"value":"tag1", "class":"red", "id":1}, ...] | ||
tagify.addTags([{value:"banana", color:"yellow"}, {value:"apple", color:"red"}, {value:"watermelon", color:"green"}]) | ||
``` | ||
### Tags with properties ([example](https://yaireo.github.io/tagify#section-extra-properties)) | ||
The below example will populate the Tags component with 2 tags, each with specific attributes & values. | ||
the `addTags` method accepts an Array of Objects with **any** key/value, as long as the `value` key is defined. | ||
```html | ||
<input placeholder="add tags"> | ||
``` | ||
```javascript | ||
var allowedTags = [ | ||
{ | ||
"value" : "apple", | ||
"data-id" : 3, | ||
"class" : 'color-green' | ||
}, | ||
{ | ||
"value" : "orange", | ||
"data-id" : 56, | ||
"class" : 'color-orange' | ||
}, | ||
{ | ||
"value" : "passion fruit", | ||
"data-id" : 17, | ||
"class" : 'color-purple' | ||
}, | ||
{ | ||
"value" : "banana", | ||
"data-id" : 12, | ||
"class" : 'color-yellow' | ||
}, | ||
{ | ||
"value" : "paprika", | ||
"data-id" : 25, | ||
"class" : 'color-red' | ||
} | ||
]; | ||
var input = document.querySelector('input'), | ||
tagify = new Tagify(input, { | ||
whitelist : allowedTags | ||
}); | ||
// Add the first 2 tags from the "allowedTags" Array | ||
tagify.addTags( allowedTags.slice(0,2) ) | ||
``` | ||
The above will prepend a "tags" element before the original input element: | ||
```html | ||
<tags class="tagify"> | ||
<tag readonly="true" class="color-red" data-id="8" value="strawberry"> | ||
<x></x><div><span title="strawberry">strawberry</span></div> | ||
</tag> | ||
<tag readonly="true" class="color-darkblue" data-id="6" value="blueberry"> | ||
<x></x><div><span title="blueberry">blueberry</span></div> | ||
</tag> | ||
<div contenteditable data-placeholder="add tags" class="tagify--input"></div> | ||
</tags> | ||
<input placeholder="add tags"> | ||
``` | ||
Another way to add tags is: | ||
```javascript | ||
tagify.addTags( | ||
["banana", "orange", "apple"].map( item => ({ value:item }) ) | ||
); | ||
``` | ||
### Suggestions selectbox | ||
@@ -238,4 +137,9 @@ The suggestions selectbox is shown is a whitelist Array of Strings or Objects was passed in the settings when the Tagify instance was created. | ||
### jQuery plugin version (jQuery.tagify.js) | ||
### jQuery version | ||
`jQuery.tagify.js` | ||
A jQuery wrapper verison is also available, but I advise not using it because it's basically the exact same as the "normal" | ||
script (non-jqueryfied) and all the jQuery's wrapper does is allowing to chain the event listeners for ('add', 'remove', 'invalid') | ||
```javascript | ||
@@ -258,2 +162,3 @@ $('[name=tags]') | ||
removeTag | Removes a specific tag (argument is the tag DOM element to be removed. see source code.) | ||
loadOriginalValues | Converts the input's value into tags. This method gets called automatically when instansiating Tagify | ||
getTagIndexByValue | | ||
@@ -263,3 +168,3 @@ getTagElmByValue | | ||
## Exposed events | ||
## Events | ||
@@ -279,2 +184,3 @@ Name | Info | ||
pattern | String | null | Validate input by REGEX pattern (can also be applied on the input itself as an attribute) Ex: /[1-9]/ | ||
mode | String | null | use 'mix' as value to allow mixed-content. The 'pattern' setting must be set to some character. | ||
duplicates | Boolean | false | (flag) Should duplicate tags be allowed or not | ||
@@ -281,0 +187,0 @@ enforceWhitelist | Boolean | false | Should ONLY use tags allowed in whitelist |
@@ -24,2 +24,3 @@ function Tagify( input, settings ){ | ||
this.state = {}; | ||
this.value = []; // tags' data | ||
@@ -100,3 +101,3 @@ | ||
DOM = this.DOM, | ||
template = `<tags class="tagify ${input.className}" ${this.settings.readonly ? 'readonly' : ''}> | ||
template = `<tags class="tagify ${this.settings.mode ? "tagify--mix" : "" } ${input.className}" ${this.settings.readonly ? 'readonly' : ''}> | ||
<div contenteditable data-placeholder="${input.placeholder}" class="tagify__input"></div> | ||
@@ -125,2 +126,24 @@ </tags>`; | ||
/** | ||
* If the original input had an values, add them as tags | ||
*/ | ||
loadOriginalValues(){ | ||
var value = this.DOM.originalInput.value; | ||
// if the original input already had any value (tags) | ||
if( !value ) return; | ||
try{ value = JSON.parse(value) } | ||
catch(err){} | ||
if( this.settings.mode == 'mix' ){ | ||
this.DOM.input.innerHTML = this.parseMixTags(value); | ||
} | ||
else | ||
this.addTags(value).forEach(tag => { | ||
tag && tag.classList.add('tagify--noAnim'); | ||
}); | ||
}, | ||
/** | ||
* Merge two objects into a new one | ||
@@ -210,17 +233,6 @@ * TEST: extend({}, {a:{foo:1}, b:[]}, {a:{bar:2}, b:[1], c:()=>{}}) | ||
var _CB = this.events.callbacks, | ||
// setup callback references so events could be removed later | ||
_CBR = (this.listeners.main = this.listeners.main || { | ||
paste : ['input', _CB.onPaste.bind(this)], | ||
focus : ['input', _CB.onFocusBlur.bind(this)], | ||
blur : ['input', _CB.onFocusBlur.bind(this)], | ||
keydown : ['input', _CB.onKeydown.bind(this)], | ||
click : ['scope', _CB.onClickScope.bind(this)] | ||
}), | ||
_CBR, | ||
action = bindUnbind ? 'addEventListener' : 'removeEventListener'; | ||
for( var eventName in _CBR ){ | ||
this.DOM[_CBR[eventName][0]][action](eventName, _CBR[eventName][1]); | ||
} | ||
if( bindUnbind ){ | ||
if( bindUnbind && !this.listeners.main ){ | ||
// this event should never be unbinded | ||
@@ -233,2 +245,15 @@ // IE cannot register "input" events on contenteditable elements, so the "keydown" should be used instead.. | ||
} | ||
// setup callback references so events could be removed later | ||
_CBR = (this.listeners.main = this.listeners.main || { | ||
paste : ['input', _CB.onPaste.bind(this)], | ||
focus : ['input', _CB.onFocusBlur.bind(this)], | ||
blur : ['input', _CB.onFocusBlur.bind(this)], | ||
keydown : ['input', _CB.onKeydown.bind(this)], | ||
click : ['scope', _CB.onClickScope.bind(this)] | ||
}); | ||
for( var eventName in _CBR ){ | ||
this.DOM[_CBR[eventName][0]][action](eventName, _CBR[eventName][1]); | ||
} | ||
}, | ||
@@ -243,2 +268,4 @@ | ||
if( this.settings.mode == 'mix' ) return; | ||
if( e.type == "focus" ){ | ||
@@ -266,2 +293,4 @@ // e.target.classList.remove('placeholder'); | ||
if( this.settings.mode == 'mix' ) return; | ||
if( e.key == 'Backspace' && (s == "" || s.charCodeAt(0) == 8203) ){ | ||
@@ -291,2 +320,5 @@ lastTag = this.DOM.scope.querySelectorAll('tag:not(.tagify--hide)'); | ||
if( this.settings.mode == 'mix' ) | ||
return this.events.callbacks.onMixTagsInput.call(this, e); | ||
if( !value ){ | ||
@@ -312,2 +344,25 @@ this.input.set.call(this, ''); | ||
onMixTagsInput( e ){ | ||
var sel, range, split, tag, | ||
patternLen = this.settings.pattern.length; | ||
this.state.tag = null; | ||
if( window.getSelection ){ | ||
sel = window.getSelection(); | ||
if( sel.rangeCount > 0 ){ | ||
range = sel.getRangeAt(0).cloneRange(); | ||
range.collapse(true); | ||
range.setStart(window.getSelection().focusNode, 0); | ||
split = range.toString().split(/,|\.|\s/); // ["foo", "bar", "@a"] | ||
tag = split[split.length-1]; | ||
tag = this.state.tag = (tag.substr(0, patternLen) == this.settings.pattern && tag.length > patternLen) ? tag.slice(patternLen) : null; | ||
} | ||
} | ||
this.dropdown[tag ? "show" : "hide"].call(this, tag); | ||
}, | ||
onInputIE(e){ | ||
@@ -335,19 +390,2 @@ var _this = this; | ||
/** | ||
* If the original input had an values, add them as tags | ||
*/ | ||
loadOriginalValues(){ | ||
var value = this.DOM.originalInput.value; | ||
// if the original input already had any value (tags) | ||
if( !value ) return; | ||
try{ value = JSON.parse(value) } | ||
catch(err){} | ||
this.addTags(value).forEach(tag => { | ||
tag && tag.classList.add('tagify--noAnim'); | ||
}); | ||
}, | ||
/** | ||
* input bridge for accessing & setting | ||
@@ -369,3 +407,3 @@ * @type {Object} | ||
// https://stackoverflow.com/a/3866442/104380 | ||
setRangeAtEnd(){ | ||
setRangeAtStartEnd( start=false, node ){ | ||
var range, selection; | ||
@@ -376,4 +414,4 @@ | ||
range = document.createRange(); | ||
range.selectNodeContents(this.DOM.input); | ||
range.collapse(false); | ||
range.selectNodeContents(node || this.DOM.input); | ||
range.collapse(start); | ||
selection = window.getSelection(); | ||
@@ -424,3 +462,3 @@ selection.removeAllRanges(); | ||
this.dropdown.hide.call(this); | ||
this.input.setRangeAtEnd.call(this); | ||
this.input.setRangeAtStartEnd.call(this); | ||
} | ||
@@ -555,3 +593,2 @@ | ||
// if the value is a "simple" String, ex: "aaa, bbb, ccc" | ||
@@ -562,7 +599,7 @@ if( typeof tagsItems == 'string' ){ | ||
// go over each tag and add it (if there were multiple ones) | ||
return tagsItems.split(this.settings.delimiters).filter(n => n).map(v => ({ value:v.trim() })); | ||
tagsItems = tagsItems.split(this.settings.delimiters).filter(n => n).map(v => ({ value:v.trim() })); | ||
} | ||
if( tagsItems instanceof Array ) | ||
return tagsItems.map(v => ({ value:v.trim() })) | ||
else if( tagsItems instanceof Array ) | ||
tagsItems = tagsItems.map(v => ({ value:v.trim() })) | ||
@@ -575,3 +612,3 @@ // search if the tag exists in the whitelist as an Object (has props), to be able to use its properties | ||
temp.push( matchObj[0] ); // set the Array (with the found Object) as the new value | ||
else | ||
else if( this.settings.mode != 'mix' ) | ||
temp.push(tag) | ||
@@ -582,5 +619,69 @@ }) | ||
} | ||
return tagsItems; | ||
}, | ||
parseMixTags( s ){ | ||
var htmlString = ''; | ||
s = s.split( this.settings.pattern ); | ||
// this.DOM.scope.innerHTML | ||
htmlString = s.shift() + s.map(part => { | ||
var tagElm, i, normalizedTag; | ||
if( !part ) return ''; | ||
for( i in part ){ | ||
if( part[i].match(/,|\.| /) ){ | ||
normalizedTag = this.normalizeTags.call(this, part.substr(0, i))[0]; | ||
if( normalizedTag ) tagElm = this.createTagElem(normalizedTag); | ||
else i = 0; // a tag was found but was not in the whitelist, so reset the "i" index | ||
break; | ||
} | ||
} | ||
return tagElm ? tagElm.outerHTML + part.slice(i) : this.settings.pattern + part | ||
}).join(''); | ||
return htmlString; | ||
}, | ||
/** | ||
* Add a tag where it might be beside textNodes | ||
*/ | ||
addMixTag( tagData ){ | ||
if( !tagData ) return; | ||
var sel = window.getSelection(), | ||
node = sel.focusNode, | ||
nodeText = node.textContent, | ||
wrapElm = document.createDocumentFragment(), | ||
tagElm = this.createTagElem(tagData), | ||
textNodeBefore, | ||
textNodeAfter, | ||
range, | ||
parrernLen = this.settings.pattern.length; | ||
if( sel.rangeCount > 0 ){ | ||
range = sel.getRangeAt(0).cloneRange(); | ||
range.collapse(true); | ||
range.setStart(node, 0); | ||
textNodeBefore = range.toString().slice(0, - this.state.tag.length - parrernLen); | ||
textNodeAfter = nodeText.slice(textNodeBefore.length + this.state.tag.length + parrernLen, nodeText.length); | ||
textNodeBefore = document.createTextNode(textNodeBefore); | ||
textNodeAfter = document.createTextNode(textNodeAfter.trim() ? textNodeAfter : " \u200b"); | ||
wrapElm.appendChild( textNodeBefore ); | ||
wrapElm.appendChild( tagElm ); | ||
wrapElm.appendChild( textNodeAfter ); | ||
} | ||
node.parentNode.replaceChild(wrapElm, node); | ||
this.input.setRangeAtStartEnd.call(this, true, textNodeAfter); | ||
}, | ||
/** | ||
* add a "tag" element to the "tags" component | ||
@@ -594,6 +695,9 @@ * @param {String/Array} tagsItems [A string (single or multiple values with a delimiter), or an Array of Objects or just Array of Strings] | ||
tagsItems = this.normalizeTags.call(this, tagsItems); | ||
if( this.settings.mode == 'mix' ) | ||
return this.addMixTag(tagsItems[0]); | ||
this.DOM.input.removeAttribute('style'); | ||
tagsItems = this.normalizeTags.call(this, tagsItems); | ||
tagsItems.forEach(tagData => { | ||
@@ -654,2 +758,6 @@ var tagValidation, tagElm; | ||
minify( html ){ | ||
return html.replace( new RegExp( "\>[\r\n ]+\<" , "g" ) , "><" ); | ||
}, | ||
/** | ||
@@ -660,6 +768,6 @@ * creates a DOM tag element and injects it into the component (this.DOM.scope) | ||
*/ | ||
createTagElem(tagData){ | ||
createTagElem( tagData ){ | ||
var tagElm, | ||
v = this.escapeHtml(tagData.value), | ||
template = `<tag title='${v}'> | ||
template = `<tag title='${v}' contenteditable='false'> | ||
<x title=''></x><div><span>${v}</span></div> | ||
@@ -685,2 +793,3 @@ </tag>`; | ||
template = this.minify(template); | ||
tagElm = this.parseHTML(template); | ||
@@ -900,4 +1009,4 @@ | ||
if( listItemElm ){ | ||
this.input.set.call(this) | ||
this.addTags( listItemElm.textContent ); | ||
this.addTags( listItemElm.textContent, true ); | ||
this.dropdown.hide.call(this); | ||
} | ||
@@ -904,0 +1013,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
368100
18
2876
191