/*! @preserve
* jquery-consistent-listbox v0.1.2-dev (https://github.com/SimonLitt/jquery-consistent-listbox/)
* Copyright 2025 Simon Litt.
* @license Licensed under MIT (https://github.com/SimonLitt/jquery-consistent-listbox/blob/main/LICENSE)
*/
(function( $ ) {
/**
* The jQuery UI singleselect and multiselect listbox plugin.
* @namespace jquery-consistent-listbox
*/
/**
* @typedef {Object} lb_item
* @name jquery-consistent-listbox#lb_item
* @property {sting} val - Associated value. When used inside a HTML form, it will be passed in the request.
* @property {sting} text - Display text.
* @property {sting} class - Optional. Custom css class of the item.
* @property {number} sort_order - Optional. Custom sort order.
* @property {object} data - Optional. Related user-defined data. Automatic or manual sorting changes or creates the `sort_order` property.
*/
$.widget( "simonlitt.listbox", {
options: {
/**
* @name jquery-consistent-listbox#items
* @type {lb_item[]}
* @description The initial array of item objects to populate the listbox. Used for initialization only.
*/
/**
* @name jquery-consistent-listbox#multiSelect
* @type {sting}
* @description This is a multiselect listbox, otherwise it is a singleselect listbox. Default false.
*/
multiSelect: false,
/**
* @name jquery-consistent-listbox#name
* @type {boolean}
* @description The HTML `name` attribute identifier. Default `lb_name<uuid>`. Note: When there is more than one listbox with the same name on one page, there will be display and functionality issues!
*/
name: '',
/**
* @name jquery-consistent-listbox#quiet
* @type {boolean}
* @description Disable call `onChange`. Default false.
*/
quiet: false,
/**
* @name jquery-consistent-listbox#autoSort
* @type {boolean}
* @description Enable auto sorting. Default false.
*/
autoSort: false,
/**
* @name jquery-consistent-listbox#sortOrder
* @type {string}
* @description Sort data key. Default null.
*/
sortOrder: null,
/**
* @name jquery-consistent-listbox#isSortByDataProp
* @type {boolean}
* @description Determines whether sorting will be done by `sort_order` property of the related data. Default null, what does sort by item text mean. If true, then sort by `sort_order` property of the related data. If false, then sort by value.
*/
isSortByDataProp: null,
/**
* @name jquery-consistent-listbox#sortable
* @type {boolean}
* @description Allowed to sort manually by dragging items. Default false.
*/
sortable: false,
/**
* @name jquery-consistent-listbox#sortOrderStep
* @type {number}
* @description `sort_order` data key increment step for auto sorting. Default 10.
*/
sortOrderStep: 10,
},
_create: function() {
this._item_data = new Map();
if (this.options.items) {
this._load_items(this.options.items);
delete this.options.items;
}
this.element.addClass('ui-menu').addClass('ui-widget').addClass('ui-widget-content').addClass('ui-corner-all');
this.element.delegate('input', 'change', $.proxy( this._itemChange, null, this));
this.element.delegate('input', 'click', $.proxy( this._itemClick, null, this));
if (this.options.sortable) {
if(!this.options.autoSort) {
this._set_sortable();
} else {
this._sort_opt_warning();
}
}
this._last_val = this.options.multiSelect ? this._sorted_vals() : this.val();
this._wait_reorder = false;
},
_sort_opt_warning: function() {
console.warn('simonlitt.listbox widget: because of the "autoSort" option the "sortable" option is ignored!');
},
_get_sort_key: function() {
return this.options.sortOrder ? this.options.sortOrder : 'sort_order';
},
_reorder: function() {
let that = this;
let sort_order = 1;
this.element.find('input').each(function() {
that.setItemDataVar($(this).val(), that._get_sort_key(), sort_order);
sort_order += that.options.sortOrderStep;
});
},
_set_sortable: function() {
this.element.addClass('lb-sortable');
let that = this;
this.element.sortable({
change: function(e, ui) {
this._wait_reorder = true;
},
stop: function(e, ui) {
if (this._wait_reorder) {
this._wait_reorder = false;
that._reorder();
}
}
});
this.element.disableSelection();
},
_sortable_html: function() {
return '<i class="drag-icon"></i>';
},
_switch_to_unsortable: function() {
this.element.sortable('destroy');
this.element.find('label.ui-menu-item > i.drag-icon').remove();
this.element.removeClass('lb-sortable');
},
_switch_to_sortable: function() {
this.element.find('label.ui-menu-item').append(this._sortable_html());
this._set_sortable();
},
_get_html_name: function() {
return this.options.name ? this.options.name : ('lb_name' + this.uuid);
},
_item_html: function(text, value, html_class, is_checked) {
return '<label class="ui-menu-item ui-menu-item-wrapper ui-corner-all' + (is_checked ? ' ui-state-active' : '') + (html_class ? (' ' + html_class) : '') + '" title="' + text + '"><input type="' + (this.options.multiSelect ? 'checkbox' : 'radio') + '" name="' + this._get_html_name(this.options.name) + '" class="form-check-input" value="' + value + '"' + (is_checked ? ' checked="checked"' : '') + '/><span class="item-text">' + text + '</span>' + ((this.options.sortable && !this.options.autoSort) ? this._sortable_html() : '') + '</label>';
},
/**
* Gets the specified item index.
* See also: {@link jquery-consistent-listbox#getItemIndex|getItemIndex()}.
* @name jquery-consistent-listbox#index
* @function
* @returns {boolean} The item index.
*/
index: function() {
return this.getItemIndex(this.val());
},
/**
* Gets an item index by value.
* See also: {@link jquery-consistent-listbox#index|index()}.
* @name jquery-consistent-listbox#getItemIndex
* @function
* @param {string} value - Item value.
* @returns {number} The item index.
*/
getItemIndex: function(value) {
let selected_input = this.element.find('input[value=\'' + value + '\']');
return selected_input.closest('.ui-menu-item').index();
},
/**
* Gets the first item value.
* See also: {@link jquery-consistent-listbox#last|last()}.
* @name jquery-consistent-listbox#first
* @function
* @returns {string} The first item value.
*/
first: function() {
let indexed_ctrl = this.element.find('label.ui-menu-item').first();
return indexed_ctrl.length ? (indexed_ctrl.find('input.form-check-input').val()) : null;
},
/**
* Gets the last item value.
* See also: {@link jquery-consistent-listbox#first|first()}.
* @name jquery-consistent-listbox#last
* @function
* @returns {string} The last item value.
*/
last: function() {
let indexed_ctrl = this.element.find('label.ui-menu-item').last();
return indexed_ctrl.length ? (indexed_ctrl.find('input.form-check-input').val()) : null;
},
_insert: function(index, item) {
let value = item.hasOwnProperty('val') ? String(item.val) : '';
if (this._item_data.has(value)) {
return false;
}
let text = item.hasOwnProperty('text') ? String(item.text) : '';
let is_checked = item.hasOwnProperty('checked') ? item.checked : false;
if (is_checked && !this.options.multiSelect) {
this._uncheck_all();
}
let html_class = item.hasOwnProperty('class') ? String(item.class) : '';
let sort_order = null;
if (item.hasOwnProperty('sort_order')) {
sort_order = parseInt(item.sort_order, 10) || 0;
if (this.options.sortable) {
sort_order = 1 + this.options.sortOrderStep * this.length();
}
}
let html = this._item_html(text, value, html_class, is_checked);
if (index === 0) {
this.element.prepend(html);
} else if (!index) {
this.element.append(html);
} else {
let num_index = parseInt(index, 10) || 0;
num_index++;
let indexed_ctrl = this.element.find('label.ui-menu-item:nth-child(' + num_index + ')');
if (indexed_ctrl.length) {
indexed_ctrl.before(html);
} else {
this.element.append(html);
}
}
let data = item.hasOwnProperty('data') && (typeof item.data == 'object') ? item.data : {};
if (sort_order !== null) {
data[this._get_sort_key()] = sort_order;
}
this._item_data.set(value, data);
return true;
},
/**
* Adds a item.
* @name jquery-consistent-listbox#add
* @function
* @param {lb_item} item - Item data.
* @param {boolean=} [is_quiet=false] - Disable or enable call `onChange`. Default false.
* @param {object=} [e=null] - jQuery event or another data which will be transferred to `onChange`. Default null.
*/
add: function(item, is_quiet = false, e = null) {
if (this._insert(null, item)) {
if (this.options.autoSort) {
this.sort();
}
this._change(is_quiet, e);
}
},
/**
* Inserts a item into the specified position.
* @name jquery-consistent-listbox#insert
* @function
* @param {number} index - Insert position. `autoSort` option can change position after insetrtion.
* @param {lb_item} item - Item data.
* @param {boolean=} [is_quiet=false] - Disable or enable call `onChange`. Default false.
* @param {object=} [e=null] - jQuery event or another data which will be transferred to `onChange`. Default null.
*/
insert: function(index, item, is_quiet = false, e = null) {
if (this._insert(index, item)) {
if (this.options.autoSort) {
this.sort();
}
this._change(is_quiet, e);
}
},
/**
* Removes the selected items.
* See also: {@link jquery-consistent-listbox#delete|delete()}.
* @name jquery-consistent-listbox#remove
* @function
* @param {boolean=} [is_quiet=false] - Disable or enable call `onChange`. Default false.
* @param {object=} [e=null] - jQuery event or another data which will be transferred to `onChange`. Default null.
* @returns {boolean} Whether the selected items were found and removed.
*/
remove: function(is_quiet = false, e = null) {
let chk_list = this.element.find('input:checked');
let has_affected = chk_list.length;
if (has_affected) {
let that = this;
chk_list.each(function() {
that._item_data.delete($(this).val());
});
chk_list.off().parent().remove();
this._change(is_quiet, e);
}
return has_affected;
},
/**
* Deletes the specified item.
* See also: {@link jquery-consistent-listbox#remove|remove()}.
* @name jquery-consistent-listbox#delete
* @function
* @param {string} value - Item value.
* @param {boolean=} [is_quiet=false] - Disable or enable call `onChange`. Default false.
* @param {object=} [e=null] - jQuery event or another data which will be transferred to `onChange`. Default null.
* @returns {boolean} Whether the specified items were found and deleted.
*/
delete: function(value, is_quiet = false, e = null) {
let chk_list = this.element.find('input[value=\'' + value + '\']');
let has_affected = chk_list.length;
if (has_affected) {
this._item_data.delete(String(value));
chk_list.off().parent().remove();
this._change(is_quiet, e);
}
return has_affected
},
_clear: function(e) {
let chk_list = this.element.find('input');
let result = Boolean(chk_list.length);
if (result) {
chk_list.off().parent().remove();
this._item_data.clear();
}
return result;
},
/**
* Deletes all items.
* @name jquery-consistent-listbox#clear
* @function
* @param {boolean=} [is_quiet=false] - Disable or enable call `onChange`.
* @param {object=} [e=null] - jQuery event or another data which will be transferred to `onChange`.
*/
clear: function(is_quiet = false, e = null) {
if (this._clear(e)) {
this._change(is_quiet, e);
}
},
_load_items: function(items) {
if (items && Array.isArray(items) && items.length) {
for (const item of items) {
this._insert(null, item);
}
if (this.options.autoSort) {
this.sort();
}
}
},
/**
* Deletes all items and insert specified new items.
* @name jquery-consistent-listbox#replace
* @function
* @param {boolean=} [is_quiet=false] - Disable or enable call `onChange`. Default false.
* @param {object=} [e=null] - jQuery event or another data which will be transferred to `onChange`. Default null.
*/
replace: function(items, is_quiet = false, e = null) {
this._clear(e);
this._load_items(items);
this._change(is_quiet, e);
},
/**
* Returns the number of items.
* @name jquery-consistent-listbox#length
* @function
* @returns {number} Number of elements.
*/
length: function() {
return this.element.find('input').length;
},
/**
* Returns selected item value.
* @name jquery-consistent-listbox#val
* @function
* @returns {string} Selected item value.
*/
val: function() {
let chk_list = this.element.find('input:checked');
return chk_list.length ? chk_list.val() : null;
},
/**
* Checks if at least one item is selected.
* @name jquery-consistent-listbox#hasSelected
* @function
* @returns {boolean} At least one item is selected.
*/
hasSelected: function() {
return Boolean(this.element.find('input:checked').length);
},
/**
* Returns selected items values.
* @name jquery-consistent-listbox#vals
* @function
* @returns {array} Selected items values.
*/
vals: function() {
let vals = [];
this.element.find('input:checked').each(function() {
vals.push($(this).val());
});
return vals;
},
/**
* Sorts items.
* See also: {@link jquery-consistent-listbox#sortBy|sortBy()}.
* @name jquery-consistent-listbox#sort
* @function
*/
sort: function() {
this.sortBy(this.options.isSortByDataProp)
},
/**
* Sorts items.
* See also: {@link jquery-consistent-listbox#sort|sort()}.
* @name jquery-consistent-listbox#sortBy
* @function
* @param {boolean} [is_sort_by_data_prop=null] - Determines whether sorting will be done by `sort_order` property of the related data. Default null, what does sort by item text mean. If true, then sort by `sort_order` property of the related data. If false, then sort by value.
*/
sortBy: function(is_sort_by_data_prop = null) {
let that = this;
this.element.html($(this.element.find('label.ui-menu-item').toArray().sort(function(a, b) {
let a_val, b_val;
if (is_sort_by_data_prop === null) {
a_val = $(a).find('span.item-text').text();
b_val = $(b).find('span.item-text').text();
} else {
a_val = $(a).find('input.form-check-input').val();
b_val = $(b).find('input.form-check-input').val();
if (is_sort_by_data_prop) {
let a_order = parseInt(that.getItemDataVar(a_val, that._get_sort_key()), 10) || 0;
let b_order = parseInt(that.getItemDataVar(b_val, that._get_sort_key()), 10) || 0;
if (a_order === b_order) {
a_val = $(a).find('span.item-text').text();
b_val = $(b).find('span.item-text').text();
} else {
return a_order - b_order;
}
}
}
return a_val.localeCompare(b_val, undefined, { numeric: true });
})));
},
/**
* Returns `sort_order` property of the related data.
* See also: {@link jquery-consistent-listbox#setSortOrder|setSortOrder()}.
* @name jquery-consistent-listbox#getSortOrder
* @function
* @returns {number} `sort_order` property of the related data.
*/
getSortOrder: function() {
return this.getItemDataVar(this.val(), this._get_sort_key());
},
/**
* Sets `sort_order` property of the related data.
* See also: {@link jquery-consistent-listbox#getSortOrder|getSortOrder()}.
* @name jquery-consistent-listbox#setSortOrder
* @function
* @param {number} sort_order - `sort_order` value.
*/
setSortOrder: function(sort_order) {
if (typeof sort_order !== 'number') {
sort_order = parseInt(sort_order, 10) || 0;
}
this.setItemDataVar(this.val(), this._get_sort_key(), sort_order);
},
/**
* Gets selected item text.
* See also: {@link jquery-consistent-listbox#getItemText|getItemText()}, {@link jquery-consistent-listbox#setText|setText()}.
* @name jquery-consistent-listbox#getText
* @function
* @returns {string} Item text.
*/
getText: function() {
return this.getItemText(this.val());
},
/**
* Sets selected item text.
* See also: {@link jquery-consistent-listbox#setItemText|setItemText()}, {@link jquery-consistent-listbox#getText|getText()}.
* @name jquery-consistent-listbox#setText
* @function
* @param {string} text - New text for the selected item.
*/
setText: function(text) {
this.setItemText(this.val(), text);
},
/**
* Gets item text by value.
* See also: {@link jquery-consistent-listbox#getText|getText()}, {@link jquery-consistent-listbox#setItemText|setItemText()}.
* @name jquery-consistent-listbox#getItemText
* @function
* @param {string} value - An item value.
* @returns {string} Item text.
*/
getItemText: function(value) {
return this.element.find('input[value=\'' + value + '\']').parent().find('span.item-text').text();
},
/**
* Sets item text by value.
* See also: {@link jquery-consistent-listbox#setText|setText()}, {@link jquery-consistent-listbox#getItemText|getItemText()}.
* @name jquery-consistent-listbox#setItemText
* @function
* @param {string} value - An item value.
* @param {string} text - New text for the item.
*/
setItemText: function(value, text) {
this.element.find('input[value=\'' + value + '\']').parent().find('span.item-text').text(text);
},
/**
* Gets assigned data.
* @name jquery-consistent-listbox#getAllData
* @function
* @param {boolean=} [is_as_array=false] - Whether to return the result as an array (sorted as in the listbox control), otherwise return the object (when iterating over the properties of this object, the result will be sorted by value). Default false.
* @param {boolean=} [is_return_text=false] - Update data object field with the item text. Default false.
* @param {string=} [text_alias=""] - From which property of the data to take the value of the text. Default is empty string, which means take it from the `text` property of the data object.
* @returns {(array|object)} Assigned data.
*/
getAllData: function(is_as_array = false, is_return_text = false, text_alias = '') {
let that = this;
let all_data;
if (is_as_array) {
all_data = [];
this.element.find('input').each(function() {
let value = $(this).val();
data = that.getItemData(value);
if (is_return_text) {
data[text_alias ? text_alias : 'text'] = $(this).parent().find('span.item-text').text();
}
all_data.push(data);
});
} else {
all_data = {};
this.element.find('input').each(function() {
let value = $(this).val();
all_data[value] = that.getItemData(value);
if (is_return_text) {
all_data[value][text_alias ? text_alias : 'text'] = $(this).parent().find('span.item-text').text();
}
});
}
return all_data;
},
/**
* Gets selected item object.
* See also: {@link jquery-consistent-listbox#items|items()}.
* @name jquery-consistent-listbox#item
* @function
* @param {boolean=} [is_with_data=true] - Whether to return assigned data. Default true.
* @returns {lb_item} Selected item object.
*/
item: function(is_with_data = true) {
return this._getItem(this.val(), is_with_data);
},
/**
* Gets list of selected items.
* See also: {@link jquery-consistent-listbox#item|item()}.
* @name jquery-consistent-listbox#items
* @function
* @param {boolean=} [is_with_data=true] - Whether to return assigned data. Default true.
* @returns {array} List of items.
*/
items: function(is_with_data = true) {
let that = this;
let items = [];
this.element.find('input:checked').each(function() {
items.push(that._getItem($(this).val(), is_with_data));
});
return items;
},
/**
* Checks if an element with the specified value exists.
* @name jquery-consistent-listbox#hasItem
* @function
* @param {string} value - An item value.
* @returns {boolean} Does the element exist.
*/
hasItem: function(value) {
return Boolean(this.element.find('input[value=\'' + value + '\']').length);
},
_getItem: function(value, is_with_data) {
return is_with_data ? {val: value, text: this.getItemText(value), data: this.getItemData(value)} : {val: value, text: this.getItemText(value)};
},
/**
* Gets item object by an item value.
* @name jquery-consistent-listbox#getItem
* @function
* @param {string} value - An item value.
* @param {boolean=} [is_with_data=true] - Whether to return assigned data. Default true.
* @returns {lb_item} Item object.
*/
getItem: function(value, is_with_data = true) {
if (!this.hasItem(value)) {
return null;
}
return this._getItem(value, is_with_data);
},
/**
* Gets selected item assigned data.
* See also: {@link jquery-consistent-listbox#getItemData|getItemData()}, {@link jquery-consistent-listbox#updateData|updateData()}, {@link jquery-consistent-listbox#replaceData|replaceData()}.
* @name jquery-consistent-listbox#getData
* @function
* @returns {object} Related user-defined data.
*/
getData: function() {
return this.getItemData(this.val());
},
/**
* Updates related user-defined data. All unaffected properties remain unchanged.
* See also: {@link jquery-consistent-listbox#updateItemData|updateItemData()}, {@link jquery-consistent-listbox#getData|getData()}, {@link jquery-consistent-listbox#replaceData|replaceData()}.
* @name jquery-consistent-listbox#updateData
* @function
* @param {object} data - Data object.
* @param {string=} [is_update_text=false] - Whether to update the item text. Default false.
* @param {string=} [text_alias=""] - From which property of the data to take the value of the text. Default is empty string, which means take it from the `text` property of the data object.
*/
updateData: function(data, is_update_text = false, text_alias = '') {
this.updateItemData(this.val(), data, is_update_text, text_alias);
},
/**
* Replaces the user-defined data with a new object.
* See also: {@link jquery-consistent-listbox#replaceItemData|replaceItemData()}, {@link jquery-consistent-listbox#getData|getData()}, {@link jquery-consistent-listbox#updateData|updateData()}.
* @name jquery-consistent-listbox#replaceData
* @function
* @param {object} new_data - Data object.
* @param {string=} [is_update_text=false] - Whether to update the item text. Default false.
* @param {string=} [text_alias=""] - From which property of the data to take the value of the text. Default is empty string, which means take it from the `text` property of the data object.
*/
replaceData: function(new_data, is_update_text = false, text_alias = '') {
this.replaceItemData(this.val(), new_data, is_update_text, text_alias);
},
/**
* Gets item assigned data by an item value.
* See also: {@link jquery-consistent-listbox#updateItemData|updateItemData()}, {@link jquery-consistent-listbox#replaceItemData|replaceItemData()}, {@link jquery-consistent-listbox#getData|getData()}.
* @name jquery-consistent-listbox#getItemData
* @function
* @param {string} value - An item value.
* @returns {object} Related user-defined data.
*/
getItemData: function(value) {
if (value === null) {
return null;
}
let _item_id = String(value);
return this._item_data.has(_item_id) ? this._item_data.get(_item_id) : null;
},
/**
* Updates related user-defined data by item value. All unaffected properties remain unchanged.
* See also: {@link jquery-consistent-listbox#getItemData|getItemData()}, {@link jquery-consistent-listbox#replaceItemData|replaceItemData()}, {@link jquery-consistent-listbox#updateData|updateData()}.
* @name jquery-consistent-listbox#updateItemData
* @function
* @param {string} value - An item value.
* @param {object} data - Data object.
* @param {string=} [is_update_text=false] - Whether to update the item text. Default false.
* @param {string=} [text_alias=""] - From which property of the data to take the value of the text. Default is empty string, which means take it from the `text` property of the data object.
*/
updateItemData: function(value, data, is_update_text = false, text_alias = '') {
if (value !== null) {
let _item_id = String(value);
if (this._item_data.has(_item_id)) {
if (typeof data !== 'object') {
data = {};
}
let field_name = text_alias ? text_alias : 'text';
if (is_update_text && data.hasOwnProperty(field_name)) {
this.setItemText(value, data[field_name]);
}
let data_obj = this._item_data.get(_item_id);
Object.entries(data).forEach(([key, val]) => {
data_obj[key] = val;
});
}
}
},
/**
* Replaces the user-defined data with a new object by item value.
* See also: {@link jquery-consistent-listbox#getItemData|getItemData()}, {@link jquery-consistent-listbox#updateItemData|updateItemData()}, {@link jquery-consistent-listbox#replaceData|replaceData()}.
* @name jquery-consistent-listbox#replaceItemData
* @function
* @param {string} value - An item value.
* @param {object} new_data - Data object.
* @param {string=} [is_update_text=false] - Whether to update the item text. Default false.
* @param {string=} [text_alias=""] - From which property of the data to take the value of the text. Default is empty string, which means take it from the `text` property of the data object.
*/
replaceItemData: function(value, new_data, is_update_text = false, text_alias = '') {
if (value !== null) {
let _item_id = String(value);
if (this._item_data.has(_item_id)) {
if (typeof new_data !== 'object') {
new_data = {};
}
let field_name = text_alias ? text_alias : 'text';
if (is_update_text && new_data.hasOwnProperty(field_name)) {
this.setItemText(value, new_data[field_name]);
}
this._item_data.set(_item_id, new_data);
}
}
},
/**
* Selects the specified item or multiple specified items for multiselect.
* @name jquery-consistent-listbox#select
* @function
* @param {(string|string[])} value - An item value, or an array of item values. If the value is null or undefined, the selection is removed. For singleselect selects the last item in the array.
* @param {boolean=} [is_quiet=false] - Disable or enable call `onChange`. Default false.
* @param {object=} [e=null] - jQuery event or another data which will be transferred to `onChange`. Default null.
*/
select: function(value, is_quiet = false, e = null) {
this._uncheck_all();
this.element.find('input.form-check-input').prop('checked', false);
if (value !== null || value === undefined) {
let list_item;
if (Array.isArray(value)) {
for (const item_val of value) {
list_item = this.element.find('input.form-check-input[value=\'' + item_val + '\']').prop('checked', true);
this._itemSetMark(this, list_item);
}
} else {
list_item = this.element.find('input.form-check-input[value=\'' + value + '\']').prop('checked', true);
this._itemSetMark(this, list_item);
}
}
this._change(is_quiet, e);
},
/**
* Replaces selected item value.
* See also: {@link jquery-consistent-listbox#setItemVal|setItemVal()}.
* @name jquery-consistent-listbox#setVal
* @function
* @param {string} new_value - New item value.
* @param {boolean=} [is_quiet=false] - Disable or enable call `onChange`. Default false.
* @param {object=} [e=null] - jQuery event or another data which will be transferred to `onChange`. Default null.
*/
setVal: function(new_value, is_quiet = false, e = null) {
this.setItemVal(this.val(), new_value, is_quiet, e);
},
/**
* Replaces item value by item value.
* See also: {@link jquery-consistent-listbox#setVal|setVal()}.
* @name jquery-consistent-listbox#setItemVal
* @function
* @param {string} value - An item value.
* @param {string} new_value - New item value.
* @param {boolean=} [is_quiet=false] - Disable or enable call `onChange`. Default false.
* @param {object=} [e=null] - jQuery event or another data which will be transferred to `onChange`. Default null.
*/
setItemVal: function(value, new_value, is_quiet = false, e = null) {
this.element.find('input[value=\'' + value + '\']').val(new_value);;
let old_data = this.getItemData(value);
let _item_id = String(value)
if (old_data) {
this._item_data.delete(String(value));
}
this._item_data.set(String(new_value), old_data);
this._change(is_quiet, e);
},
/**
* Gets a user-defined data property by key.
* See also: {@link jquery-consistent-listbox#getItemDataVar|getItemDataVar()}, {@link jquery-consistent-listbox#unsetDataVar|unsetDataVar()}, {@link jquery-consistent-listbox#setDataVar|setDataVar()}.
* @name jquery-consistent-listbox#getDataVar
* @function
* @param {string} key - Property key.
* @returns Property value.
*/
getDataVar: function(key) {
return this.getItemDataVar(this.val(), key);
},
/**
* Sets a user-defined data property by key.
* See also: {@link jquery-consistent-listbox#setItemDataVar|setItemDataVar()}, {@link jquery-consistent-listbox#unsetDataVar|unsetDataVar()}, {@link jquery-consistent-listbox#getDataVar|getDataVar()}.
* @name jquery-consistent-listbox#setDataVar
* @function
* @param {string} key - Property key.
* @param {string} prop - Property value.
*/
setDataVar: function(key, prop) {
this.setItemDataVar(this.val(), key, prop);
},
/**
* Unsets a user-defined data property property by key.
* See also: {@link jquery-consistent-listbox#getItemDataVar|getItemDataVar()}, {@link jquery-consistent-listbox#setDataVar|setDataVar()}, {@link jquery-consistent-listbox#setDataVar|setDataVar()}.
* @name jquery-consistent-listbox#unsetDataVar
* @function
* @param {string} key - Property key.
*/
unsetDataVar: function(key) {
this.unsetItemDataVar(this.val(), key);
},
/**
* Gets user-defined data property by item value and property key.
* See also: {@link jquery-consistent-listbox#getDataVar|getDataVar()}, {@link jquery-consistent-listbox#setItemDataVar|setItemDataVar()}, {@link jquery-consistent-listbox#setItemDataVar|setItemDataVar()}.
* @name jquery-consistent-listbox#getItemDataVar
* @function
* @param {string} value - An item value.
* @param {string} key - Property key.
* @returns Property value.
*/
getItemDataVar: function(value, key) {
if (value === null) {
return null;
}
let _item_id = String(value);
let item_var = null;
if (this._item_data.has(_item_id)) {
let data = this._item_data.get(_item_id);
if (data.hasOwnProperty(key)) {
item_var = data[key];
}
}
return item_var;
},
/**
* Sets user-defined data property by item value and property key.
* See also: {@link jquery-consistent-listbox#setDataVar|setDataVar()}, {@link jquery-consistent-listbox#unsetItemDataVar|unsetItemDataVar()}, {@link jquery-consistent-listbox#getItemDataVar|getItemDataVar()}.
* @name jquery-consistent-listbox#setItemDataVar
* @function
* @param {string} value - An item value.
* @param {string} key - Property key.
* @param {string} prop - Property value.
*/
setItemDataVar: function(value, key, prop) {
if (prop !== null) {
let _item_id = String(value);
if (this._item_data.has(_item_id)) {
let data = this._item_data.get(_item_id);
data[key] = prop;
}
}
},
/**
* Unsets a user-defined data property property by item value and property key.
* See also: {@link jquery-consistent-listbox#getDataVar|getDataVar()}, {@link jquery-consistent-listbox#setItemDataVar|setItemDataVar()}, {@link jquery-consistent-listbox#setItemDataVar|setItemDataVar()}.
* @name jquery-consistent-listbox#unsetItemDataVar
* @function
* @param {string} value - An item value.
* @param {string} key - Property key.
*/
unsetItemDataVar: function(value, key) {
if (value !== null) {
let _item_id = String(value);
if (this._item_data.has(_item_id)) {
let data = this._item_data.get(_item_id);
if (data.hasOwnProperty(key)) {
delete data[key];
}
}
}
},
_sorted_vals: function() {
return this.vals().sort();
},
_setOption: function(key, value) {
let is_reorder = false;
let is_resort = false;
switch( key ) {
case 'multiSelect':
this.element.find('input').attr('type', value ? 'checkbox' : 'radio');
this._last_val = value ? this._sorted_vals() : this.val();
break;
case 'name':
this.element.find('input.form-check-input').attr('name', this._get_html_name(value));
break;
case 'sortable':
if (value) {
if (!this.options.sortable && !this.options.autoSort) {
this._switch_to_sortable();
}
} else {
if (this.options.sortable && !this.options.autoSort) {
this._switch_to_unsortable();
}
}
break;
case 'autoSort':
if (value) {
if (this.options.sortable && !this.options.autoSort) {
this._switch_to_unsortable();
}
is_resort = true;
} else {
if (this.options.sortable && this.options.autoSort) {
this._switch_to_sortable();
}
}
break;
case 'sortOrderStep':
is_reorder = true;
break;
case 'isSortByDataProp':
if (this.options.autoSort) {
is_resort = true;
}
break;
}
this._super(key, value);
if (is_reorder) {
this._reorder();
}
if (is_resort) {
this.sort();
}
},
_setOptions: function(options) {
var that = this;
$.each(options, function(key, value) {
that._setOption(key, value);
});
},
_destroy: function() {
this.element.removeClass('ui-menu').removeClass('ui-widget').removeClass('ui-widget-content').removeClass('ui-corner-all').removeClass('lb-sortable');
this.element.html('');
this.element.off('click', 'input');
if (this.options.sortable && !this.options.autoSort) {
this.element.sortable('destroy');
}
},
_uncheck_all: function() {
this.element.find('label.ui-state-active').removeClass("ui-state-active");
},
_itemClick: function(that, e) {
let list_item = $(this);
that._trigger('onClick', e, list_item.val());
},
_itemSetMark: function(that, list_item) {
if (that.options.multiSelect) {
if (list_item.prop('checked')) {
list_item.parent().addClass("ui-state-active");
} else {
list_item.parent().removeClass("ui-state-active");
}
} else {
that._uncheck_all();
if (list_item.prop('checked')) {
list_item.parent().addClass("ui-state-active");
}
}
},
_itemChange: function(that, e) {
that._itemSetMark(that, $(this))
that._change(false, e);
},
_change: function(is_quiet, e) {
if (this.options.quiet) {
return;
}
if (this.options.multiSelect) {
let new_vals = this._sorted_vals();
if (new_vals.length !== this._last_val.length || !this._last_val.every(function(value, index) {
return value === new_vals[index];
})) {
if (!is_quiet) {
this._trigger('onChange', e, {cur: new_vals, old: this._last_val});
}
this._last_val = new_vals;
}
} else {
let new_val = this.val();
if (new_val !== this._last_val) {
if (!is_quiet) {
this._trigger('onChange', e, {cur: new_val, old: this._last_val});
}
this._last_val = new_val;
}
}
},
});
}( jQuery ) );