/** * SelectoMoo - form select fields replacement with style * @version 1.0.0 * @MooTools version 1.2 * @author Constantin Boiangiu * @copyright Constantin Boiangiu * MIT-style license. */ var SelectoMoo = new Class({ Implements: [Options,Events], options: { element: null, css: 'selectoMoo', listHeight:120 }, initialize: function(options){ this.setOptions(options); this.visible = 0; /* the select box */ this.selBox = $(this.options.element); this.selBox.setStyle('display','none'); // this.selBoxContainer = new Element('div', { // 'styles':{ // 'display':'none' // } // }) this.opt = this.selBox.getElements('option'); this.currentOption = 0; /* selected option container */ this.selectedOption = new Element('div', { 'class':this.options.css+'_selectedOption' }); /* create a wrapper around the select */ this.container = new Element('div',{ 'class':this.options.css+'_container', 'events':{ 'click': function(event){ event.stopPropagation(); this.selBox.focus(); this.showList(); }.bind(this) }, 'styles':{ 'overflow':'hidden' } // }).inject(this.selBox,'after').adopt( this.selBoxContainer.adopt(this.selBox) , this.selectedOption ); }).inject(this.selBox,'after').adopt(this.selectedOption); /* options container */ this.outerOptContainer = new Element('div',{ 'class':this.options.css+'_options', 'styles':{ 'opacity':0, 'height':this.options.listHeight, 'top':0 }, 'events':{ click: function(event){ event.preventDefault(); }.bind(this), 'mousewheel': function(event){ /* event.wheel holds direction. Up returns 1, down returns -1 */ event.stop(); var elem = this.currentOption-event.wheel; if( event.wheel > 0 && elem < 0 ) elem = 0; if( event.wheel < 0 && elem >= this.totalOptions-1 ) elem = this.totalOptions-1; this.scrollFx.toElement(this.fakeOptions[elem]); this.fakeOptions[this.currentOption].removeClass(this.options.css+'_naved'); this.fakeOptions[elem].addClass(this.options.css+'_naved'); this.currentOption = elem; }.bind(this) } }).inject(this.container,'inside'); this.optContainer = new Element('div',{ 'class':this.options.css+'_options_inner' }).inject(this.outerOptContainer,'inside'); //this.outerOptContainer.set('morph',{'duration':300, 'transition':'back:out', 'wait':false}); this.scrollFx = new Fx.Scroll(this.outerOptContainer, { wait: false, duration: 300, wheelStops: false, transition: Fx.Transitions.Sine.easeOut }); /* IE hack to work for labels */ /* $(document).getElement('label[for='+this.options.element+']').addEvent('click', function(event){ event.stopPropagation(); event.stop(); if( this.visible ) this.hideList(); else { this.selBox.focus(); this.showList(); } }.bind(this)) */ this.addOptions(); this.totalOptions = this.fakeOptions.length; /* close list when clicked outside the options list */ $(document).addEvent('click', function(event){ if( this.visible ) this.hideList(); }.bind(this)); /* open the list when tab used for navigation */ this.selBox.addEvents({ 'focus': function(event){ if( this.visible ) return; this.showList(); }.bind(this), 'blur': function(event){ /* delay list hiding in case an option from list was clicked */ if( !this.visible ) return; this.hideList.delay(100, this); }.bind(this), 'keydown':function(evt){ if( evt.key == 'enter' ){ this.fakeOptions[this.currentOption].fireEvent('click'); return; } if( evt.key!=='up' && evt.key!=='down' ) return; var step = evt.key == 'up' ? -1 : 1; var elem = this.currentOption+step; if( evt.key == 'up' && elem < 0 ) elem = 0; if( evt.key == 'down' && elem >= this.totalOptions-1 ) elem = this.totalOptions-1; this.scrollFx.toElement(this.fakeOptions[elem]); this.fakeOptions[this.currentOption].removeClass(this.options.css+'_naved'); this.fakeOptions[elem].addClass(this.options.css+'_naved'); this.currentOption = elem; }.bind(this), 'mousewheel': function(event){ event.stop(); var elem = this.currentOption-event.wheel; if( event.wheel > 0 && elem < 0 ) elem = 0; if( event.wheel < 0 && elem >= this.totalOptions-1 ) elem = this.totalOptions-1; this.scrollFx.toElement(this.fakeOptions[elem]); this.fakeOptions[this.currentOption].removeClass(this.options.css+'_naved'); this.fakeOptions[elem].addClass(this.options.css+'_naved'); this.currentOption = elem; }.bind(this) }); }, addOptions: function(){ this.fakeOptions = new Array(); this.opt.each(function(el, i){ /* add selected option text */ if( el.selected == true ){ this.selectedOption.set({'text':el.get('text')}); } if( el.get('value') !== '' ){ /* set options in options container */ var option = new Element('div', { 'class':this.options.css+'_option'+( el.selected == true ? ' '+this.options.css+'_selected':'' ), 'text':el.get('text'), 'events':{ 'click': function(event){ if(this.visible==1){ if( event ) event.stopPropagation(); this.setOption(i, el.get('text')); } }.bind(this), 'mouseenter': function(){ option.addClass(this.options.css+'_hovered'); }.bind(this), 'mouseleave': function(){ option.removeClass(this.options.css+'_hovered'); }.bind(this) } }).inject(this.optContainer,'inside'); this.fakeOptions.include(option); if( el.selected == true ) this.currentOption = this.fakeOptions.indexOf(option); this.opt[i]['fakeOption'] = option; } }.bind(this)) }, showList: function(){ if( !Browser.ie ) this.selBox.focus(); if(this.visible == 1) return; this.visible = 1; this.container.setStyle('overflow', 'visible'); this.outerOptContainer.morph({'height':[0,this.options.listHeight], 'opacity':[.5,1], 'top':-this.options.listHeight/2}); this.scrollFx.toElement(this.fakeOptions[this.currentOption]); }, hideList: function(){ /* Note: when using back, bounce or elastic as transition and play with height, in IE you can't morph to 0 because size goes out of bounds and errors are issued */ if(this.visible == 0) return; this.visible = 0; this.outerOptContainer.morph({'height':30, 'opacity':0, 'top':0}); this.container.setStyle('overflow', 'hidden'); //if( !Browser.Engine.trident ) this.selBox.blur(); }, setOption: function(key, optValue){ var val=null; this.opt.each(function(el, i){ if( el.selected == true ) el.selected = false; if( i == key ){ el.set({'selected':true}); val=el.get('value'); } }.bind(this)); $$('.'+this.options.css+'_selected').removeClass(this.options.css+'_selected'); this.opt[key]['fakeOption'].addClass(this.options.css+'_selected'); this.selectedOption.set({'text':optValue}); this.hideList(); this.fireEvent('change',val); }, destroy:function(){ this.container.destroy(); this.selBox.setStyle('display','block').removeEvents(); } })