194 lines
9.5 KiB
JavaScript
Executable File
194 lines
9.5 KiB
JavaScript
Executable File
define([
|
|
"jquery"
|
|
], function($){
|
|
|
|
/*
|
|
* jQuery.multiselect plugin
|
|
*
|
|
* Form control: allow select several values from list and add new value(s) to list
|
|
*
|
|
* Licensed under the BSD License:
|
|
* http://www.opensource.org/licenses/bsd-license
|
|
*
|
|
* Version: 0.9.0
|
|
*
|
|
* @author Dmitry (dio) Levashov, dio@std42.ru
|
|
* @example
|
|
* html: <select name="my-select" multiple="on"><option .... </select>
|
|
* js : $('select[name="my-select"]').multiselect()
|
|
* or
|
|
* var opts = { ... };
|
|
* $('select[name="my-select"]').multiselect(opts);
|
|
*/
|
|
$.fn.multiselect = function(opts) {
|
|
var o = $.extend({
|
|
mselectHiddenClass: 'mselect-hidden',
|
|
mselectItemNotEditableClass: 'mselect-list-item-not-editable',
|
|
mselectItemNotRemovableClass: 'mselect-list-item-not-removable',
|
|
mselectListClass: 'mselect-list',
|
|
mselectItemsWrapperClass: 'mselect-items-wrapper',
|
|
mselectButtonAddClass: 'mselect-button-add',
|
|
mselectInputContainerClass: 'mselect-input-container',
|
|
mselectInputClass: 'mselect-input',
|
|
mselectButtonCancelClass: 'mselect-cancel',
|
|
mselectButtonSaveClass: 'mselect-save',
|
|
mselectListItemClass: 'mselect-list-item',
|
|
mselectItemsWrapperOverflowClass: 'mselect-fixed',
|
|
mselectDisabledClass: 'mselect-disabled',
|
|
mselectCheckedClass: 'mselect-checked',
|
|
layout: '<section class="block %mselectListClass%">'
|
|
+'<div class="block-content"><div class="%mselectItemsWrapperClass%">'
|
|
+'%items%'
|
|
+'</div></div>'
|
|
+'<footer class="block-footer">'
|
|
+'<span class="action-add %mselectButtonAddClass%">%addText%</span>'
|
|
+'</footer>'
|
|
+'<div class="%mselectInputContainerClass%">'
|
|
+'<input type="text" class="%mselectInputClass%" title="%inputTitle%"/>'
|
|
+'<span class="%mselectButtonCancelClass%" title="%cancelText%"></span>'
|
|
+'<span class="%mselectButtonSaveClass%" title="Add"></span>'
|
|
+'</div>'
|
|
+'</section>',
|
|
item : '<div class="%mselectListItemClass% %mselectDisabledClass% %iseditable% %isremovable%"><label><input type="checkbox" class="%mselectCheckedClass%" value="%value%" %checked% %disabled% /><span>%label%</span></label>' +
|
|
'<span class="mselect-edit" title="Edit">Edit</span>' +
|
|
'<span class="mselect-delete" title="Delete">Delete</span> ' +
|
|
'</div>',
|
|
addText: 'Add new value',
|
|
cancelText: 'Cancel',
|
|
inputTitle: 'Enter new option',
|
|
size: 5,
|
|
keyCodes: {
|
|
Enter: 13,
|
|
Esc: 27
|
|
},
|
|
toggleAddButton: true,
|
|
// New option for callback
|
|
mselectInputSubmitCallback: null,
|
|
parse : function(v) { return v.split(/\s*,\s*/); }
|
|
}, opts||{});
|
|
|
|
return this.filter('select[multiple]:not(.' + o.mselectHiddenClass + ')').each(function() {
|
|
var select = $(this).addClass(o.mselectHiddenClass).hide(),
|
|
size = select.attr('size') > 0 ? select.attr('size') : o.size,
|
|
items = (function() {
|
|
var str = '';
|
|
|
|
select.children('option').each(function(i, option) {
|
|
option = $(option);
|
|
|
|
str += o.item
|
|
.replace(/%value%/gi, option.val())
|
|
.replace(/%checked%/gi, option.prop('selected') ? 'checked' : '')
|
|
.replace(/%mselectCheckedClass%/gi, option.prop('selected') ? ''+o.mselectCheckedClass+'' : '')
|
|
.replace(/%disabled%/gi, option.prop('disabled') ? 'disabled' : '')
|
|
.replace(/%mselectDisabledClass%/gi, option.prop('disabled') ? ''+o.mselectDisabledClass+'' : '')
|
|
.replace(/%mselectListItemClass%/gi, o.mselectListItemClass)
|
|
.replace(/%iseditable%/gi, option.attr('data-is-editable') ? ''+o.mselectItemNotEditableClass+'' : '')
|
|
.replace(/%isremovable%/i, option.attr('data-is-removable') ? ''+o.mselectItemNotRemovableClass+'' : '')
|
|
.replace(/%label%/gi, option.html());
|
|
});
|
|
|
|
return str;
|
|
})(),
|
|
html = o.layout
|
|
.replace(/%items%/gi, items)
|
|
.replace(/%mselectListClass%/gi, o.mselectListClass)
|
|
.replace(/%mselectButtonAddClass%/gi, o.mselectButtonAddClass)
|
|
.replace(/%mselectButtonSaveClass%/gi, o.mselectButtonSaveClass)
|
|
.replace(/%mselectButtonCancelClass%/gi, o.mselectButtonCancelClass)
|
|
.replace(/%mselectItemsWrapperClass%/gi, o.mselectItemsWrapperClass)
|
|
.replace(/%mselectInputContainerClass%/gi, o.mselectInputContainerClass)
|
|
.replace(/%mselectInputClass%/gi, o.mselectInputClass)
|
|
.replace(/%addText%/gi, o.addText)
|
|
.replace(/%cancelText%/gi, o.cancelText)
|
|
.replace(/%inputTitle%/gi, o.inputTitle),
|
|
widget = $(html)
|
|
.insertAfter(this)
|
|
.on('change.mselectCheck', '[type=checkbox]', function() {
|
|
var checkbox = $(this),
|
|
index = checkbox.closest('.' + o.mselectListItemClass + '').index();
|
|
|
|
select.find('option').eq(index).prop('selected', !!checkbox.prop('checked'));
|
|
}),
|
|
list = widget.find('.' + o.mselectItemsWrapperClass + ''),
|
|
buttonAdd = widget.find('.' + o.mselectButtonAddClass + '')
|
|
.on('click.mselectAdd', function(e) {
|
|
e.preventDefault();
|
|
o.toggleAddButton && buttonAdd.hide();
|
|
container.show();
|
|
input.trigger('focus');
|
|
if (input.parents(o.mselectListClass).length) {
|
|
list.scrollTop(list.height());
|
|
}
|
|
}),
|
|
container = widget.find('.' + o.mselectInputContainerClass + ''),
|
|
input = container.find('[type=text].' + o.mselectInputClass + '')
|
|
.on('blur.mselectReset', function() {
|
|
reset();
|
|
})
|
|
.on('keydown.mselectAddNewOption', function(e) {
|
|
var c = e.keyCode;
|
|
|
|
if (c == o.keyCodes.Enter || c == o.keyCodes.Esc) {
|
|
e.preventDefault();
|
|
c == o.keyCodes.Enter ? append(input.val()) : reset();
|
|
}
|
|
}),
|
|
buttonSave = container.find('.' + o.mselectButtonSaveClass + '')
|
|
.on('mousedown.mselectSave', function(e) {
|
|
append(input.val());
|
|
}),
|
|
buttonCancel = container.find('.' + o.mselectButtonCancelClass + '')
|
|
.on('mousedown.mdelectCancel', function(e) {
|
|
input.val('');
|
|
}),
|
|
append = function(v) {
|
|
// Add ability to define custom handler for adding new values
|
|
if ($.isFunction(o.mselectInputSubmitCallback)) {
|
|
o.mselectInputSubmitCallback(v, o);
|
|
return;
|
|
}
|
|
// end of callback implementation
|
|
$.each(typeof(o.parse) == 'function' ? o.parse(v) : [$.trim(v)], function(i, v) {
|
|
var item;
|
|
|
|
if (v && !select.children('[value="' + v + '"]').length) {
|
|
item = $(o.item.replace(/%value%|%label%/gi, v)
|
|
.replace(/%mselectDisabledClass%|%iseditable%|%isremovable%/gi,'')
|
|
.replace(/%mselectListItemClass%/gi,o.mselectListItemClass))
|
|
.find('[type=checkbox]')
|
|
.addClass(o.mselectCheckedClass)
|
|
.prop('checked', true)
|
|
.end();
|
|
|
|
list.children('.' + o.mselectListItemClass + '').length
|
|
? list.children('.' + o.mselectListItemClas ).last().after(item)
|
|
: list.prepend(item);
|
|
|
|
select.append('<option value="' + v + '" selected="selected">' + v + '</option>');
|
|
}
|
|
});
|
|
|
|
reset();
|
|
list.scrollTop(list.height());
|
|
},
|
|
reset = function() {
|
|
var ch = select.children();
|
|
|
|
input.val('');
|
|
container.hide();
|
|
buttonAdd.show();
|
|
list[list.children().length ? 'show' : 'hide']();
|
|
|
|
if (ch.length >= size && !list.hasClass(o.mselectItemsWrapperOverflowClass)) {
|
|
list.height(list.children('.' + o.mselectListItemClass)
|
|
.first()
|
|
.outerHeight(true) * size)
|
|
.addClass(o.mselectItemsWrapperOverflowClass);
|
|
}
|
|
};
|
|
reset();
|
|
}).end();
|
|
};
|
|
});
|