window.fSelect=(()=> {
var build={};
class fSelect {
constructor(selector, options){
let that=this;
var defaults={
placeholder: 'Select some options',
numDisplayed: 3,
overflowText: '{n} selected',
searchText: 'Search',
noResultsText: 'No results found',
showSearch: true,
optionFormatter: false
};
that.settings=Object.assign({}, defaults, options);
build={output: '', optgroup: 0, idx: 0};
if('string'===typeof selector){
var nodes=Array.from(document.querySelectorAll(selector));
}
else if(selector instanceof Node){
var nodes=[selector];
}
else if(Array.isArray(selector)){
var nodes=selector;
}else{
var nodes=[];
}
if('undefined'===typeof window.fSelectInit){
window.fSelectInit={
searchCache: '',
lastChoice: null,
lastFocus: null,
activeEl: null
};
that.bindEvents();
}
nodes.forEach((input)=> {
if(typeof input.fselect==='object'){
input.fselect.destroy();
}
that.settings.multiple=input.matches('[multiple]');
input.fselect=that;
that.input=input;
that.create();
});
}
create(){
var that=this;
var options=that.buildOptions();
var label=that.getDropdownLabel();
var mode=(that.settings.multiple) ? 'multiple':'single';
var searchClass=(that.settings.showSearch) ? '':' fs-hidden';
var noResultsClass=(build.idx < 2) ? '':' fs-hidden';
var html=`
<div class="fs-wrap ${mode}" tabindex="0">
<div class="fs-label-wrap">
<div class="fs-label">${label}</div>
<span class="fs-arrow"></span>
</div>
<div class="fs-dropdown fs-hidden">
<div class="fs-search${searchClass}">
<input type="text" placeholder="${that.settings.searchText}" />
</div>
<div class="fs-no-results${noResultsClass}">${that.settings.noResultsText}</div>
<div class="fs-options">${options}</div>
</div>
</div>
`;
var tpl=document.createElement('template');
tpl.innerHTML=html;
var wrap=tpl.content.querySelector('.fs-wrap');
that.input.parentNode.insertBefore(wrap, that.input.nextSibling);
that.input.classList.add('fs-hidden');
that.input._rel=wrap;
wrap._rel=that.input;
}
destroy(){
this.input._rel.remove();
this.input.classList.remove('fs-hidden');
delete this.input._rel;
}
reload(){
this.destroy();
this.create();
}
open(){
var wrap=this.input._rel;
wrap.classList.add('fs-open');
wrap.querySelector('.fs-dropdown').classList.remove('fs-hidden');
if(! window.matchMedia("(pointer: coarse)").matches){
wrap.querySelector('.fs-search input').focus();
}
window.fSelectInit.lastChoice=this.getSelectedOptions('value');
window.fSelectInit.activeEl=wrap;
this.trigger('fs:opened', wrap);
}
close(){
this.input._rel.classList.remove('fs-open');
this.input._rel.querySelector('.fs-dropdown').classList.add('fs-hidden');
window.fSelectInit.searchCache='';
window.fSelectInit.lastChoice=null;
window.fSelectInit.lastFocus=null;
window.fSelectInit.activeEl=null;
this.trigger('fs:closed', this.input._rel);
}
buildOptions(parent){
var that=this;
var parent=parent||that.input;
Array.from(parent.children).forEach((node)=> {
if('optgroup'===node.nodeName.toLowerCase()){
var opt=`<div class="fs-optgroup-label" data-group="${build.optgroup}">${node.label}</div>`;
build.output +=opt;
that.buildOptions(node);
build.optgroup++;
}else{
var val=node.value;
if(0===build.idx&&''===val&&that.settings.multiple){
build.idx++;
return;
}
var classes=['fs-option', 'g' + build.optgroup];
node.className.split(' ').forEach((className)=> {
if(''!==className){
classes.push(className);
}});
if(node.matches('[disabled]')) classes.push('disabled');
if(node.matches('[selected]')) classes.push('selected');
classes=classes.join(' ');
if('function'===typeof that.settings.optionFormatter){
node.label=that.settings.optionFormatter(node.label, node);
}
var opt=`<div class="${classes}" data-value="${val}" data-idx="${build.idx}" tabindex="-1"><span class="fs-checkbox"><i></i></span><div class="fs-option-label">${node.label}</div></div>`;
build.output +=opt;
build.idx++;
}});
return build.output;
}
getSelectedOptions(field, context){
var context=context||this.input;
return Array.from(context.selectedOptions).map((opt)=> {
return (field) ? opt[field]:opt;
});
}
getAdjacentSibling(which){
var that=this;
var which=which||'next';
var sibling=window.fSelectInit.lastFocus;
var selector='.fs-option:not(.fs-hidden):not(.disabled)';
if(sibling){
sibling=sibling[which + 'ElementSibling'];
while (sibling){
if(sibling.matches(selector)) break;
sibling=sibling[which + 'ElementSibling'];
}
return sibling;
}
else if('next'==which){
sibling=that.input._rel.querySelector(selector);
}
return sibling;
}
getDropdownLabel(){
var that=this;
var labelText=that.getSelectedOptions('text');
if(labelText.length < 1){
labelText=that.settings.placeholder;
}
else if(labelText.length > that.settings.numDisplayed){
labelText=that.settings.overflowText.replace('{n}', labelText.length);
}else{
labelText=labelText.join(', ');
}
return labelText;
}
debounce(func, wait){
var timeout;
return (...args)=> {
var boundFunc=func.bind(this, ...args);
clearTimeout(timeout);
timeout=setTimeout(boundFunc, wait);
}}
trigger(eventName, ...args){
document.dispatchEvent(new CustomEvent(eventName, {detail: [...args]}));
}
on(eventName, elementSelector, handler){
document.addEventListener(eventName, function(e){
for (var target=e.target; target&&target!=this; target=target.parentNode){
if(target.matches(elementSelector)){
handler.call(target, e);
break;
}}
}, false);
}
bindEvents(){
var that=this;
var optionSelector='.fs-option:not(.fs-hidden):not(.disabled)';
var unaccented=(str)=> str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
that.on('keyup', '.fs-search input', that.debounce(function(e){
var wrap=e.target.closest('.fs-wrap');
var options=wrap._rel.options;
var matchOperators=/[|\\{}()[\]^$+*?.]/g;
var keywords=e.target.value.replace(matchOperators, '\\$&');
keywords=unaccented(keywords);
if(0!==keywords.indexOf(window.fSelectInit.searchCache)){
wrap.querySelectorAll('.fs-option, .fs-optgroup-label').forEach((node)=> node.classList.remove('fs-hidden'));
}
window.fSelectInit.searchCache=keywords;
for (var i=0; i < options.length; i++){
if(''===options[i].value) continue;
var needle=new RegExp(keywords, 'gi');
var haystack=unaccented(options[i].text);
if(null===haystack.match(needle)){
wrap.querySelector('.fs-option[data-idx="' + i + '"]').classList.add('fs-hidden');
}}
wrap.querySelectorAll('.fs-optgroup-label').forEach((node)=> {
var group=node.getAttribute('data-group');
var container=node.closest('.fs-options');
var count=container.querySelectorAll('.fs-option.g' + group + ':not(.fs-hidden)').length;
if(count < 1){
node.classList.add('fs-hidden');
}});
if(wrap.querySelectorAll('.fs-option:not(.fs-hidden').length){
wrap.querySelector('.fs-no-results').classList.add('fs-hidden');
}else{
wrap.querySelector('.fs-no-results').classList.remove('fs-hidden');
}}, 100));
that.on('click', optionSelector, function(e){
var wrap=this.closest('.fs-wrap');
var value=this.getAttribute('data-value');
var input=wrap._rel;
var isMultiple=wrap.classList.contains('multiple');
if(!isMultiple){
input.value=value;
wrap.querySelectorAll('.fs-option.selected').forEach((node)=> node.classList.remove('selected'));
}else{
var idx=parseInt(this.getAttribute('data-idx'));
input.options[idx].selected = !this.classList.contains('selected');
}
this.classList.toggle('selected');
var label=input.fselect.getDropdownLabel();
wrap.querySelector('.fs-label').innerHTML=label;
input.dispatchEvent(new Event('change', { bubbles: true }));
input.fselect.trigger('fs:changed', wrap);
if(!isMultiple){
input.fselect.close();
}
e.stopImmediatePropagation();
});
that.on('keydown', '*', function(e){
var wrap=this.closest('.fs-wrap');
if(!wrap) return;
if(-1 < [38, 40, 27].indexOf(e.which)){
e.preventDefault();
}
if(32==e.which||13==e.which){
if(e.target.closest('.fs-search')){
}
else if(e.target.matches(optionSelector)){
e.target.click();
e.preventDefault();
}else{
wrap.querySelector('.fs-label').click();
e.preventDefault();
}}
else if(38==e.which){
var sibling=wrap._rel.fselect.getAdjacentSibling('previous');
window.fSelectInit.lastFocus=sibling;
if(sibling){
sibling.focus();
}else{
wrap.querySelector('.fs-search input').focus();
}}
else if(40==e.which){
var sibling=wrap._rel.fselect.getAdjacentSibling('next');
if(sibling){
sibling.focus();
window.fSelectInit.lastFocus=sibling;
}}
else if(9==e.which||27==e.which){
wrap._rel.fselect.close();
}});
that.on('click', '*', function(e){
var wrap=this.closest('.fs-wrap');
var lastActive=window.fSelectInit.activeEl;
if(wrap){
var labelWrap=this.closest('.fs-label-wrap');
if(labelWrap){
if(lastActive){
lastActive._rel.fselect.close();
}
if(wrap!==lastActive){
wrap._rel.fselect.open();
}}
}else{
if(lastActive){
lastActive._rel.fselect.close();
}}
});
}}
var $=(selector, options)=> new fSelect(selector, options);
return $;
})();
if('undefined'!==typeof fUtil){
fUtil.fn.fSelect=function(opts){
this.each(function(){
fSelect(this, opts);
});
return this;
};};