| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550 |
- /**
- * Copyright (c) 2009 Anders Ekdahl (http://coffeescripter.com/)
- * var select = $('.editable-select:first');
- * var instances = select.editableSelectInstances();
- * instances[0].addOption('Germany', 'value added programmatically');
- *
- * Version: 1.3.2
- * yingxian modify
- * Demo and documentation: http://coffeescripter.com/code/editable-select/
- */
- (function($) {
- var instances = [];
- $.fn.editableSelect = function(options) {
- var defaults = { bg_iframe: false,
- onSelect: false,
- items_then_scroll: 10,
- case_sensitive: false
- };
- var settings = $.extend(defaults, options);
- // Only do bg_iframe for browsers that need it
- /*if(settings.bg_iframe && !$.browser.msie) {
- settings.bg_iframe = false;
- };*/
- var instance = false;
- $(this).each(function() {
- var i = instances.length;
- if($(this).data('editable-selecter') !== null) {
- instances[i] = new EditableSelect(this, settings);
- $(this).data('editable-selecter', i);
- };
- });
- return $(this);
- };
- $.fn.editableSelectInstances = function() {
- var ret = [];
- $(this).each(function() {
- if($(this).data('editable-selecter') !== null) {
- ret[ret.length] = instances[$(this).data('editable-selecter')];
- };
- });
- return ret;
- };
- var EditableSelect = function(select, settings) {
- this.init(select, settings);
- };
- EditableSelect.prototype = {
- settings: false,
- text: false,
- select: false,
- select_width: 0,
- wrapper: false,
- list_item_height: 20,
- list_height: 0,
- list_is_visible: false,
- hide_on_blur_timeout: false,
- bg_iframe: false,
- current_value: '',
- init: function(select, settings) {
- this.settings = settings;
- this.wrapper = $(document.createElement('div'));
- this.wrapper.addClass('editable-select-options');
- this.select = $(select);
- var name = this.select.attr('name');
- if(!name) {
- name = 'editable-select'+ instances.length;
- };
- var id = this.select.attr('id');
- if(!id) {
- id = 'editable-select'+ instances.length;
- };
- this.text = $('<input type="text">');
- this.text_submit = $('<input type="hidden">');
- this.text.attr('name', name + "_sele");
- this.text_submit.attr('name', name);
- this.text.data('editable-selecter', this.select.data('editable-selecter'));
- this.text_submit.data('editable-selecter', this.select.data('editable-selecter'));
- // Because we don't want the value of the select when the form
- // is submitted
- this.select.attr('disabled', 'disabled');
- this.text[0].className = this.select[0].className;
- this.text_submit[0].className = this.select[0].className;
-
- this.text.attr('id', id + "_sele");
- this.text_submit.attr('id', id);
- this.wrapper.attr('id',id+'_editable-select-options');
- this.text.attr('autocomplete', 'off');
- this.text.attr('autocomplete', 'off');
- this.text.addClass('editable-select');
- this.text_submit.addClass('editable-select');
- this.select.attr('id', id +'_hidden_select');
- this.select.attr('name', name +'_hidden_select');
- this.select.after(this.text);
- this.select.after(this.text_submit);
- if(this.select.css('display') == 'none') {
- //this.text.css('display', 'none');
- this.text_submit.css('display', 'none');
- }
- if(this.select.css('visibility') == 'hidden') {
- //this.text.css('visibility', 'visibility');
- this.text_submit.css('visibility', 'visibility');
- }
- // Set to hidden, because we want to call .show()
- // on it to get it's width but not having it display
- // on the screen
- this.select.css('visibility', 'hidden');
- this.select.hide();
- this.initInputEvents(this.text);
- this.duplicateOptions();
- this.setWidths();
- $(document.body).append(this.wrapper);
-
- if(this.settings.bg_iframe) {
- this.createBackgroundIframe();
- };
- if(typeof this.settings.success == "function") {
- this.settings.success.call(this,this.text_submit[0]);
- };
- },
- /**
- * Take the select lists options and
- * populate an unordered list with them
- */
- duplicateOptions: function() {
- var context = this,text,val;
- var option_list = $(document.createElement('ul'));
- this.wrapper.empty();
- this.wrapper.append(option_list);
- var options = this.select.find('option');
- this.dataList = [];
- options.each(function(i) {
- text = $(this).text();
- val = $(this).val();
- if($(this).attr('selected') /*|| i == 0*/) {
- context.text.val(text);
- context.text_submit.val(val);
- context.current_value = text;
- };
- if(context.trim(text) != "") context.dataList.push(text);
- var li = $('<li value="'+ val +'">'+ text +'</li>');
- li.hide();
- context.initListItemEvents(li);
- option_list.append(li);
- });
- this.setWidths();
- this.checkScroll();
- },in_array:function(e,arr)
- {
- for(i=0,len = arr.length;i < len;i++)
- {
- if(arr[i] == e)
- {
- return true;
- }
- }
- return false;
- },trim:function(str){
- return typeof str == "string" ? str.replace(/^\s*|\s*$/g,"") : str;
- },
- /**
- * Check if the list has enough items to display a scroll
- */
- checkScroll: function() {
- var options = this.wrapper.find('li');
- if(options.length > this.settings.items_then_scroll) {
- this.list_height = this.list_item_height * this.settings.items_then_scroll;
- this.wrapper.css('height', this.list_height +'px');
- this.wrapper.css('overflow', 'auto');
- } else {
- this.wrapper.css('height', 'auto');
- this.wrapper.css('overflow', 'visible');
- };
- },
- addOption: function(value,text) {
- var li = $('<li value="' + value + '">'+ text +'</li>');
- var option = $('<option value="' + value + '">'+ text +'</option>');
- this.select.append(option);
- this.initListItemEvents(li);
- this.wrapper.find('ul').append(li);
- this.setWidths();
- this.checkScroll();
- },
- /**
- * Init the different events on the input element
- */
- initInputEvents: function(text) {
- var context = this;
- var timer = false;
- $(document.body).click(
- function() {
- context.clearSelectedListItem();
- context.hideList();
- }
- );
- text.blur(
- function(e) {
- var val = context.trim(this.value);
- var isInArr = context.in_array(val,context.dataList);
- if( val == "")
- {
- context.text_submit.val("");
- }else if(val != "" && !isInArr)
- {
- context.text_submit.val("-1");
- }
- var list_item = typeof context.settings.onSelect == 'function' && isInArr ? context.findItem(val) : null;
-
- if(typeof context.settings.onSelect == 'function' && list_item != null) {
-
- context.text.val(list_item.text());
- context.text_submit.val(list_item.attr("value"));
- context.current_value = context.text.val();
- context.settings.onSelect.call(context, list_item,context.text_submit[0]);
- };
-
- context.hideList();
-
- e.preventDefault();
- e.stopPropagation();
- }
- );
- text.focus(
- function(e) {
- // Can't use the blur event to hide the list, because the blur event
- // is fired in some browsers when you scroll the list
- context.showList();
- context.highlightSelected();
- e.stopPropagation();
- }
- ).click(
- function(e) {
- e.stopPropagation();
- context.showList();
- context.highlightSelected();
- }
- ).keydown(
- // Capture key events so the user can navigate through the list
- function(e) {
- switch(e.keyCode) {
- // Down
- case 40:
- if(!context.listIsVisible()) {
- context.showList();
- context.highlightSelected();
- } else {
- e.preventDefault();
- context.selectNewListItem('down');
- };
- break;
- // Up
- case 38:
- e.preventDefault();
- context.selectNewListItem('up');
- break;
- // Tab
- case 9:
- context.pickListItem(context.selectedListItem());
- break;
- // Esc
- case 27:
- e.preventDefault();
- context.hideList();
- return false;
- break;
- // Enter, prevent form submission
- case 13:
- e.preventDefault();
- context.pickListItem(context.selectedListItem());
- return false;
- };
- }
- ).keyup(
- function(e) {
- // Prevent lots of calls if it's a fast typer
- if(timer !== false) {
- clearTimeout(timer);
- timer = false;
- };
- timer = setTimeout(
- function() {
- // If the user types in a value, select it if it's in the list
- if(context.text.val() != context.current_value) {
- context.current_value = context.text.val();
- context.highlightSelected();
- //context.showList();
- };
- },
- 200
- );
-
- // if input text change,list show.yingxian add hack by 2013-09-08
- (e.keyCode == 13) ? context.hideList() : context.showList();
- e.stopPropagation();
- }
- ).keypress(
- function(e) {
- if(e.keyCode == 13) {
- // Enter, prevent form submission
- e.preventDefault();
- return false;
- };
- }
- );
- },
- initListItemEvents: function(list_item) {
- var context = this;
- list_item.mouseover(
- function() {
- context.clearSelectedListItem();
- context.selectListItem(list_item);
- }
- ).mousedown(
- // Needs to be mousedown and not click, since the inputs blur events
- // fires before the list items click event
- function(e) {
- e.stopPropagation();
- context.pickListItem(context.selectedListItem());
- }
- );
- },
- selectNewListItem: function(direction) {
- var li = this.selectedListItem();
- if(!li.length) {
- li = this.selectFirstListItem();
- };
- if(direction == 'down') {
- var sib = this.selectNextItem(li);
- } else {
- var sib = this.selectPrevItem(li);
- };
- if(sib.length) {
- this.selectListItem(sib);
- this.scrollToListItem(sib);
- this.unselectListItem(li);
- };
- },selectNextItem:function(el){
- var e = el.next();
- if(e && e[0].display == "none")
- {
- return el;
- }
- return e;
- },selectPrevItem:function(el){
-
- var e = el.prev();
- if(e && e[0].display == "none")
- {
- return el;
- }
- return e;
-
- },
- selectListItem: function(list_item) {
- this.clearSelectedListItem();
- list_item.addClass('selected');
- },
- selectFirstListItem: function() {
- this.clearSelectedListItem();
- var first = this.wrapper.find('li:first');
- //this.wrapper.find('li').hide();
- first.addClass('selected');
- //first.show();
- return first;
- },
- unselectListItem: function(list_item) {
- list_item.removeClass('selected');
- },
- selectedListItem: function() {
- return this.wrapper.find('li.selected');
- },
- clearSelectedListItem: function() {
- this.wrapper.find('li.selected').removeClass('selected');
- },
- /**
- * The difference between this method and selectListItem
- * is that this method also changes the text field and
- * then hides the list
- */
- pickListItem: function(list_item) {
- if(list_item.length) {
- this.text.val(list_item.text());
- this.text_submit.val(list_item.attr("value"));
- this.current_value = this.text.val();
- };
- if(typeof this.settings.onSelect == 'function') {
- this.settings.onSelect.call(this, list_item,this.text_submit[0]);
- };
- this.hideList();
- },
- listIsVisible: function() {
- return this.list_is_visible;
- },
- showList: function() {
- this.positionElements();
- this.setWidths();
- this.wrapper.show();
- this.hideOtherLists();
- this.list_is_visible = true;
- if(this.settings.bg_iframe) {
- this.bg_iframe.show();
- };
- },
- findItem: function(text1) {
- var context = this;
- var current_value = context.trim(text1);
- var list_items = context.wrapper.find('li');
- var best_candiate = false;
- var value_found = false;
- list_items.each(
- function() {
- var text = context.trim($(this).text());
- if(!value_found) {
- if(!context.settings.case_sensitive) {
- text = text.toLowerCase();
- };
- if(text == current_value) {
- value_found = true;
- best_candiate = $(this);
- return false;
- }
- };
-
- }
-
- );
-
- if(value_found) {
- return best_candiate;
- }else if(!best_candiate && !value_found) {
- return null;
- };
-
- },
- highlightSelected: function() {
- var context = this;
- var current_value = context.trim(this.text.val());
- if(current_value.length < 0) {
- if(highlight_first) {
- this.selectFirstListItem();
- };
- return;
- };
-
- var list_items = this.wrapper.find('li');
- if(current_value.length == 0) {
- list_items.show();
- this.selectFirstListItem();
- return;
- };
-
- if(!context.settings.case_sensitive) {
- current_value = current_value.toLowerCase();
- };
- var best_candiate = false;
- var value_found = false;
- list_items.each(
- function() {
- var text = $(this).text();
- if(!value_found) {
- if(!context.settings.case_sensitive) {
- text = text.toLowerCase();
- };
- if(text == current_value) {
- value_found = true;
- context.clearSelectedListItem();
- context.selectListItem($(this));
- context.scrollToListItem($(this));
- //return false;
- } else if(text.search(current_value) > -1 && !best_candiate) {
- // Can't do return false here, since we still need to iterate over
- // all list items to see if there is an exact match
- best_candiate = $(this);
- };
-
- };
- if(context.settings.isFilter && text.search(current_value) > -1 && current_value != "")
- {
- $(this).show();
-
- }else if(context.settings.isFilter)
- {
- $(this).hide();
- }
- }
-
- );
-
- if(best_candiate && !value_found) {
- context.clearSelectedListItem();
- context.selectListItem(best_candiate);
- context.scrollToListItem(best_candiate);
- }else if(!best_candiate && !value_found) {
- this.selectFirstListItem();
- };
- },
- scrollToListItem: function(list_item) {
- if(this.list_height) {
- this.wrapper.scrollTop(list_item[0].offsetTop - (this.list_height / 2));
- };
- },
- hideList: function() {
- this.wrapper.hide();
- this.list_is_visible = false;
- if(this.settings.bg_iframe) {
- this.bg_iframe.hide();
- };
- },
- hideOtherLists: function() {
- for(var i = 0; i < instances.length; i++) {
- if(i != this.select.data('editable-selecter')) {
- instances[i].hideList();
- };
- };
- },
- positionElements: function() {
- var offset = this.text.offset();
- offset = { top: offset.top, left: offset.left };
- offset.top += this.text[0].offsetHeight;
- this.wrapper.css({top: offset.top +'px', left: offset.left +'px'});
- // Need to do this in order to get the list item height
- this.wrapper.css('visibility', 'hidden');
- this.wrapper.show();
- this.list_item_height = this.wrapper.find('li')[0] ? this.wrapper.find('li')[0].offsetHeight : 0;
- this.wrapper.css('visibility', 'visible');
- this.wrapper.hide();
- },
- setWidths: function() {
- // The text input has a right margin because of the background arrow image
- // so we need to remove that from the width
- this.select.show();
- var width = this.select.width() + 2 + 20;
- this.select.hide();
- var padding_right = parseInt(this.text.css('padding-right').replace(/px/, ''), 10);
- this.text.width(width - padding_right + 18);
- this.wrapper.width(width + 2 + 20);
- if(this.bg_iframe) {
- this.bg_iframe.width(width + 4 + 20);
- };
- },
- createBackgroundIframe: function() {
- var bg_iframe = $('<iframe frameborder="0" class="editable-select-iframe" src="about:blank;"></iframe>');
- $(document.body).append(bg_iframe);
- bg_iframe.width(this.select.width() + 2);
- bg_iframe.height(this.wrapper.height());
- bg_iframe.css({top: this.wrapper.css('top'), left: this.wrapper.css('left')});
- this.bg_iframe = bg_iframe;
- }
- };
- })(jQuery);
|