Files
medicalalert-web-reloaded/wp/wp-content/plugins/facetwp/assets/vendor/fSelect/fSelect.js
Tony Volpe 779393381f Merged in feature/plugins-update (pull request #9)
wp plugin updates from pantheon

* wp plugin updates from pantheon
2023-12-15 18:08:21 +00:00

421 lines
15 KiB
JavaScript

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');
// add a relationship link
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');
// don't auto-focus for touch devices
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;
// skip the first choice in multi-select mode
if (0 === build.idx && '' === val && that.settings.multiple) {
build.idx++;
return;
}
var classes = ['fs-option', 'g' + build.optgroup];
// append existing classes
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) {
// loop parent nodes from the target to the delegation node
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, '');
// debounce search for better performance
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 the searchCache already has a prefixed version of this search
// then don't un-hide the existing exclusions
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');
}
}
// hide optgroups if no choices
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');
}
});
// toggle the noResultsText div
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;
// fire a change event
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) { // space, enter
if (e.target.closest('.fs-search')) {
// preserve spaces for 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) { // up
var sibling = wrap._rel.fselect.getAdjacentSibling('previous');
window.fSelectInit.lastFocus = sibling; // stop at the search box
if (sibling) {
sibling.focus();
}
else {
wrap.querySelector('.fs-search input').focus();
}
}
else if (40 == e.which) { // down
var sibling = wrap._rel.fselect.getAdjacentSibling('next');
if (sibling) {
sibling.focus();
window.fSelectInit.lastFocus = sibling; // stop at the bottom
}
}
else if (9 == e.which || 27 == e.which) { // tab, esc
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() { // no arrow function to preserve "this"
fSelect(this, opts);
});
return this;
};
}