(function(SKY,$){ var defOptions = { type: null, nullable: true, lookup: function(pattern,response){} }; class SkySelect { constructor(element,options){ this.options = Object.assign({},defOptions); Object.assign(this.options, options); var self = this; element = $(element); element.data("skySelect", this); this.element = element; this.wrap = $("
"); this.element.appendTo(this.wrap); this.popup = $("
") .addClass("skyselect-popup") .appendTo(this.wrap); this.element .on("keydown", function(ev){ self.onKeyDown(ev); } ) .on("focusin", function(ev){ self.onFocusIn(ev); } ) .on("focusout", function(ev){ self.onFocusOut(ev); } ) .on("dblclick", function(ev){ self.open(); self.update(); } ); this.wrap.addClass("skyselect-wrapper"); this.type = SKY.type(this.options.type); this.selectedItem = null; this.activeItem = null; this.timer = null; } html(){ return this.wrap; } value(item){ if (arguments.length) { this.select(item); return this; } return this.selectedItem; } open(){ this.popup.slideDown(); this.isOpen = true; return this; } close(){ if (this.isOpen) { clearInterval(this.timer); this.popup.hide(); this.isOpen = false; this.activeItem = null; } return this; } restoreSelection(){ if (this.options.nullable && (this.element.val()=="")) { this.selectedItem = null; } else { this.element.val( this.selectedItem ? this.options.render(this.selectedItem) : "" ); } } markCurrentSelected(){ var self = this; var currentSelectedIdentity = self.options.identity(self.selectedItem); this.popup.children(".skyselect-item").each(function(){ var li = $(this); var item = li.data("skyselectItem"); if (self.options.identity(item) == currentSelectedIdentity) li.addClass("selected"); else li.removeClass("selected"); }); } markActive(item){ var self = this; var currentActiveIdentity = self.type.identity(item); self.activeItem = item; this.popup.children(".skyselect-item").each(function(){ var li = $(this); var item = li.data("skyselectItem"); if (self.type.identity(item) == currentActiveIdentity) li.addClass("active"); else li.removeClass("active"); }); } select(item){ this.close(); this.selectedItem = item; this.element.val(this.type.render(item)); this.markCurrentSelected(); } update(){ var self = this; self.options.lookup( self.element.val(), function(items){ self.items(items); } ); } items(items){ var self = this; this.popup.empty(); $.each( items, function(){ var item = this; var identity = self.type.identity(item); var li = $("
") .addClass("skyselect-item") .append(self.type.render(this)) .appendTo(self.popup) .data("skyselectItem", item) .on("click", function(ev){ self.select( item ); } ) .on("mouseover", function(ev){ self.markActive(item); }); if (self.currentActiveIdentity == identity) li.addClass("active"); }) this.markCurrentSelected(); } valid(){ return true; } onKeyDown(ev){ var self = this; switch (ev.which) { case 13: // RETURN if (this.isOpen){ } break; case 27: // ESC this.close(); break; case 38: // UP if (this.isOpen){ var currentActiveLi = $(".skyselect-item.active", this.popup); if (currentActiveLi.length){ var prevLi = currentActiveLi.prev(".skyselect-item"); if (prevLi.length) { this.markActive(prevLi.data("skyselectItem")); } } else { var firstLi = $(this.popup.children(".skyselect-item").get(0)); if (firstLi.length) this.markActive(firstLi.data("skyselectItem")); } } break; case 40: // DOWN if (this.isOpen){ var currentActiveLi = $(".skyselect-item.active", this.popup); if (currentActiveLi.length){ var nextLi = currentActiveLi.next(".skyselect-item"); if (nextLi.length) { this.markActive(nextLi.data("skyselectItem")); } } else { var firstLi = $(this.popup.children(".skyselect-item").get(0)); if (firstLi.length){ this.markActive(firstLi.data("skyselectItem")); } } } else { this.open(); } break; default: console.log("ev.which=" + ev.which); if ((ev.which >= 32)) { clearInterval(this.timer); this.timer = setInterval( function(){ self.update(); }, 250); this.open(); } break; } } onFocusIn(ev){ this.element.select(); } onFocusOut(ev){ var self = this; setTimeout( function(){ self.close(); }, 100 ); this.restoreSelection(); } } $.fn.skySelect = function(options){ /* $.each( this, function(index,item){ var skySelect = $(item).data("skySelect"); if (!skySelect) skySelect = new SkySelect(item,options); }); var me = $(this.get(0)); if (me.hasClass("skyselect-wrapper")) return me; return me.parents(".skyselect-wrapper").data("skySelect"); */ var me = $(this.get(0)); var skySelect = me.data("skySelect"); if (skySelect && options){ throw "skySelect() must not be called with options when already initiaized"; } if (!skySelect) skySelect = new SkySelect(me,options); return skySelect; } }( SKY, jQuery ));