/*
* 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);