// custom select module
jcf.addModule({
name:'select',
selector:'select',
defaultOptions: {
useNativeDropOnMobileDevices: true,
hideDropOnScroll: true,
showNativeDrop: false,
handleDropPosition: true,
selectDropPosition: 'bottom', // or 'top'
wrapperClass:'select-area',
focusClass:'select-focus',
dropActiveClass:'select-active',
selectedClass:'item-selected',
currentSelectedClass:'current-selected',
disabledClass:'select-disabled',
valueSelector:'span.center',
optGroupClass:'optgroup',
openerSelector:'a.select-opener',
selectStructure:'',
wrapperTag: 'span',
classPrefix:'select-',
dropMaxHeight: 255,
dropFlippedClass: 'select-options-flipped',
dropHiddenClass:'options-hidden',
dropScrollableClass:'options-overflow',
dropClass:'select-options',
dropClassPrefix:'drop-',
dropStructure:'
',
dropSelector:'div.drop-list'
},
checkElement: function(el){
return (!el.size && !el.multiple);
},
setupWrapper: function(){
jcf.lib.addClass(this.fakeElement, this.options.wrapperClass);
this.realElement.parentNode.insertBefore(this.fakeElement, this.realElement);
this.fakeElement.innerHTML = this.options.selectStructure;
this.fakeElement.style.width = (this.realElement.offsetWidth > 0 ? this.realElement.offsetWidth + 'px' : 'auto');
// show native drop if specified in options
if(this.options.useNativeDropOnMobileDevices && (jcf.isTouchDevice || jcf.isWinPhoneDevice)) {
this.options.showNativeDrop = true;
}
if(this.options.showNativeDrop) {
this.fakeElement.appendChild(this.realElement);
jcf.lib.removeClass(this.realElement, this.options.hiddenClass);
jcf.lib.setStyles(this.realElement, {
top:0,
left:0,
margin:0,
padding:0,
opacity:0,
border:'none',
position:'absolute',
width: jcf.lib.getInnerWidth(this.fakeElement) - 1,
height: jcf.lib.getInnerHeight(this.fakeElement) - 1
});
jcf.lib.event.add(this.realElement, jcf.eventPress, function(){
this.realElement.title = '';
}, this)
}
// create select body
this.opener = jcf.lib.queryBySelector(this.options.openerSelector, this.fakeElement)[0];
this.valueText = jcf.lib.queryBySelector(this.options.valueSelector, this.fakeElement)[0];
jcf.lib.disableTextSelection(this.valueText);
this.opener.jcf = this;
if(!this.options.showNativeDrop) {
this.createDropdown();
this.refreshState();
this.onControlReady(this);
this.hideDropdown(true);
} else {
this.refreshState();
}
this.addEvents();
},
addEvents: function(){
if(this.options.showNativeDrop) {
jcf.lib.event.add(this.realElement, 'click', this.onChange, this);
} else {
jcf.lib.event.add(this.fakeElement, 'click', this.toggleDropdown, this);
}
jcf.lib.event.add(this.realElement, 'change', this.onChange, this);
},
onFakeClick: function() {
// do nothing (drop toggles by toggleDropdown method)
},
onFocus: function(){
jcf.modules[this.name].superclass.onFocus.apply(this, arguments);
if(!this.options.showNativeDrop) {
// Mac Safari Fix
if(jcf.lib.browser.safariMac) {
this.realElement.setAttribute('size','2');
}
jcf.lib.event.add(this.realElement, 'keydown', this.onKeyDown, this);
if(jcf.activeControl && jcf.activeControl != this) {
jcf.activeControl.hideDropdown();
jcf.activeControl = this;
}
}
},
onBlur: function(){
if(!this.options.showNativeDrop) {
// Mac Safari Fix
if(jcf.lib.browser.safariMac) {
this.realElement.removeAttribute('size');
}
if(!this.isActiveDrop() || !this.isOverDrop()) {
jcf.modules[this.name].superclass.onBlur.apply(this);
if(jcf.activeControl === this) jcf.activeControl = null;
if(!jcf.isTouchDevice) {
this.hideDropdown();
}
}
jcf.lib.event.remove(this.realElement, 'keydown', this.onKeyDown);
} else {
jcf.modules[this.name].superclass.onBlur.apply(this);
}
},
onChange: function() {
this.refreshState();
},
onKeyDown: function(e){
this.dropOpened = true;
jcf.tmpFlag = true;
setTimeout(function(){jcf.tmpFlag = false},100);
var context = this;
context.keyboardFix = true;
setTimeout(function(){
context.refreshState();
},10);
if(e.keyCode == 13) {
context.toggleDropdown.apply(context);
return false;
}
},
onResizeWindow: function(e){
if(this.isActiveDrop()) {
this.hideDropdown();
}
},
onScrollWindow: function(e){
if(this.options.hideDropOnScroll) {
this.hideDropdown();
} else if(this.isActiveDrop()) {
this.positionDropdown();
}
},
onOptionClick: function(e){
var opener = e.target && e.target.tagName && e.target.tagName.toLowerCase() == 'li' ? e.target : jcf.lib.getParent(e.target, 'li');
if(opener) {
this.dropOpened = true;
this.realElement.selectedIndex = parseInt(opener.getAttribute('rel'));
if(jcf.isTouchDevice) {
this.onFocus();
} else {
this.realElement.focus();
}
this.refreshState();
this.hideDropdown();
jcf.lib.fireEvent(this.realElement, 'change');
}
return false;
},
onClickOutside: function(e){
if(jcf.tmpFlag) {
jcf.tmpFlag = false;
return;
}
if(!jcf.lib.isParent(this.fakeElement, e.target) && !jcf.lib.isParent(this.selectDrop, e.target)) {
this.hideDropdown();
}
},
onDropHover: function(e){
if(!this.keyboardFix) {
this.hoverFlag = true;
var opener = e.target && e.target.tagName && e.target.tagName.toLowerCase() == 'li' ? e.target : jcf.lib.getParent(e.target, 'li');
if(opener) {
this.realElement.selectedIndex = parseInt(opener.getAttribute('rel'));
this.refreshSelectedClass(parseInt(opener.getAttribute('rel')));
}
} else {
this.keyboardFix = false;
}
},
onDropLeave: function(){
this.hoverFlag = false;
},
isActiveDrop: function(){
return !jcf.lib.hasClass(this.selectDrop, this.options.dropHiddenClass);
},
isOverDrop: function(){
return this.hoverFlag;
},
createDropdown: function(){
// remove old dropdown if exists
if(this.selectDrop) {
$(this.selectDrop.children).remove()
// this.selectDrop.parentNode.removeChild(this.selectDrop);
}
// create dropdown holder
this.selectDrop = document.createElement('div');
this.selectDrop.className = this.options.dropClass;
this.selectDrop.innerHTML = this.options.dropStructure;
jcf.lib.setStyles(this.selectDrop, {position:'absolute'});
this.selectList = jcf.lib.queryBySelector(this.options.dropSelector,this.selectDrop)[0];
jcf.lib.addClass(this.selectDrop, this.options.dropHiddenClass);
document.body.appendChild(this.selectDrop);
this.selectDrop.jcf = this;
jcf.lib.event.add(this.selectDrop, 'click', this.onOptionClick, this);
jcf.lib.event.add(this.selectDrop, 'mouseover', this.onDropHover, this);
jcf.lib.event.add(this.selectDrop, 'mouseout', this.onDropLeave, this);
this.buildDropdown();
},
buildDropdown: function() {
// build select options / optgroups
this.buildDropdownOptions();
// position and resize dropdown
this.positionDropdown();
// cut dropdown if height exceedes
this.buildDropdownScroll();
},
buildDropdownOptions: function() {
this.resStructure = '';
this.optNum = 0;
for(var i = 0; i < this.realElement.children.length; i++) {
this.resStructure += this.buildElement(this.realElement.children[i], i) +'\n';
}
this.selectList.innerHTML = this.resStructure;
},
buildDropdownScroll: function() {
jcf.lib.addClass(this.selectDrop, jcf.lib.getAllClasses(this.realElement.className, this.options.dropClassPrefix, jcf.baseOptions.hiddenClass));
if(this.options.dropMaxHeight) {
if(this.selectDrop.offsetHeight > this.options.dropMaxHeight) {
this.selectList.style.height = this.options.dropMaxHeight+'px';
this.selectList.style.overflow = 'auto';
this.selectList.style.overflowX = 'hidden';
jcf.lib.addClass(this.selectDrop, this.options.dropScrollableClass);
}
}
},
parseOptionTitle: function(optTitle) {
return (typeof optTitle === 'string' && /\.(jpg|gif|png|bmp|jpeg)(.*)?$/i.test(optTitle)) ? optTitle : '';
},
buildElement: function(obj, index){
// build option
var res = '', optImage;
if(obj.tagName.toLowerCase() == 'option') {
if(!jcf.lib.prevSibling(obj) || jcf.lib.prevSibling(obj).tagName.toLowerCase() != 'option') {
res += '';
}
return res;
}
// build option group with options
else if(obj.tagName.toLowerCase() == 'optgroup' && obj.label) {
res += '';
res += ''+(obj.label)+'';
for(var i = 0; i < obj.children.length; i++) {
res += this.buildElement(obj.children[i], i);
}
res += '
';
return res;
}
},
positionDropdown: function(){
var ofs = jcf.lib.getOffset(this.fakeElement), selectAreaHeight = this.fakeElement.offsetHeight, selectDropHeight = this.selectDrop.offsetHeight;
var fitInTop = ofs.top - selectDropHeight >= jcf.lib.getScrollTop() && jcf.lib.getScrollTop() + jcf.lib.getWindowHeight() < ofs.top + selectAreaHeight + selectDropHeight;
if((this.options.handleDropPosition && fitInTop) || this.options.selectDropPosition === 'top') {
this.selectDrop.style.top = (ofs.top - selectDropHeight)+'px';
jcf.lib.addClass(this.fakeElement, this.options.dropFlippedClass);
jcf.lib.addClass(this.selectDrop, this.options.dropFlippedClass);
} else {
this.selectDrop.style.top = (ofs.top + selectAreaHeight)+'px';
jcf.lib.removeClass(this.fakeElement, this.options.dropFlippedClass);
jcf.lib.removeClass(this.selectDrop, this.options.dropFlippedClass);
}
this.selectDrop.style.left = ofs.left+'px';
this.selectDrop.style.width = this.fakeElement.offsetWidth+'px';
},
showDropdown: function(){
document.body.appendChild(this.selectDrop);
jcf.lib.removeClass(this.selectDrop, this.options.dropHiddenClass);
jcf.lib.addClass(this.fakeElement,this.options.dropActiveClass);
this.positionDropdown();
// highlight current active item
var activeItem = this.getFakeActiveOption();
this.removeClassFromItems(this.options.currentSelectedClass);
jcf.lib.addClass(activeItem, this.options.currentSelectedClass);
// show current dropdown
jcf.lib.event.add(window, 'resize', this.onResizeWindow, this);
jcf.lib.event.add(window, 'scroll', this.onScrollWindow, this);
jcf.lib.event.add(document, jcf.eventPress, this.onClickOutside, this);
this.positionDropdown();
},
hideDropdown: function(partial){
if(this.selectDrop.parentNode) {
if(this.selectDrop.offsetWidth) {
this.selectDrop.parentNode.removeChild(this.selectDrop);
}
if(partial) {
return;
}
}
if(typeof this.origSelectedIndex === 'number') {
this.realElement.selectedIndex = this.origSelectedIndex;
}
jcf.lib.removeClass(this.fakeElement,this.options.dropActiveClass);
jcf.lib.addClass(this.selectDrop, this.options.dropHiddenClass);
jcf.lib.event.remove(window, 'resize', this.onResizeWindow);
jcf.lib.event.remove(window, 'scroll', this.onScrollWindow);
jcf.lib.event.remove(document.documentElement, jcf.eventPress, this.onClickOutside);
if(jcf.isTouchDevice) {
this.onBlur();
}
},
toggleDropdown: function(){
if(!this.realElement.disabled) {
if(jcf.isTouchDevice) {
this.onFocus();
} else {
this.realElement.focus();
}
if(this.isActiveDrop()) {
this.hideDropdown();
} else {
this.showDropdown();
}
this.refreshState();
}
},
scrollToItem: function(){
if(this.isActiveDrop()) {
var dropHeight = this.selectList.offsetHeight;
var offsetTop = this.calcOptionOffset(this.getFakeActiveOption());
var sTop = this.selectList.scrollTop;
var oHeight = this.getFakeActiveOption().offsetHeight;
//offsetTop+=sTop;
if(offsetTop >= sTop + dropHeight) {
this.selectList.scrollTop = offsetTop - dropHeight + oHeight;
} else if(offsetTop < sTop) {
this.selectList.scrollTop = offsetTop;
}
}
},
getFakeActiveOption: function(c) {
return jcf.lib.queryBySelector('li[rel="'+(typeof c === 'number' ? c : this.realElement.selectedIndex) +'"]',this.selectList)[0];
},
calcOptionOffset: function(fake) {
var h = 0;
var els = jcf.lib.queryBySelector('.jcfcalc',this.selectList);
for(var i = 0; i < els.length; i++) {
if(els[i] == fake) break;
h+=els[i].offsetHeight;
}
return h;
},
childrenHasItem: function(hold,item) {
var items = hold.getElementsByTagName('*');
for(i = 0; i < items.length; i++) {
if(items[i] == item) return true;
}
return false;
},
removeClassFromItems: function(className){
var children = jcf.lib.queryBySelector('li',this.selectList);
for(var i = children.length - 1; i >= 0; i--) {
jcf.lib.removeClass(children[i], className);
}
},
setSelectedClass: function(c){
jcf.lib.addClass(this.getFakeActiveOption(c), this.options.selectedClass);
},
refreshSelectedClass: function(c){
if(!this.options.showNativeDrop) {
this.removeClassFromItems(this.options.selectedClass);
this.setSelectedClass(c);
}
if(this.realElement.disabled) {
jcf.lib.addClass(this.fakeElement, this.options.disabledClass);
if(this.labelFor) {
jcf.lib.addClass(this.labelFor, this.options.labelDisabledClass);
}
} else {
jcf.lib.removeClass(this.fakeElement, this.options.disabledClass);
if(this.labelFor) {
jcf.lib.removeClass(this.labelFor, this.options.labelDisabledClass);
}
}
},
refreshSelectedText: function() {
if(!this.dropOpened && this.realElement.title) {
this.valueText.innerHTML = this.realElement.title;
} else {
if(this.realElement.options[this.realElement.selectedIndex].title) {
var optImage = this.parseOptionTitle(this.realElement.options[this.realElement.selectedIndex].title);
this.valueText.innerHTML = (optImage ? '' : '') + this.realElement.options[this.realElement.selectedIndex].innerHTML;
} else {
this.valueText.innerHTML = this.realElement.options[this.realElement.selectedIndex].innerHTML;
}
}
},
refreshState: function(){
this.origSelectedIndex = this.realElement.selectedIndex;
this.refreshSelectedClass();
this.refreshSelectedText();
if(!this.options.showNativeDrop) {
this.positionDropdown();
if(this.selectDrop.offsetWidth) {
this.scrollToItem();
}
}
}
});