// 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(); } } } });