/* * easydropdown - a drop-down builder for styleable inputs and menus * version: 2.1.3 * license: creative commons attribution 3.0 unported - cc by 3.0 * http://creativecommons.org/licenses/by/3.0/ * this software may be used freely on commercial and non-commercial projects with attribution to the author/copyright holder. * author: patrick kunka * copyright 2013 patrick kunka, all rights reserved */ (function($){ function easydropdown(){ this.isfield = true, this.down = false, this.infocus = false, this.disabled = false, this.cutoff = false, this.haslabel = false, this.keyboardmode = false, this.nativetouch = true, this.wrapperclass = 'dropdown', this.onchange = null; }; easydropdown.prototype = { constructor: easydropdown, instances: [], init: function(domnode, settings){ var self = this; $.extend(self, settings); self.$select = $(domnode); self.id = domnode.id; self.options = []; self.$options = self.$select.find('option'); self.istouch = 'ontouchend' in document; self.$select.removeclass(self.wrapperclass+' dropdown'); if(self.$select.is(':disabled')){ self.disabled = true; }; if(self.$options.length){ self.$options.each(function(i){ var $option = $(this); if($option.is(':selected')){ self.selected = { index: i, title: $option.text() } self.focusindex = i; }; if($option.hasclass('label') && i == 0){ self.haslabel = true; self.label = $option.text(); $option.attr('value',''); } else { self.options.push({ domnode: $option[0], title: $option.text(), value: $option.val(), selected: $option.is(':selected') }); }; }); if(!self.selected){ self.selected = { index: 0, title: self.$options.eq(0).text() } self.focusindex = 0; }; self.render(); }; }, render: function(){ var self = this, touchclass = self.istouch && self.nativetouch ? ' touch' : '', disabledclass = self.disabled ? ' disabled' : ''; self.$container = self.$select.wrap('
').parent().parent(); self.$active = $(''+self.selected.title+'').appendto(self.$container); self.$carat = $('').appendto(self.$container); self.$scrollwrapper = $('
').appendto(self.$container); self.$dropdown = self.$scrollwrapper.find('ul'); self.$form = self.$container.closest('form'); $.each(self.options, function(){ var option = this, active = option.selected ? ' class="active"':''; self.$dropdown.append(''+option.title+''); }); self.$items = self.$dropdown.find('li'); self.maxheight = 0; if(self.cutoff && self.$items.length > self.cutoff)self.$container.addclass('scrollable'); for(i = 0; i < self.$items.length; i++){ var $item = self.$items.eq(i); self.maxheight += $item.outerheight(); if(self.cutoff == i+1){ break; }; }; if(self.istouch && self.nativetouch){ self.bindtouchhandlers(); } else { self.bindhandlers(); }; }, bindtouchhandlers: function(){ var self = this; self.$container.on('click.easydropdown',function(){ self.$select.focus(); }); self.$select.on({ change: function(){ var $selected = $(this).find('option:selected'), title = $selected.text(), value = $selected.val(); self.$active.text(title); if(typeof self.onchange === 'function'){ self.onchange.call(self.$select[0],{ title: title, value: value }); }; }, focus: function(){ self.$container.addclass('focus'); }, blur: function(){ self.$container.removeclass('focus'); } }); }, bindhandlers: function(){ var self = this; self.query = ''; self.$container.on({ 'click.easydropdown': function(){ if(!self.down && !self.disabled){ self.open(); } else { self.close(); }; }, 'mousemove.easydropdown': function(){ if(self.keyboardmode){ self.keyboardmode = false; }; } }); $('body').on('click.easydropdown.'+self.id,function(e){ var $target = $(e.target), classnames = self.wrapperclass.split(' ').join('.'); if(!$target.closest('.'+classnames).length && self.down){ self.close(); }; }); self.$items.on({ 'click.easydropdown': function(){ var index = $(this).index(); self.select(index); self.$select.focus(); }, 'mouseover.easydropdown': function(){ if(!self.keyboardmode){ var $t = $(this); $t.addclass('focus').siblings().removeclass('focus'); self.focusindex = $t.index(); }; }, 'mouseout.easydropdown': function(){ if(!self.keyboardmode){ $(this).removeclass('focus'); }; } }); self.$select.on({ 'focus.easydropdown': function(){ self.$container.addclass('focus'); self.infocus = true; }, 'blur.easydropdown': function(){ self.$container.removeclass('focus'); self.infocus = false; }, 'keydown.easydropdown': function(e){ if(self.infocus){ self.keyboardmode = true; var key = e.keycode; if(key == 38 || key == 40 || key == 32){ e.preventdefault(); if(key == 38){ self.focusindex-- self.focusindex = self.focusindex < 0 ? self.$items.length - 1 : self.focusindex; } else if(key == 40){ self.focusindex++ self.focusindex = self.focusindex > self.$items.length - 1 ? 0 : self.focusindex; }; if(!self.down){ self.open(); }; self.$items.removeclass('focus').eq(self.focusindex).addclass('focus'); if(self.cutoff){ self.scrolltoview(); }; self.query = ''; }; if(self.down){ if(key == 9 || key == 27){ self.close(); } else if(key == 13){ e.preventdefault(); self.select(self.focusindex); self.close(); return false; } else if(key == 8){ e.preventdefault(); self.query = self.query.slice(0,-1); self.search(); cleartimeout(self.resetquery); return false; } else if(key != 38 && key != 40){ var letter = string.fromcharcode(key); self.query += letter; self.search(); cleartimeout(self.resetquery); }; }; }; }, 'keyup.easydropdown': function(){ self.resetquery = settimeout(function(){ self.query = ''; },1200); } }); self.$dropdown.on('scroll.easydropdown',function(e){ if(self.$dropdown[0].scrolltop >= self.$dropdown[0].scrollheight - self.maxheight){ self.$container.addclass('bottom'); } else { self.$container.removeclass('bottom'); }; }); if(self.$form.length){ self.$form.on('reset.easydropdown', function(){ var active = self.haslabel ? self.label : self.options[0].title; self.$active.text(active); }); }; }, unbindhandlers: function(){ var self = this; self.$container .add(self.$select) .add(self.$items) .add(self.$form) .add(self.$dropdown) .off('.easydropdown'); $('body').off('.'+self.id); }, open: function(){ var self = this, scrolltop = window.scrolly || document.documentelement.scrolltop, scrollleft = window.scrollx || document.documentelement.scrollleft, scrolloffset = self.notinviewport(scrolltop); self.closeall(); self.$select.focus(); window.scrollto(scrollleft, scrolltop+scrolloffset); self.$container.addclass('open'); self.$scrollwrapper.css('height',self.maxheight+'px'); self.down = true; }, close: function(){ var self = this; self.$container.removeclass('open'); self.$scrollwrapper.css('height','0px'); self.focusindex = self.selected.index; self.query = ''; self.down = false; }, closeall: function(){ var self = this, instances = object.getprototypeof(self).instances; for(var key in instances){ var instance = instances[key]; instance.close(); }; }, select: function(index){ var self = this; if(typeof index === 'string'){ index = self.$select.find('option[value='+index+']').index() - 1; }; var option = self.options[index], selectindex = self.haslabel ? index + 1 : index; self.$items.removeclass('active').eq(index).addclass('active'); self.$active.text(option.title); self.$select .find('option') .removeattr('selected') .eq(selectindex) .prop('selected',true) .parent() .trigger('change'); self.selected = { index: index, title: option.title }; self.focusindex = i; if(typeof self.onchange === 'function'){ self.onchange.call(self.$select[0],{ title: option.title, value: option.value }); }; }, search: function(){ var self = this, lock = function(i){ self.focusindex = i; self.$items.removeclass('focus').eq(self.focusindex).addclass('focus'); self.scrolltoview(); }, gettitle = function(i){ return self.options[i].title.touppercase(); }; for(i = 0; i < self.options.length; i++){ var title = gettitle(i); if(title.indexof(self.query) == 0){ lock(i); return; }; }; for(i = 0; i < self.options.length; i++){ var title = gettitle(i); if(title.indexof(self.query) > -1){ lock(i); break; }; }; }, scrolltoview: function(){ var self = this; if(self.focusindex >= self.cutoff){ var $focusitem = self.$items.eq(self.focusindex), scroll = ($focusitem.outerheight() * (self.focusindex + 1)) - self.maxheight; self.$dropdown.scrolltop(scroll); }; }, notinviewport: function(scrolltop){ var self = this, range = { min: scrolltop, max: scrolltop + (window.innerheight || document.documentelement.clientheight) }, menubottom = self.$dropdown.offset().top + self.maxheight; if(menubottom >= range.min && menubottom <= range.max){ return 0; } else { return (menubottom - range.max) + 5; }; }, destroy: function(){ var self = this; self.unbindhandlers(); self.$select.unwrap().siblings().remove(); self.$select.unwrap(); delete object.getprototypeof(self).instances[self.$select[0].id]; }, disable: function(){ var self = this; self.disabled = true; self.$container.addclass('disabled'); self.$select.attr('disabled',true); if(!self.down)self.close(); }, enable: function(){ var self = this; self.disabled = false; self.$container.removeclass('disabled'); self.$select.attr('disabled',false); } }; var instantiate = function(domnode, settings){ domnode.id = !domnode.id ? 'easydropdown'+rand() : domnode.id; var instance = new easydropdown(); if(!instance.instances[domnode.id]){ instance.instances[domnode.id] = instance; instance.init(domnode, settings); }; }, rand = function(){ return ('00000'+(math.random()*16777216<<0).tostring(16)).substr(-6).touppercase(); }; $.fn.easydropdown = function(){ var args = arguments, datareturn = [], eachreturn; eachreturn = this.each(function(){ if(args && typeof args[0] === 'string'){ var data = easydropdown.prototype.instances[this.id][args[0]](args[1], args[2]); if(data)datareturn.push(data); } else { instantiate(this, args[0]); }; }); if(datareturn.length){ return datareturn.length > 1 ? datareturn : datareturn[0]; } else { return eachreturn; }; }; $(function(){ if(typeof object.getprototypeof !== 'function'){ if(typeof 'test'.__proto__ === 'object'){ object.getprototypeof = function(object){ return object.__proto__; }; } else { object.getprototypeof = function(object){ return object.constructor.prototype; }; }; }; $('select.dropdown').each(function(){ var json = $(this).attr('data-settings'); settings = json ? $.parsejson(json) : {}; instantiate(this, settings); }); }); })(jquery);