File: /python/moda/public_html/tech/old/assets/js/whmcs/selectize.js
/**
* Selectize module
*
* Most basic usage:
* `WHMCS.selectize.register('#mySelect');`
* - This will selectize the <select id="mySelect"></select>. See
* .register() for more specifics.
*
* Pre-made usage:
* `WHMCS.selectize.clientSearch();`
* - selectize all '.selectize-client-search'
*
* `WHMCS.selectize.users(selector, options)`
* - selectize a given selector with a static array of options (user objects)
*
*/
(function(module) {
if (!WHMCS.hasModule('selectize')) {
WHMCS.loadModule('selectize', module);
}
})(
function () {
/**
* Applying fix
* Selectize dropdown selection outside modal window causes the modal to close
* Bug occurs only when same option is being selected twice
* Long known bug https://github.com/selectize/selectize.js/issues/1689
* Solution is taken from https://github.com/selectize/selectize.js/pull/1813
*/
$(document).ready(function () {
const originalSetup = Selectize.prototype.setup;
Selectize.prototype.setup = function () {
originalSetup.apply(this, arguments);
const self = this;
this.$dropdown.off('mousedown', '[data-selectable]')
this.$dropdown.on('mouseup', '[data-selectable]', function() {
return self.onOptionSelect.apply(self, arguments);
});
this.$control.off('mousedown', '*:not(input)');
this.$control.on('mouseup', '*:not(input)', function(e) {
let child = e.target;
while (child && child.parentNode !== self.$control[0]) {
child = child.parentNode;
}
e.currentTarget = child;
return self.onItemSelect.apply(self, arguments);
});
this.$control_input.off('blur');
$(document).off('mousedown' + this.eventNS);
$(document).on('mousedown' + this.eventNS, function(e) {
if (self.isFocused) {
if ((e.target === self.$dropdown[0]) || (e.target.parentNode === self.$dropdown[0])) {
return false;
}
if ((!self.$dropdown.has(e.target).length) && (e.target !== self.$control[0])) {
self.blur(e.target);
}
}
});
}
});
/**
* Search-on-type client select & click "#goButton" on 'change' event
* - will bind to <select> with '.selectize-client-search'
* - <select> needs data-search-url attribute for 'load' event
*
* @returns {Selectize}
*/
this.clientSearch = function () {
var itemDecorator = function(item, escape) {
if (typeof dropdownSelectClient === "function") {
if (jQuery(".selectize-dropdown-content > div").length === 0) {
// updates DOM for admin/supporttickets.php
dropdownSelectClient(
escape(item.id),
escape(item.name)
+ (item.companyname ? ' (' + escape(item.companyname) + ')' : '')
+ (item.id > 0 ? ' - #' + escape(item.id) : ''),
escape(item.email)
);
}
}
return '<div class="client-name"><span class="name">' + escape(item.name) +
(item.companyname ? ' (' + escape(item.companyname) + ')' : '') +
(item.id > 0 ? ' - #' + escape(item.id) : '') + '</span></div>';
};
var selector ='.selectize-client-search';
var selectElement = jQuery(selector);
var module = this;
var selectized = [];
selectElement.each(function (){
var element = $(this);
var configuration = {
'valueField': element.data('value-field'),
allowEmptyOption: (element.data('allow-empty-option') === 1),
'labelField': 'name', //legacy? shouldn't be required with render
'render': {
item: itemDecorator
},
optgroupField: 'status',
optgroupLabelField: 'name',
optgroupValueField: 'id',
optgroups: [
{$order: 1, id: 'active', name: element.data('active-label')},
{$order: 2, id: 'inactive', name: element.data('inactive-label')}
],
'load': module.builder.onLoadEvent(
element.data('search-url'),
function (query) {
return {
dropdownsearchq: query,
clientId: instance.currentValue,
showNoneOption: (element.data('allow-empty-option') === 1),
};
}
),
'onChange': function(value) {
// Updates DOM for admin/supporttickets.php
if (value && typeof dropdownSelectClient === 'function') {
value = parseInt(value);
var newSelection = jQuery(".selectize-dropdown-content div[data-value|='" + value + "']");
dropdownSelectClient(
value,
newSelection.children("span.name").text(),
newSelection.children("span.email").text()
);
}
}
};
var instance = module.clients(element, undefined, configuration);
instance.on('change', module.builder.onChangeEvent(instance, '#goButton'));
return selectized.push(instance);
});
if (selectized.length > 1) {
return selectized;
}
return selectized[0];
};
this.userSearch = function () {
var itemDecorator = function(item, escape) {
var idAppend = '',
isNumeric = !isNaN(item.id);
if (isNumeric && item.id > 0) {
idAppend = ' - #' + escape(item.id);
}
return '<div><span class="name">' + escape(item.name) + idAppend + '</span></div>';
};
var selector ='.selectize-user-search';
var selectElement = jQuery(selector);
var module = this;
var selectized = [];
selectElement.each(function (){
var element = $(this);
var configuration = {
'valueField': element.data('value-field'),
'labelField': 'name',
'render': {
item: itemDecorator
},
'preload': false,
'load': module.builder.onLoadEvent(
element.data('search-url'),
function (query) {
return {
token: csrfToken,
search: query
};
}
)
};
var instance = module.users(selector, undefined, configuration);
return selectized.push(instance);
});
if (selectized.length > 1) {
return selectized;
}
return selectized[0];
};
this.serviceSearch = function () {
var itemDecorator = function(item) {
var newDiv = $('<div>');
if (item.color) {
newDiv.css('background-color', item.color);
}
newDiv.append(
$('<span>').attr('class', 'name').text(item.name)
)
return newDiv;
};
var selector ='.selectize-service-search';
var selectElement = jQuery(selector);
var module = this;
var selectized = [];
selectElement.each(function (){
var element = $(this);
var configuration = {
'valueField': 'id',
'labelField': 'name',
'render': {
item: itemDecorator
},
'preload': true,
'load': module.builder.onLoadEvent(
element.data('search-url'),
function (query) {
return {
token: csrfToken,
search: query
};
}
)
};
var instance = module.services(selector, undefined, configuration);
return selectized.push(instance);
});
if (selectized.length > 1) {
return selectized;
}
return selectized[0];
};
this.productSearch = function() {
var selector = '.selectize-product-search',
selectElement = jQuery(selector),
module = this,
selectized = [],
itemDecorator = function(data, escape) {
var newDiv = jQuery('<div>'),
newSpan = jQuery('<span>').attr('class', 'name').text(escape(data.name));
newDiv.append(newSpan);
return newDiv;
};
selectElement.each(function() {
var element = jQuery(this),
configuration = {
'valueField': 'id',
'labelField': 'name',
'render': {
item: itemDecorator
},
optgroupField: 'groupid',
optgroupLabelField: 'name',
optgroupValueField: 'id',
'preload': true,
'load': module.builder.onLoadEvent(
element.data('search-url'),
function (query) {
return {
token: csrfToken,
search: query,
};
}
),
'onLoad': function(data) {
var instance = this,
listItems = jQuery('.product-recommendations-wrapper li');
data.forEach(function(item) {
if (listItems.find('input[value="' + item.id + '"]').length) {
instance.removeOption(item.id);
return;
}
instance.addOptionGroup(item.groupid, {
$order: item.order,
name: item.group,
});
});
},
'onBlur': function() {
this.clear();
},
'onItemAdd': function(value) {
var listItems = jQuery('.product-recommendations-wrapper li'),
existingItems = listItems.find('input[value="' + value + '"]').length,
recommendationAlert = jQuery('div.recommendation-alert'),
isChanged = false;
if (value && existingItems < 1) {
var newSelection = jQuery(".selectize-dropdown-content div[data-value|='" + value + "']"),
clonableItem = jQuery('.product-recommendations-wrapper .clonable-item'),
parentList = clonableItem.closest('ul'),
clonedItem = clonableItem.clone().removeClass('hidden clonable-item');
clonedItem.find('a span.recommendation-name').text(
newSelection.siblings('div.optgroup-header').text() +
' - ' +
newSelection.children("span.name").text()
);
jQuery('<input>').attr({
type: 'hidden',
name: 'productRecommendations[]',
value: value
}).appendTo(clonedItem);
clonedItem.find('input').val(value);
clonedItem.appendTo(parentList);
instance.removeOption(value);
isChanged = true;
}
if (listItems.length > 0) {
jQuery('.product-recommendations-wrapper .placeholder-list-item').addClass('hidden');
isChanged = true;
}
if (isChanged && recommendationAlert.not(':visible')) {
jQuery('.recommendation-alert').removeClass('hidden');
}
}
},
instance = module.products(selector, undefined, configuration);
return selectized.push(instance);
});
if (selectized.length > 1) {
return selectized;
}
return selectized[0];
};
/**
* Generic selectize of users
* - no 'change' or 'load' events
*
* @param selector
* @param options
* @param configuration
* @returns {Selectize}
*/
this.clients = function (selector, options, configuration) {
var instance = this.register(
selector,
options,
WHMCS.selectize.optionDecorator.client,
configuration
);
instance.settings.searchField = ['name', 'email', 'companyname'];
return instance;
};
this.users = function (selector, options, configuration) {
var instance = this.register(
selector,
options,
WHMCS.selectize.optionDecorator.user,
configuration
);
instance.settings.searchField = ['name', 'email'];
return instance;
};
this.services = function (selector, options, configuration) {
var instance = this.register(
selector,
options,
WHMCS.selectize.optionDecorator.service,
configuration
);
instance.settings.searchField = ['name', 'noResults'];
return instance;
};
this.billingContacts = function (selector, options, configuration) {
var instance = this.register(
selector,
options,
WHMCS.selectize.optionDecorator.billingContact,
configuration
);
instance.settings.searchField = ['name', 'email', 'companyname', 'address'];
return instance;
};
this.payMethods = function (selector, options, configuration) {
var instance = this.register(
selector,
options,
WHMCS.selectize.optionDecorator.payMethod,
configuration
);
instance.settings.searchField = ['description', 'shortAccountNumber', 'type', 'payMethodType'];
return instance;
};
this.products = function (selector, options, configuration) {
var instance = this.register(
selector,
options,
WHMCS.selectize.optionDecorator.product,
configuration
);
instance.settings.searchField = ['id', 'name', 'noResults'];
return instance;
};
this.html = function (selector, options, configuration) {
var instance = this.register(
selector,
options,
function(item, escape) {
return '<div class="item">' + item.html + '</div>';
},
configuration
);
instance.settings.searchField = ['html'];
return instance;
};
this.simple = function (selector, options, configuration) {
var instance = this.register(
selector,
options,
function(item, escape) {
return '<div class="item">' + item.value + '</div>';
},
configuration
);
instance.settings.searchField = ['value'];
return instance;
};
/**
* Arguments:
* selector
* CSS selector of the <select> element to selectize
*
* options
* The second argument is a JS array of objects that will be decorated
* into <option>s.
*
* decorator
* The third argument is the option decorator. By default, it will
* decorate using the userDecorator. Value can be a global function,
* lambda, or fq function. This argument will _not_ be applied when
* configuration supplies the .render.item or .render.option properties
*
* configuration
* configuration settings to use during Selectize initialization
*
*
* Some Assumptions & Default settings:
* settings.valueField and settings.labelField
* These are set to 'id' by default; change as needed
*
* settings.searchField
* Is empty by default; change as needed
*
* option and item decoration
* this.optionDecorator.user will be applied by default if nothing is
* supplied (by means of the decorator arg or within the configuration arg)
*
* @copyright Copyright (c) WHMCS Limited 2005-2018
* @license http://www.whmcs.com/license/ WHMCS Eula
*/
this.register = function (selector, options, decorator, configuration) {
var self = this;
var element = jQuery(selector);
var instance = self.builder.init(element, configuration);
// add item & option decorator if not provided in configuration
var itemDecorator = self.builder.itemDecorator(decorator);
if (typeof configuration === "undefined") {
instance.settings.render.item = itemDecorator;
instance.settings.render.option = itemDecorator;
} else if (typeof configuration.render === "undefined") {
instance.settings.render.item = itemDecorator;
instance.settings.render.option = itemDecorator;
} else {
if (typeof configuration.render.item === "undefined") {
instance.settings.render.item = itemDecorator;
}
if (typeof configuration.render.option === "undefined") {
instance.settings.render.option = itemDecorator;
}
}
this.builder.addOptions(instance, options);
return instance;
};
this.optionDecorator = {
client: function(item, escape) {
var name = escape(item.name),
companyname = '',
descriptor = '',
email = '';
if (item.companyname) {
companyname = ' (' + escape(item.companyname) + ')';
}
if (typeof item.descriptor === "undefined") {
descriptor = (item.id > 0 ? ' - #' + escape(item.id) : '');
} else {
descriptor = escape(item.descriptor);
}
if (item.email) {
email = '<span class="email">' + escape(item.email) + '</span>';
}
return '<div>'
+ '<span class="name">' + name + companyname + descriptor + '</span>'
+ email
+ '</div>';
},
user: function(item, escape) {
var name = escape(item.name),
descriptor = '',
email = '',
isNumericId = !isNaN(item.id);
if (typeof item.descriptor === "undefined") {
descriptor = (isNumericId && item.id > 0 ? ' - #' + escape(item.id) : '');
} else {
descriptor = escape(item.descriptor);
}
if (isNumericId && item.id > 0 && item.email) {
email = '<span class="email">' + escape(item.email) + '</span>';
}
return '<div>'
+ '<span class="name">' + name + descriptor + '</span>'
+ email
+ '</div>';
},
billingContact: function(item, escape) {
var name = escape(item.name),
companyname = '',
descriptor = '',
email = '',
address = '';
if (item.companyname) {
companyname = ' (' + escape(item.companyname) + ')';
}
if (typeof item.descriptor === "undefined") {
descriptor = (item.id > 0 ? ' - #' + escape(item.id) : '');
} else {
descriptor = escape(item.descriptor);
}
if (item.email) {
email = '<span class="email">' + escape(item.email) + '</span>';
}
if (item.address) {
address = '<span class="email">' + escape(item.address) + '</span>';
}
return '<div>'
+ '<span class="name">' + name + companyname + descriptor + '</span>'
+ email
+ address
+ '</div>';
},
payMethod: function(item, escape) {
var brandIcon = '',
description = '',
isDefault = '',
shortAccountNumber = '',
detail1 = '';
if (item.brandIcon) {
brandIcon = '<i class="' + item.brandIcon + '"></i>';
}
if (item.isDefault) {
isDefault = ' <i class="fal fa-user-check"></i>';
}
if (item.description) {
description = item.description;
}
if (item.shortAccountNumber) {
if (description.indexOf(item.shortAccountNumber) === -1) {
shortAccountNumber = '(' + escape(item.shortAccountNumber) + ')';
}
}
if (item.detail1) {
detail1 = '<span class="mouse">' + escape(item.detail1) + '</span>';
}
return '<div>'
+ '<span class="name"> '
+ brandIcon + ' '
+ description + ' '
+ shortAccountNumber + ' '
+ ' ' + detail1 + ' '
+ isDefault
+ '</span>'
+ '</div>';
},
service: function (item, escape) {
var color = '';
if (item.color) {
color = ' style="background-color: ' + item.color + ';"';
}
return '<div' + color + '><span class="name">' + escape(item.name) + '</span>'
+ (item.noResults ? '<span class="email">' + escape(item.noResults) + '</span>' : '') +
'</div>';
},
product: function (item, escape) {
return '<div><span class="name">' + escape(item.name) + '</span>'
+ (item.noResults ? '<span class="email">' + escape(item.noResults) + '</span>' : '') +
'</div>';
}
};
this.builder = {
init: function (element, configuration)
{
var merged,
defaults = {
plugins: ['whmcs_no_results'],
valueField: 'id',
labelField: 'id',
create: false,
maxItems: 1,
preload: 'focus'
};
if (typeof configuration === "undefined") {
configuration = {};
}
merged = jQuery.extend({}, defaults, configuration);
var thisSelectize = element.selectize(merged);
/**
* selectize assigns any items to an array. In order to be able to
* run additional functions on this (like auto-submit and clear).
*
* @link https://github.com/brianreavis/selectize.js/blob/master/examples/api.html
*/
thisSelectize = thisSelectize[0].selectize;
thisSelectize.currentValue = '';
thisSelectize.on('focus', function () {
thisSelectize.currentValue = thisSelectize.getValue();
thisSelectize.clear();
});
thisSelectize.on('blur', function () {
var thisValue = thisSelectize.getValue(),
isNumeric = !(isNaN(thisValue)),
minValue = 1;
if (element.data('allow-empty-option') === 1) {
minValue = 0;
}
if (
thisValue === ''
|| (isNumeric && (thisValue < minValue))
) {
thisSelectize.setValue(thisSelectize.currentValue);
}
});
return thisSelectize;
},
addOptions: function (selectize, options) {
if (typeof options !== "undefined" && options.length) {
selectize.addOption(options);
}
},
itemDecorator: function (decorator) {
if (typeof decorator === "function") {
return decorator;
} else if (typeof decorator === "undefined") {
return WHMCS.selectize.optionDecorator.user;
}
},
onLoadEvent: function (searchUrl, dataCallback) {
return function (query, callback) {
jQuery.ajax({
url: searchUrl,
type: 'POST',
dataType: 'json',
data: dataCallback(query),
error: function () {
callback();
},
success: function (res) {
callback(res);
}
});
};
},
onChangeEvent: function (instance, onChangeSelector) {
var onChange;
if (typeof onChangeSelector !== "undefined") {
onChange = function (value) {
var changeSelector = jQuery(onChangeSelector);
if (changeSelector.length) {
if (
!(isNaN(instance.currentValue))
&& instance.currentValue > 0
&& (value.length && value !== instance.currentValue)
) {
changeSelector.click();
}
}
}
}
return onChange;
}
};
return this;
});