Skip to content

Instantly share code, notes, and snippets.

@carcus88
Created April 27, 2011 18:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save carcus88/944923 to your computer and use it in GitHub Desktop.
Save carcus88/944923 to your computer and use it in GitHub Desktop.
TextboxList Enhancements (clear function, drop down list to show all items)
/*
Script: TextboxList.Autocomplete.js
TextboxList Autocomplete plugin
Authors:
Guillermo Rauch
Note:
TextboxList is not priceless for commercial use. See <http://devthought.com/projects/jquery/textboxlist/>
Purchase to remove this message.
*/
(function($){
$.TextboxList.Autocomplete = function(textboxlist, _options){
var index, prefix, method, container, list, values = [], searchValues = [], results = [], placeholder = false, current, currentInput, hidetimer, doAdd, currentSearch, currentRequest;
var options = $.extend(true, {
minLength: 1,
maxResults: 10,
insensitive: true,
highlight: true,
highlightSelector: null,
mouseInteraction: true,
onlyFromValues: false,
queryRemote: false,
dropDownImage: false,
remote: {
url: '',
param: 'search',
extraParams: {},
loadPlaceholder: 'Please wait...'
},
method: 'standard',
placeholder: 'Type to receive suggestions'
}, _options);
var init = function(){
textboxlist.addEvent('bitEditableAdd', setupBit)
.addEvent('bitEditableFocus', search)
.addEvent('bitEditableBlur', hide)
.setOptions({bitsOptions: {editable: {addKeys: false, stopEnter: false}}});
if ($.browser.msie) textboxlist.setOptions({bitsOptions: {editable: {addOnBlur: false}}});
prefix = textboxlist.getOptions().prefix + '-autocomplete';
method = $.TextboxList.Autocomplete.Methods[options.method];
container = $('<div class="'+ prefix +'" />').width(textboxlist.getContainer().width()).appendTo(textboxlist.getContainer());
if (chk(options.placeholder)) placeholder = $('<div class="'+ prefix +'-placeholder" />').html(options.placeholder).appendTo(container);
list = $('<ul class="'+ prefix +'-results" />').appendTo(container).click(function(ev){
ev.stopPropagation(); ev.preventDefault();
});
if (chk(options.dropDownImage)) {
image = $('<img src="'+options.dropDownImage+'" style="float:right; cursor:pointer; top:-28px; left:19px; position:relative;"/>')
.appendTo(textboxlist.getContainer())
.click(function(e){
showResults();
});
}
};
var setupBit = function(bit){
bit.toElement().keydown(navigate).keyup(function(){ search(); });
};
var search = function(bit){
if (bit) currentInput = bit;
if (!options.queryRemote && !values.length) return;
var search = $.trim(currentInput.getValue()[1]);
if (search.length < options.minLength) showPlaceholder();
if (search == currentSearch) return;
currentSearch = search;
list.css('display', 'none');
if (search.length < options.minLength) return;
if (options.queryRemote){
if (searchValues[search]){
values = searchValues[search];
} else {
var data = options.remote.extraParams;
data[options.remote.param] = search;
if (currentRequest) currentRequest.abort();
currentRequest = $.ajax({
url: options.remote.url,
data: data,
dataType: 'json',
success: function(r){
searchValues[search] = r;
values = r;
showResults(search);
}
});
}
}
showResults(search);
};
var showResults = function(search){
var results;
if (search) {
results = method.filter(values, search, options.insensitive, options.maxResults);
} else {
results = values;
}
if (textboxlist.getOptions().unique){
results = $.grep(results, function(v){ return textboxlist.isDuplicate(v) == -1; });
}
hidePlaceholder();
if (!results.length) return;
blur();
list.empty().css('display', 'block');
$.each(results, function(i, r){ addResult(r, search); });
if (options.onlyFromValues) focusFirst();
results = results;
};
var addResult = function(r, searched){
var element = $('<li class="'+ prefix +'-result" />').html(r[3] ? r[3] : r[1]).data('textboxlist:auto:value', r);
element.appendTo(list);
if (options.highlight) $(options.highlightSelector ? element.find(options.highlightSelector) : element).each(function(){
if ($(this).html() && searched) method.highlight($(this), searched, options.insensitive, prefix + '-highlight');
});
if (options.mouseInteraction){
element.css('cursor', 'pointer').hover(function(){ focus(element); }).mousedown(function(ev){
ev.stopPropagation();
ev.preventDefault();
clearTimeout(hidetimer);
doAdd = true;
}).mouseup(function(){
if (doAdd){
addCurrent();
currentInput.focus();
search();
doAdd = false;
}
});
if (!options.onlyFromValues) element.mouseleave(function(){ if (current && (current.get(0) == element.get(0))) blur(); });
}
};
var hide = function(){
hidetimer = setTimeout(function(){
hidePlaceholder();
list.css('display', 'none');
currentSearch = null;
}, $.browser.msie ? 150 : 0);
};
var showPlaceholder = function(){
if (placeholder) placeholder.css('display', 'block');
};
var hidePlaceholder = function(){
if (placeholder) placeholder.css('display', 'none');
};
var focus = function(element){
if (!element || !element.length) return;
blur();
current = element.addClass(prefix + '-result-focus');
};
var blur = function(){
if (current && current.length){
current.removeClass(prefix + '-result-focus');
current = null;
}
};
var focusFirst = function(){
return focus(list.find(':first'));
};
var focusRelative = function(dir){
if (!current || !current.length) return self;
return focus(current[dir]());
};
var addCurrent = function(){
var value = current.data('textboxlist:auto:value');
var b = textboxlist.create('box', value.slice(0, 3));
if (b){
b.autoValue = value;
if ($.isArray(index)) index.push(value);
currentInput.setValue([null, '', null]);
b.inject(currentInput.toElement(), 'before');
}
blur();
return self;
};
var navigate = function(ev){
var evStop = function(){ ev.stopPropagation(); ev.preventDefault(); };
switch (ev.which){
case 38:
evStop();
(!options.onlyFromValues && current && current.get(0) === list.find(':first').get(0)) ? blur() : focusRelative('prev');
break;
case 40:
evStop();
(current && current.length) ? focusRelative('next') : focusFirst();
break;
case 13:
evStop();
if (current && current.length) addCurrent();
else if (!options.onlyFromValues){
var value = currentInput.getValue();
var b = textboxlist.create('box', value);
if (b){
b.inject(currentInput.toElement(), 'before');
currentInput.setValue([null, '', null]);
}
}
}
};
this.setValues = function(v){
values = v;
};
this.showAll = function() {
showResults();
};
init();
};
$.TextboxList.Autocomplete.Methods = {
standard: {
filter: function(values, search, insensitive, max){
var newvals = [], regexp = new RegExp('\\b' + escapeRegExp(search), insensitive ? 'i' : '');
for (var i = 0; i < values.length; i++){
if (newvals.length === max) break;
if (regexp.test(values[i][1])) newvals.push(values[i]);
}
return newvals;
},
highlight: function(element, search, insensitive, klass){
var regex = new RegExp('(<[^>]*>)|(\\b'+ escapeRegExp(search) +')', insensitive ? 'ig' : 'g');
return element.html(element.html().replace(regex, function(a, b, c){
return (a.charAt(0) == '<') ? a : '<strong class="'+ klass +'">' + c + '</strong>';
}));
}
}
};
var chk = function(v){ return !!(v || v === 0); };
var escapeRegExp = function(str){ return str.replace(/([-.*+?^${}()|[\]\/\\])/g, "\\$1"); };
})(jQuery);
/*
Script: TextboxList.js
Displays a textbox as a combination of boxes an inputs (eg: facebook tokenizer)
Authors:
Guillermo Rauch
Note:
TextboxList is not priceless for commercial use. See <http://devthought.com/projects/jquery/textboxlist/>.
Purchase to remove this message.
*/
(function($){
$.TextboxList = function(element, _options){
var original, container, list, current, focused = false, index = [], blurtimer, events = {};
var options = $.extend(true, {
prefix: 'textboxlist',
id : null,
max: null,
unique: false,
uniqueInsensitive: true,
endEditableBit: true,
startEditableBit: true,
hideEditableBits: true,
inBetweenEditableBits: true,
keys: {previous: 37, next: 39},
bitsOptions: {editable: {}, box: {}},
plugins: {},
// tip: you can change encode/decode with JSON.stringify and JSON.parse
encode: function(o){
return $.grep($.map(o, function(v){
v = (chk(v[0]) ? v[0] : v[1]);
return chk(v) ? v.toString().replace(/,/, '') : null;
}), function(o){ return o != undefined; }).join(',');
},
decode: function(o){ return o.split(','); }
}, _options);
element = $(element);
var self = this;
var init = function(){
original = element.css('display', 'none').attr('autocomplete', 'off').focus(focusLast);
container = $('<div id="'+options.id+'" class="'+options.prefix+'" />')
.insertAfter(element)
.click(function(e){
if ((e.target == list.get(0) || e.target == container.get(0)) && (!focused || (current && current.toElement().get(0) != list.find(':last-child').get(0)))) focusLast();
});
list = $('<ul class="'+ options.prefix +'-bits" />').appendTo(container);
for (var name in options.plugins) enablePlugin(name, options.plugins[name]);
afterInit();
};
var enablePlugin = function(name, options){
self.plugins[name] = new $.TextboxList[camelCase(capitalize(name))](self, options);
};
/**
* Clear the box and return all the values that where cleared
*/
var clear = function() {
element.val('')
var values = [];
list.children().each(function(){
var bit = getBit(this);
if (!bit.is('editable')) values.push(bit.remove());
});
return values;
}
var afterInit = function(){
if (options.endEditableBit) create('editable', null, {tabIndex: original.tabIndex}).inject(list);
addEvent('bitAdd', update, true);
addEvent('bitRemove', update, true);
$(document).click(function(e){
if (!focused) return;
if (e.target.className.indexOf(options.prefix) != -1){
if (e.target == $(container).get(0)) return;
var parent = $(e.target).parents('div.' + options.prefix);
if (parent.get(0) == container.get(0)) return;
}
blur();
}).keydown(function(ev){
if (!focused || !current) return;
var caret = current.is('editable') ? current.getCaret() : null;
var value = current.getValue()[1];
var special = !!$.map(['shift', 'alt', 'meta', 'ctrl'], function(e){ return ev[e]; }).length;
var custom = special || (current.is('editable') && current.isSelected());
var evStop = function(){ ev.stopPropagation(); ev.preventDefault(); };
switch (ev.which){
case 8:
if (current.is('box')){
evStop();
return current.remove();
}
case options.keys.previous:
if (current.is('box') || ((caret == 0 || !value.length) && !custom)){
evStop();
focusRelative('prev');
}
break;
case 46:
if (current.is('box')){
evStop();
return current.remove();
}
case options.keys.next:
if (current.is('box') || (caret == value.length && !custom)){
evStop();
focusRelative('next');
}
}
});
setValues(options.decode(original.val()));
};
var create = function(klass, value, opt){
if (klass == 'box'){
if (chk(options.max) && list.children('.' + options.prefix + '-bit-box').length + 1 > options.max) return false;
if (options.unique && $.inArray(uniqueValue(value), index) != -1) return false;
}
return new $.TextboxListBit(klass, value, self, $.extend(true, options.bitsOptions[klass], opt));
};
var uniqueValue = function(value){
return chk(value[0]) ? value[0] : (options.uniqueInsensitive ? value[1].toLowerCase() : value[1]);
}
var add = function(plain, id, html, afterEl){
var b = create('box', [id, plain, html]);
if (b){
if (!afterEl || !afterEl.length) afterEl = list.find('.' + options.prefix + '-bit-box').filter(':last');
b.inject(afterEl.length ? afterEl : list, afterEl.length ? 'after' : 'top');
}
return self;
};
var focusRelative = function(dir, to){
var el = getBit(to && $(to).length ? to : current).toElement();
var b = getBit(el[dir]());
if (b) b.focus();
return self;
};
var focusLast = function(){
var lastElement = list.children().filter(':last');
if (lastElement) getBit(lastElement).focus();
return self;
};
var blur = function(){
if (! focused) return self;
if (current) current.blur();
focused = false;
return fireEvent('blur');
};
var getBit = function(obj){
return (obj.type && (obj.type == 'editable' || obj.type == 'box')) ? obj : $(obj).data('textboxlist:bit');
};
var getValues = function(){
var values = [];
list.children().each(function(){
var bit = getBit(this);
if (!bit.is('editable')) values.push(bit.getValue());
});
return values;
};
var setValues = function(values){
if (!values) return;
$.each(values, function(i, v){
if (v) add.apply(self, $.isArray(v) ? [v[1], v[0], v[2]] : [v]);
});
};
var update = function(){
original.val(options.encode(getValues()));
};
var addEvent = function(type, fn){
if (events[type] == undefined) events[type] = [];
var exists = false;
$.each(events[type], function(f){
if (f === fn){
exists = true;
return;
};
});
if (!exists) events[type].push(fn);
return self;
};
var fireEvent = function(type, args, delay){
if (!events || !events[type]) return self;
$.each(events[type], function(i, fn){
(function(){
args = (args != undefined) ? splat(args) : Array.prototype.slice.call(arguments);
var returns = function(){
return fn.apply(self || null, args);
};
if (delay) return setTimeout(returns, delay);
return returns();
})();
});
return self;
};
var removeEvent = function(type, fn){
if (events[type]){
for (var i = events[type].length; i--; i){
if (events[type][i] === fn) events[type].splice(i, 1);
}
}
return self;
};
var isDuplicate = function(v){
return $.inArray(uniqueValue(v), index);
};
this.onFocus = function(bit){
if (current) current.blur();
clearTimeout(blurtimer);
current = bit;
container.addClass(options.prefix + '-focus');
if (!focused){
focused = true;
fireEvent('focus', bit);
}
};
this.onAdd = function(bit){
if (options.unique && bit.is('box')) index.push(uniqueValue(bit.getValue()));
if (bit.is('box')){
var prior = getBit(bit.toElement().prev());
if ((prior && prior.is('box') && options.inBetweenEditableBits) || (!prior && options.startEditableBit)){
var priorEl = prior && prior.toElement().length ? prior.toElement() : false;
var b = create('editable').inject(priorEl || list, priorEl ? 'after' : 'top');
if (options.hideEditableBits) b.hide();
}
}
};
this.onRemove = function(bit){
if (!focused) return;
if (options.unique && bit.is('box')){
var i = isDuplicate(bit.getValue());
if (i != -1) index = index.splice(i + 1, 1);
}
var prior = getBit(bit.toElement().prev());
if (prior && prior.is('editable')) prior.remove();
focusRelative('next', bit);
};
this.onBlur = function(bit, all){
current = null;
container.removeClass(options.prefix + '-focus');
blurtimer = setTimeout(blur, all ? 0 : 200);
};
this.setOptions = function(opt){
options = $.extend(true, options, opt);
};
this.getOptions = function(){
return options;
};
this.getContainer = function(){
return container;
};
this.isDuplicate = isDuplicate;
this.addEvent = addEvent;
this.removeEvent = removeEvent;
this.fireEvent = fireEvent;
this.create = create;
this.add = add;
this.getValues = getValues;
this.clear = clear;
this.plugins = [];
init();
};
$.TextboxListBit = function(type, value, textboxlist, _options){
var element, bit, prefix, typeprefix, close, hidden, focused = false, name = capitalize(type);
var options = $.extend(true, type == 'box' ? {
deleteButton: true
} : {
tabIndex: null,
growing: true,
growingOptions: {},
stopEnter: true,
addOnBlur: false,
addKeys: [13]
}, _options);
this.type = type;
this.value = value;
var self = this;
var init = function(){
prefix = textboxlist.getOptions().prefix + '-bit';
typeprefix = prefix + '-' + type;
bit = $('<li />').addClass(prefix).addClass(typeprefix)
.data('textboxlist:bit', self)
.hover(function(){
bit.addClass(prefix + '-hover').addClass(typeprefix + '-hover');
}, function(){
bit.removeClass(prefix + '-hover').removeClass(typeprefix + '-hover');
});
if (type == 'box'){
bit.html(chk(self.value[2]) ? self.value[2] : self.value[1]).click(focus);
if (options.deleteButton){
bit.addClass(typeprefix + '-deletable');
close = $('<a href="#" class="'+ typeprefix +'-deletebutton" />').click(remove).appendTo(bit);
}
bit.children().click(function(e){ e.stopPropagation(); e.preventDefault(); });
} else {
element = $('<input type="text" class="'+ typeprefix +'-input" autocomplete="off" />').val(self.value ? self.value[1] : '').appendTo(bit);
if (chk(options.tabIndex)) element.tabIndex = options.tabIndex;
if (options.growing) new $.GrowingInput(element, options.growingOptions);
element.focus(function(){ focus(true); }).blur(function(){
blur(true);
if (options.addOnBlur) toBox();
});
if (options.addKeys || options.stopEnter){
element.keydown(function(ev){
if (!focused) return;
var evStop = function(){ ev.stopPropagation(); ev.preventDefault(); };
if (options.stopEnter && ev.which === 13) evStop();
if ($.inArray(ev.which, splat(options.addKeys)) != -1){
evStop();
toBox();
}
});
}
}
};
var inject = function(el, where){
switch(where || 'bottom'){
case 'top': bit.prependTo(el); break;
case 'bottom': bit.appendTo(el); break;
case 'before': bit.insertBefore(el); break;
case 'after': bit.insertAfter(el); break;
}
textboxlist.onAdd(self);
return fireBitEvent('add');
};
var focus = function(noReal){
if (focused) return self;
show();
focused = true;
textboxlist.onFocus(self);
bit.addClass(prefix + '-focus').addClass(prefix + '-' + type + '-focus');
fireBitEvent('focus');
if (type == 'editable' && !noReal) element.focus();
return self;
};
var blur = function(noReal){
if (!focused) return self;
focused = false;
textboxlist.onBlur(self);
bit.removeClass(prefix + '-focus').removeClass(prefix + '-' + type + '-focus');
fireBitEvent('blur');
if (type == 'editable'){
if (!noReal) element.blur();
if (hidden && !element.val().length) hide();
}
return self;
};
var remove = function(){
blur();
textboxlist.onRemove(self);
bit.remove();
return fireBitEvent('remove');
};
var show = function(){
bit.css('display', 'block');
return self;
};
var hide = function(){
bit.css('display', 'none');
hidden = true;
return self;
};
var fireBitEvent = function(type){
type = capitalize(type);
textboxlist.fireEvent('bit' + type, self).fireEvent('bit' + name + type, self);
return self;
};
this.is = function(t){
return type == t;
};
this.setValue = function(v){
if (type == 'editable'){
element.val(chk(v[0]) ? v[0] : v[1]);
if (options.growing) element.data('growing').resize();
} else value = v;
return self;
};
this.getValue = function(){
return type == 'editable' ? [null, element.val(), null] : value;
};
if (type == 'editable'){
this.getCaret = function(){
var el = element.get(0);
if (el.createTextRange){
var r = document.selection.createRange().duplicate();
r.moveEnd('character', el.value.length);
if (r.text === '') return el.value.length;
return el.value.lastIndexOf(r.text);
} else return el.selectionStart;
};
this.getCaretEnd = function(){
var el = element.get(0);
if (el.createTextRange){
var r = document.selection.createRange().duplicate();
r.moveStart('character', -el.value.length);
return r.text.length;
} else return el.selectionEnd;
};
this.isSelected = function(){
return focused && (self.getCaret() !== self.getCaretEnd());
};
var toBox = function(){
var value = self.getValue();
var b = textboxlist.create('box', value);
if (b){
b.inject(bit, 'before');
self.setValue([null, '', null]);
return b;
}
return null;
};
this.toBox = toBox;
}
this.toElement = function(){
return bit;
};
this.focus = focus;
this.blur = blur;
this.remove = remove;
this.inject = inject;
this.show = show;
this.hide = hide;
this.fireBitEvent = fireBitEvent;
init();
};
var chk = function(v){ return !!(v || v === 0); };
var splat = function(a){ return $.isArray(a) ? a : [a]; };
var camelCase = function(str){ return str.replace(/-\D/g, function(match){ return match.charAt(1).toUpperCase(); }); };
var capitalize = function(str){ return str.replace(/\b[a-z]/g, function(A){ return A.toUpperCase(); }); };
$.fn.extend({
textboxlist: function(options){
return this.each(function(){
new $.TextboxList(this, options);
});
}
});
})(jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment