/*
---
description:
- MultiSelect is a MooTools plugin that turns your checkbox set into one single multi-select dropdown menu. MultiSelect is also completely CSS skinnable.
authors:
- Blaž Maležič (http://twitter.com/blazmalezic)
version:
- 1.3.1
license:
- MIT-style license
requires:
core/1.2.1: '*'
provides:
- MultiSelect
...
*/
var MultiSelect = new Class({
Implements: [Options],
options: {
boxes: 'input[type=checkbox]', // checkbox selector
labels: 'label', // label selector
containerClass: 'MultiSelect', // element container CSS class
monitorClass: 'monitor', // monitor CSS class
monitorActiveClass: 'active', // monitor open CSS class
itemSelectedClass: 'selected', // list item selected CSS class
itemHoverClass: 'hover', // list item hover CSS class - usually we would use CSS :hover pseudo class, but we need this for keyboard navigation functionality
defaultTextEmpty: '---', // text which will be shown when no value is selected
defaultTextSelected: 'zvoleno: ' // text which will be shown when no value is selected
},
initialize: function(selector, options) {
// set options
this.setOptions(options);
// set global action variables
this.active = false;
this.action = 'open';
this.state = 'closed';
// get elements array
this.elements = document.getElements(selector);
// off we go...
this.elements.each(function(element) {
this.buildMenu(element);
element.setStyle('overflow', 'visible');
}, this);
},
buildMenu: function(element) {
// create closure
var self = this;
// add container class (for styling)
element.addClass(self.options.containerClass);
// create item instances
var boxes = element.getElements(self.options.boxes);
var labels = element.getElements(self.options.labels);
// list container
var list = new Element('ul', {
'styles': { display: 'none' },
'events': {
'mouseenter': function() { self.action = 'open'; },
'mouseleave': function() {
self.action = 'close';
self.itemHover(this, 'none');
},
'mousedown': function(e) { e.stop(); }, // stop text selection
'selectstart': function() { return false; }, // stop IE text selection
'keydown': function(e) {
if (e.key == 'esc') {
self.toggleMenu('close', monitor, this);
} else if (e.key == 'down' || e.key == 'up') {
self.itemHover(this, e.key);
}
}
}
});
// list items
boxes.each(function(box, i) {
box.addEvents({
'click': function(e) {
e.stop();
},
'keydown': function(e) {
if (e.key == 'space') {
self.active = true;
self.changeItemState(this.getParent(), this, monitor);
}
if (self.active && (e.key == 'down' || e.key == 'up')) {
self.changeItemState(this.getParent(), this, monitor);
}
},
'keyup': function(e) {
if (e.key == 'space') {
self.active = false;
}
}
});
var label = labels[i];
new Element('li', {
'class': box.get('checked') ? self.options.itemSelectedClass : '',
'events': {
'mouseenter': function() {
if (self.active === true) {
self.changeItemState(this, box, monitor);
}
self.itemHover(list, this);
},
'mousedown': function() {
self.active = true;
self.changeItemState(this, box, monitor);
}
}
}).adopt([box, label]).inject(list);
});
// list monitor
var monitor = new Element('div', {
'class': self.options.monitorClass,
'html': self.changeMonitorValue(list) + '',
'tabindex': 0,
'events': {
'mouseenter': function() { self.action = 'open'; },
'mouseleave': function() { self.action = 'close'; },
'click': function() {
if (this.hasClass(self.options.monitorActiveClass)) {
self.toggleMenu('close', monitor, list);
} else {
self.toggleMenu('open', monitor, list);
}
},
'keydown': function(e) {
if (e.key == 'space' || e.key == 'down' || e.key == 'up') {
self.action = 'close';
self.toggleMenu('open', monitor, list);
}
},
'mousedown': function(e) { e.stop(); }, // stop text selection
'selectstart': function() { return false; } // stop IE text selection
}
});
// 'global' events
document.addEvents({
'mouseup': function() { self.active = false; },
'click': function() {
if (self.action == 'close') {
self.toggleMenu('close', monitor, list);
}
},
'keydown': function(e) {
if (e.key == 'esc') {
self.toggleMenu('close', monitor, list);
self.itemHover(list, 'none');
}
if (self.state == 'opened' && (e.key == 'down' || e.key == 'up')) {
e.stop();
}
}
});
// replace element content
element.empty().adopt([monitor, list]);
},
append: function(selector) {
var elements = document.getElements(selector);
this.elements.combine(elements);
elements.each(function(element) {
this.buildMenu(element);
}, this);
},
changeItemState: function(item, checkbox, monitor) {
if (item.hasClass(this.options.itemSelectedClass)) {
item.removeClass(this.options.itemSelectedClass);
checkbox.set('checked', false).focus();
}
else {
item.addClass(this.options.itemSelectedClass);
checkbox.set('checked', true).focus();
}
monitor.set('html', this.changeMonitorValue(item.getParent()) + '');
},
changeMonitorValue: function(list) {
var text = list.getElements(this.options.boxes).filter(function(box) { return box.get('checked'); }).length;
text = (text == 0) ? this.options.defaultTextEmpty : (this.options.defaultTextSelected + text);
return text;
},
itemHover: function(list, select) {
var current = list.getElement('li.'+this.options.itemHoverClass);
switch (select) {
case 'down':
if (current && (sibling = current.getNext())) current.removeClass(this.options.itemHoverClass);
else this.itemHover(list, 'last');
break;
case 'up':
if (current && (sibling = current.getPrevious())) current.removeClass(this.options.itemHoverClass);
else this.itemHover(list, 'first');
break;
case 'none':
list.getElements('li.'+this.options.itemHoverClass).removeClass(this.options.itemHoverClass);
break;
case 'first':
var sibling = list.getFirst();
break;
case 'last':
var sibling = list.getLast();
break;
default:
if (current) current.removeClass(this.options.itemHoverClass);
var sibling = select;
break;
}
if (sibling)
sibling.addClass(this.options.itemHoverClass).getElement(this.options.boxes).focus();
},
toggleMenu: function(toggle, monitor, list) {
if (toggle == 'open') {
// close all MultiSelect menus
this.elements.getElement('div.monitor').removeClass(this.options.monitorActiveClass);
this.elements.getElement('ul').setStyle('display', 'none');
monitor.addClass(this.options.monitorActiveClass);
list.setStyle('display', '');
this.itemHover(list, 'first');
this.state = 'opened';
} else {
// close all MultiSelect menus
this.elements.getElement('div.monitor').removeClass(this.options.monitorActiveClass);
this.elements.getElement('ul').setStyle('display', 'none');
this.action = 'open';
this.state = 'closed';
}
}
});