JQuery AutoComplete Append to Open List - javascript

I tested the JQuery autocomplete UI widget and noticed that the autocomplete has extremely poor performance in IE when dealing with large amount of data. My client uses internet explorer 7.
I found a solution to mitigate the performance issue. Instead of returning all the matches of the autocomplete search I only return the first 40 matches. Code below
source: function (request, response) {
var matcher = new RegExp("^" + $.ui.autocomplete.escapeRegex(request.term), "i");
var select_el = select.get(0); // get dom element
var rep = new Array(); // response array
var maxRepSize = 40; // maximum response size
// simple loop for the options
var looper = 0;
for (var i = 0; i < select_el.length; i++) {
var text = select_el.options[i].text;
if (!request || request == '') {
// add element to result array
rep[looper++] = {
label: text,
value: text,
option: select_el.options[i]
};
}
else if ( select_el.options[i].value && matcher.test(text) ) {
// add element to result array
rep[looper++] = {
label: text,
value: text,
option: select_el.options[i]
};
}
if ( rep.length > maxRepSize ) {
needMoreItems = true;
break;
}
}
// send response
response( rep );
},
The client asked me to append a "More Results" item to the autocomplete list. If there were more than 40 items that matched the search the "More Results" item would appear of at the bottom of the list. If a user clicked on the "More Results" item, the autocomplete drop down would expand to include the next 40 matches. I experimented with the jQuery autocomplete and I was able to populate the auto suggest list with the next 40 items but when the user clicked on one of the dynamically added items, I could not bind the click event to the Select Event of the autocomplete UI widget. Code below:
open: function( event, ui ) {
if (needMoreItems) {
needMoreItems = false;
$('<li class="ui-menu-item" role="menuitem" id="yoADDMORE" ><a class="ui-corner-all" tabindex="-1">... more available<br/><br/></a></li>')
.bind({
click: function(e) {
var appendHtml = '';
var select_el = select.get(0);
var maxRepSize = 40; // maximum response size
// simple loop for the options
var looper = 0;
for (var i = 41; i < select_el.length; i++) {
appendHtml += '<li class="ui-menu-item" role="menuitem"><a class="ui-corner-all" tabindex="-1">' + select_el.options[i].text + '</a></li>';
if ( looper ++ > maxRepSize ) {
needMoreItems = true;
break;
}
}
if (needMoreItems)
appendHtml += '<li class="ui-menu-item" role="menuitem" id="yoADDMORE" ><a class="ui-corner-all" tabindex="-1">... more available<br/><br/></a></li>';
$('#yoADDMORE').remove();
$('ul.ui-autocomplete').html($('ul.ui-autocomplete').html() + appendHtml);
$('ul.ui-autocomplete > li')
.bind({
mouseenter: function(e) {
// Hover event handler
$("> a",this).attr('class','ui-corner-all ui-state-hover');
},
mouseleave: function(e) {
// Hover event handler
$("> a",this).attr('class','ui-corner-all');
}
});
},
mouseenter: function(e) {
// Hover event handler
$("> a",this).attr('class','ui-corner-all ui-state-hover');
},
mouseleave: function(e) {
// Hover event handler
$("> a",this).attr('class','ui-corner-all');
}
})
.appendTo('ul.ui-autocomplete');
}
},
Links to jsFiddle
http://jsfiddle.net/eyecode/sX4Ba/
Any help would be appreciated.
Thanks in Advance

In order to list the top 20 options with a "More .." at the bottom of the first 20 items. Once a user presses "More .." link. All the items will be displayed in the drop down.
(function ($) {
$.widget("ui.typeaheadtextbox", {
_create: function () {
var self = this,
select = this.element.hide(),
theWidth = select.width(),
selected = select.children(":selected"),
value = selected.val() ? selected.text() : "";
var _searchItem = '';
var _more = false;
var _lastIndex = 0;
var _maxSize = 20;
var _rep = null;
var input = this.input = $("<input id='" + select[0].id + "_jq' style=\"width: " + theWidth + "px\">")
.insertAfter(select)
.val(value)
.autocomplete({
delay: 10,
minLength: 0,
source: function (request, response) {
var matcher = new RegExp("^" + $.ui.autocomplete.escapeRegex(request.term), "i");
var j = 0;
_rep = response;
_searchItem = request.term;
var select_el = select.get(0); // get dom element
var rep = new Array(); // response array
for (var i = 0; i < select_el.length; i++) {
var text = select_el.options[i].text;
if (select_el.options[i].value && (!request.term || matcher.test(text))) {
j++;
if (j > _maxSize) {
_more = true;
_lastIndex = i;
break;
}
// add element to result array
rep[(j - 1)] = {
label: text.replace(new RegExp("^(?![^&;]+;)(?!<[^<>]*)(" + $.ui.autocomplete.escapeRegex(_searchItem) + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>"), value: text, option: select_el.options[i]
};
}
}
return response(rep);
},
autoFocus:true,
open: function(event, ui) {
if (_more) {
_more = false;
$('<li class="ui-menu-item_more" role="menuitem" id="' + select.get(0).id + '_ADDMORE_' + _lastIndex + '" ><a class="ui-corner-all" tabindex="-1"><img width="16" src="Content/themes/base/images/icon-search-small.png"/> <strong>View All</strong></a></li>')
.bind({
click: function(e) {
var response = _rep;
var matcher = new RegExp("^" + $.ui.autocomplete.escapeRegex(_searchItem), "i");
var select_el = select.get(0); // get dom element
var rep = new Array(); // response array
var j = 0;
for (var i = 0; i < select_el.length; i++) {
var text = select_el.options[i].text;
if (select_el.options[i].value && (!_searchItem || matcher.test(text))) {
// add element to result array
rep[j++] = {
label: text.replace(new RegExp("^(?![^&;]+;)(?!<[^<>]*)(" + $.ui.autocomplete.escapeRegex(_searchItem) + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>"), value: text, option: select_el.options[i]
};
}
}
$(this).remove();
return _rep(rep);
},
mouseenter: function(e) { $("> a",this).attr('class','ui-corner-all ui-state-hover'); },
mouseleave: function(e) { $("> a",this).attr('class','ui-corner-all');}
})
.appendTo('ul.ui-autocomplete');
_lastIndex = 0;
return true;
}
},
select: function (event, ui) {
ui.item.option.selected = true;
self._trigger("selected", event, {
item: ui.item.option
});
},
change: function (event, ui) {
if (!ui.item) {
var matcher = new RegExp("^" + $.ui.autocomplete.escapeRegex($(this).val()) + "$", "i"),
valid = false;
select.children("option").each(function () {
if ($(this).text().match(matcher)) {
this.selected = valid = true;
return false;
}
});
if (!valid) {
// remove invalid value, as it didn't match anything
$(this).val("");
select.val("");
input.data("autocomplete").term = "";
return false;
}
}
}
})
.addClass("ui-widget ui-widget-content ui-corner-all");
input.data("autocomplete")._renderMenu = function( ul, items ) {
var self = this; var htm = ''; var beginHtm = '<li><a>'; var endHtm = '</a></li>';
for(var i=0;i<items.length;i++) {
htm += beginHtm + items[i].label + endHtm;
}
ul[0].innerHTML = htm;
var liTags = ul[0].getElementsByTagName('li');
for(var i=0;i<liTags.length;i++) {
$(liTags[i]).data("item.autocomplete", items[i]);
}
return true;
};
},
destroy: function () {
this.input.remove();
this.element.show();
$.Widget.prototype.destroy.call(this);
},
clear: function () {
this.element.val("");
this.input.val("");
}
});
})(jQuery);

Related

jQuery plugin/library for this table to simplify code

https://jsfiddle.net/51Le6o06/48/
please take a look at the jsfiddle the code is getting to complicated and my functions aren't working correctly.
can anyone tell me what I could use instead of standard jQuery and javascript to make this easier to build (with a show more style pagination method).
I need to sort, filter and page existing html as in the jsfiddle.
thanks.
$(document).ready(function() {
$('.filter-gift').each(filterItems);
});
function filterItems(e) {
var items = [];
var table = '';
tableId = $(this).parent().parent().attr('tag')
var listItems = "";
listItems += "<option value=''> -Select- </option>";
$('div[tag="' + tableId + '"] table.internalActivities .information').each(function (i) {
var itm = $(this)[0].innerText;
if ($.inArray(itm, items) == -1) {
items.push($(this)[0].innerText);
listItems += "<option value='" + i + "'>" + $(this)[0].innerText + "</option>";
}
});
$('div[tag="' + tableId+ '"] .filter-gift').html(listItems);
$('.filter-gift').change(function () {
if($(this).val()!= "") {
var tableIdC = $(this).parent().parent().attr('tag');
var text = $('div[tag="' + tableIdC + '"] select option:selected')[0].text.replace(/(\r\n|\n|\r| |)/gm, "");;
$('div[tag="' + tableIdC + '"] .product-information-row').each(function (i) {
if ($(this).text().replace(/(\r\n|\n|\r| |)/gm, "") == text) {
$(this).show();
$(this).prev().show();
$(this).next().show();
}
else {
$(this).hide();
$(this).prev().hide();
$(this).next().hide();
}
});
} else {
$(this).parent().parent().find('table tr').show();
}
});
}
jQuery.fn.sortPaging = function(options) {
var defaults = {
pageRows: 2
};
var settings = $.extend(true, defaults, options);
return this.each(function() {
var container = $(this);
var tableBody = container.find('.internalActivities > tbody');
var dataRows = [];
var currentPage = 1;
var maxPages = 1;
var buttonMore = container.find('.seeMoreRecords');
var buttonLess = container.find('.seeLessRecords');
var buttonFree = container.find('.filter-free');
var tableRows = [];
var maxFree = 0;
var filterFree = buttonFree.is(':checked');
function displayRows() {
tableBody.empty();
var displayed = 0;
$.each(dataRows, function(i, ele) {
if( !filterFree || (filterFree && ele.isFree) ) {
tableBody.append(ele.thisRow).append(ele.nextRow);
displayed++;
if( displayed >= currentPage*settings.pageRows ) {
return false;
};
};
});
};
function checkButtons() {
buttonLess.toggleClass('element_invisible', currentPage<=1);
buttonMore.toggleClass('element_invisible', filterFree ? currentPage>=maxFreePages : currentPage>=maxPages);
};
function showMore() {
currentPage++;
displayRows();
checkButtons();
};
function showLess() {
currentPage--;
displayRows();
checkButtons();
};
function changedFree() {
filterFree = buttonFree.is(':checked');
if( filterFree && currentPage>maxFreePages ) {
currentPage=maxFreePages;
};
displayRows();
checkButtons();
};
tableBody.find('.product-data-row').each(function(i, j) {
var thisRow = $(this);
var nextRow = thisRow.next();
var amount = parseFloat(thisRow.find('.amount').text().replace(/£/, ''));
var isFree = thisRow.find('.free').length;
maxFree += isFree;
dataRows.push({
amount: amount,
thisRow: thisRow,
nextRow: nextRow,
isFree: isFree
});
})
dataRows.sort(function(a, b) {
return a.amount - b.amount;
});
maxPages = Math.ceil(dataRows.length/settings.pageRows);
maxFreePages = Math.ceil(maxFree/settings.pageRows);
tableRows = tableBody.find("tr");
buttonMore.on('click', showMore);
buttonLess.on('click', showLess);
buttonFree.on('change', changedFree);
displayRows();
checkButtons();
})
};
$('.sort_paging').sortPaging();
The best solution when it comes to tables with all the filter, sorting, pagination features and much more is one and only.
jQuery Datatables
Just check out the link, It's Easy and Highly Customizable.
Maybe you could use this jquery plug-in DataTables

Refactoring repeated code in javascript prototype constructor

I have a open function that once triggered, simply plays video in a dedicated panel.
This function can be triggered in two ways - one with a click and another one with a page load (window load) with url that contains a valid anchor tag.
They all work fine but some codes of the window load handler are repetitive and I'm not too sure how I can keep this DRY.
Please take a look and point me in some directions on how I can write this better.
I commented in open function which is for which.
$.videoWatch.prototype = {
init: function() {
this.$openLinks = this.$element.find(".open");
this.$closeLinks = this.$element.find(".close");
this.open();
this.close();
},
_getContent: function(element) {
var $parent = element.parent(),
id = element.attr('href').substring(1),
title = $parent.data('title'),
desc = $parent.data('desc');
return {
title: title,
desc: desc,
id: id
}
},
open: function() {
var self = this;
//open theatre with window load with #hash id
window.onload = function() {
var hash = location.hash;
var $a = $('a[href="' + hash + '"]'),
content = self._getContent($a),
$li = $a.parents("li"),
$theatreVideo = $(".playing"),
$theatreTitle = $(".theatre-title"),
$theatreText = $(".theatre-text");
$(".theatre").attr('id', content.id);
$theatreTitle.text(content.title);
$theatreText.text(content.desc);
if ($theatreText.text().length >= 90) {
$(".theatre-text").css({
'overflow': 'hidden',
'max-height': '90px',
});
$moreButton.insertAfter($theatreText);
}
$a.parent().addClass("active");
$(".theatre").insertAfter($li);
$(".theatre").slideDown('fast', scrollToTheatre);
oldIndex = $li.index();
}
//open theatre with click event
self.$openLinks.on("click", function(e) {
// e.preventDefault();
if (curID == $(this).parent().attr("id")) {
$("figure").removeClass("active");
$("button.more").remove();
$(".theatre").slideUp('fast');
$('.playing').attr("src", "");
removeHash();
oldIndex = -1;
curID = "";
return false
} else {
curID = $(this).parent().attr("id");
}
var $a = $(this),
content = self._getContent($a),
$li = $a.parents("li"),
$theatreVideo = $(".playing"),
$theatreTitle = $(".theatre-title"),
$theatreText = $(".theatre-text");
$(".theatre").attr('id', content.id);
$theatreTitle.text(content.title);
$theatreText.text(content.desc);
if ($theatreText.text().length >= 90) {
$(".theatre-text").css({
'overflow': 'hidden',
'max-height': '90px',
});
$moreButton.insertAfter($theatreText);
}
if (!($li.index() == oldIndex)) {
$("figure").removeClass("active");
$(".theatre").hide(function(){
$a.parent().addClass("active");
$(".theatre").insertAfter($li);
$(".theatre").slideDown('fast', scrollToTheatre);
oldIndex = $li.index();
});
} else {
$(".theatre").insertAfter($li);
scrollToTheatre();
$("figure").removeClass("active");
$a.parent().addClass("active");
}
});
},
...
Simplified and refactored open method:
open: function() {
var self = this;
var serviceObj = {
theatreVideo : $(".playing"),
theatre: $(".theatre"),
theatreTitle : $(".theatre-title"),
theatreText : $(".theatre-text"),
setTheatreContent: function(content){
this.theatre.attr('id', content.id);
this.theatreTitle.text(content.title);
this.theatreText.text(content.desc);
if (this.theatreText.text().length >= 90) {
this.theatreText.css({
'overflow': 'hidden',
'max-height': '90px',
});
$moreButton.insertAfter(this.theatreText);
}
},
activateTeatre: function(a, li){
a.parent().addClass("active");
this.theatre.insertAfter(li);
this.theatre.slideDown('fast', scrollToTheatre);
oldIndex = li.index();
}
};
//open theatre with window load with #hash id
window.onload = function() {
var hash = location.hash;
var $a = $('a[href="' + hash + '"]'),
content = self._getContent($a),
$li = $a.parents("li");
serviceObj.setTheatreContent(content);
serviceObj.activateTeatre($a, $li);
}
//open theatre with click event
self.$openLinks.on("click", function(e) {
// e.preventDefault();
if (curID == $(this).parent().attr("id")) {
$("figure").removeClass("active");
$("button.more").remove();
$(".theatre").slideUp('fast');
$('.playing').attr("src", "");
removeHash();
oldIndex = -1;
curID = "";
return false
} else {
curID = $(this).parent().attr("id");
}
var $a = $(this),
content = self._getContent($a),
$li = $a.parents("li");
serviceObj.setTheatreContent(content);
if (!($li.index() == oldIndex)) {
$("figure").removeClass("active");
$(".theatre").hide(function(){
serviceObj.activateTeatre($a, $li);
});
} else {
$(".theatre").insertAfter($li);
scrollToTheatre();
$("figure").removeClass("active");
$a.parent().addClass("active");
}
});
},
1st of all there are variables that don't depend on the input, you could pull them to the class (I'll show just one example, as you asked for directions):
init: function() {
this.$theatreVideo = $(".playing");
All the variables that do depend on the input, like $li could be moved to a function:
var $a = $(this),
$dependsOnA = self.dependsOnA($a);
self.actionDependsOnA($dependsOnA); // see below
function dependsOnA($a) {
return {
a: $a,
li: $a.parents("li"),
content: self._getContent($a)
}
}
Also the code that "repeats" can be moved to a function:
function actionDependsOnA($dependsOnA)
$(".theatre").attr('id', $dependsOnA.content.id);
$theatreTitle.text($dependsOnA.content.title);
$theatreText.text($dependsOnA.content.desc);
}

Setup jqueryui before element is rendered

In creating a table cells with very cool options I need to take two steps. First I create the table cell as a DOM object in javascript with all the 'normal' functionality, like is it a checkbox, a select, an input, a link, etc.
Then I need to put the table on the page to do the next step, which I consider advanced functionality like jqueryui's autocomplete and date select. It is awesome to have these drop downs for the user.
I do have this working, however, I do need to make two calls. The first to createTableCell, then I render, then I need to make another call to postRenderTableFunctions to get the autocomplete functionality working. So the question is, why the second call, what is it about jqueryui that will not work on the first call. You can see the commented section under input creation where I tried to get the autocomplete working.
function createTableCell(name, td_def)
{
var cell=document.createElement('td');
if(td_def['type'] == 'date')
{
var element = document.createElement('input');
element.name = name;
element.id = name;
cell.appendChild(element);
$('#' + element.id).datepicker(
{
dateFormat: 'yy-mm-dd',
onSelect: function ()
{
$(this).focus();
},
onClose: function (dateText, inst)
{
$(this).select();
}
});
}
else if(td_def['type'] == 'checkbox')
{
element = document.createElement('input');
element.type = 'checkbox';
element.name = name;
element.id = name;
/*if(this.tdo[r][td_def['db_field']]['data'] == 1)
{
element.checked = true;
}*/
cell.appendChild(element);
}
else if (td_def['type'] == 'select')
{
element = document.createElement('select');
element.name = name;
element.id = name;
var option = document.createElement('option');
option.value = 'NULL';
option.appendChild(document.createTextNode("Select..."));
element.appendChild(option);
for (var j in td_def['select_names'])
{
option = document.createElement('option');
option.value = td_def['select_values'][j];
option.appendChild(document.createTextNode(td_def['select_names'][j]));
element.appendChild(option);
}
cell.appendChild(element);
}
else if (td_def['type'] == 'tree_select')
{
element = document.createElement('select');
element.id = name;
element.name = name;
var option = document.createElement('option');
option.value = 'NULL';
option.appendChild(document.createTextNode("Select..."));
element.appendChild(option);
var level = 0;
//console.log(td_def['select_array']);
this.addNestedSelectOptions(element, td_def['select_array'], level);
if (typeof td_def['properties'] !== 'undefined')
{
for (var index in td_def['properties'])
{
eval('element.' + index + '= ' + td_def['properties'][index] + ';');
}
}
cell.appendChild(element);
}
else if (td_def['type'] == 'input')
{
element = document.createElement('input');
element.type = 'text';
element.id = name;
element.name = name;
cell.appendChild(element);
/*
if(typeof td_def['autoComplete'] != 'undefined')
{
console.log('attempting Autocomplete');
$( "#" + name ).autocomplete({
source: function( request, response )
{
$.ajax(
{
url: td_def['autoComplete']['url'],
type: 'GET',
async: true,
data:
{
ajax_request: td_def['autoComplete']['ajax_request'],
featureClass: "P",
style: "full",
maxRows: 12,
search_terms: request.term
},
success: function( data )
{
console.log(data);
parsed_autocomplete_data = parseJSONdata(data);
response( parsed_autocomplete_data);
}
});
},
search: function()
{
// custom minLength
var term = this.value;
//console.log(term);
if(typeof td_def['minLength'] != 'undefined')
{
var minL = td_def['minLength'];
}
else
{
var minL = 1;
}
if ( term.length < minL )
{
return false;
}
},
focus: function()
{
// prevent value inserted on focus
return false;
},
open: function(event, ui)
{
var dialog = $(this).closest('.ui-dialog');
if(dialog.length > 0){
$('.ui-autocomplete.ui-front').zIndex(dialog.zIndex()+1);
}
},
select: function( event, ui )
{
selected_autocomplete_index = $.inArray(ui.item.value, parsed_autocomplete_data);
}
});
}
*/
}
else if (td_def['type'] == 'textarea')
{
element = document.createElement('TEXTAREA');
element.id = name;
element.name = name;
cell.appendChild(element);
}
else if (td_def['type'] == 'td')
{
cell.innerHTML = 'TBD';
cell.name = name;
cell.id = name;
}
else
{
alert(td_def['type'] + ' have not coded that.....');
}
return cell;
function addNestedSelectOptions(element, category_array, level)
{
for (var key in category_array)
{
option = document.createElement('option');
option.value = key;
name = category_array[key]['name'];
for(i=0;i<level;i++)
{
name = "\u00A0" + name;
name = "\u00A0" + name;
}
option.appendChild(document.createTextNode(name));
element.appendChild(option);
if(!$.isEmptyObject(category_array[key]['children']))
{
addNestedSelectOptions(element, category_array[key]['children'], level+1);
}
}
}
}
//this function needs to be called separately.
function postRenderTableFunctions(table_div_id, table_def)
{
//extra jquery functionality -- needs to go after stuff is rendered
for(var i=0;i<table_def.length;i++)
{
if(typeof table_def[i]['autoComplete'] != 'undefined')
{
var id = table_div_id + '_' + table_def[i]['db_field'];
console.log(id);
//is the auto complete open?
/*$("#" + id ).bind('autocompleteopen', function(event, ui)
{
$(this).data('is_open',true);
});
$("#" + id ).bind('autocompleteclose', function(event, ui)
{
$(this).data('is_open',false);
});*/
/*$( "#" + id ).bind( "keydown", function( event )
{
//what is this for ?
if ( event.keyCode === $.ui.keyCode.TAB && $(this).data('is_open') )
{
event.preventDefault();
}
if ( event.keyCode === $.ui.keyCode.ENTER && !$(this).data('is_open'))
{
//do what?
}
});*/
var i2 = i;
var me = table_def;
$( "#" + id ).autocomplete({
source: function( request, response )
{
$.ajax(
{
url: me[i2]['autoComplete']['url'],
type: 'GET',
async: true,
data:
{
ajax_request: me[i2]['autoComplete']['ajax_request'],
featureClass: "P",
style: "full",
maxRows: 12,
search_terms: request.term
},
success: function( data )
{
console.log(data);
parsed_autocomplete_data = parseJSONdata(data);
response( parsed_autocomplete_data);
}
});
},
search: function()
{
// custom minLength
var term = this.value;
//console.log(term);
if(typeof table_def[i2]['minLength'] != 'undefined')
{
var minL = table_def[i2]['minLength'];
}
else
{
var minL = 1;
}
if ( term.length < minL )
{
return false;
}
},
focus: function()
{
// prevent value inserted on focus
return false;
},
open: function(event, ui)
{
var dialog = $(this).closest('.ui-dialog');
if(dialog.length > 0){
$('.ui-autocomplete.ui-front').zIndex(dialog.zIndex()+1);
}
},
select: function( event, ui )
{
selected_autocomplete_index = $.inArray(ui.item.value, parsed_autocomplete_data);
}
});
}
}
}
My original comment to your question -
I suspect i may work if you replace
$( "#" + name ).autocomplete({
with
$(element).autocomplete({
though I don't currently have means to test it.
My response -
Basically using $("#name") looks for an element with the ID name in the DOM. When you are running your code element has not been added to the DOM yet so the autocomplete cannot be attached to to your element.
Surrounding element with $() converts it to a jQuery variable which you can attach the autocomplete to before it is added to the DOM.

How to create a plugin to add attachment or file in rich text editor of Adobe CQ 5?

I need help in creating a plugin for rich text editor in Adobe cq 5 to add an image, pdf, video, ppt or any file into rich text editor.
The existing rte plugins that are available are findreplace, undo, spellcheck, table etc
How to create a plugin to add a file to rich text editor?
The plugins are an ext js files. Appreciate if any one can suggest answer. It will be of great help.
Thanks
I added a custom drop down into my RTE. It was used to add values to the editor on selecting values from it. I think this might help you solve your issue because similarly you can create your required plugin.
Please refer comments next to the methods for your reference.
/**
* Placeholder Plugin
*/
var keyvalueEnteries = [];
var newText;
var doc;
var range
CUI.rte.plugins.PlaceholderPlugin = new Class({
toString: "PlaceholderPlugin",
extend: CUI.rte.plugins.Plugin,
/**
* #private
*/
cachedFormats: null,
/**
* #private
*/
formatUI: null,
getFeatures: function() {
return [ "Myparaformat" ];
},
/**
* #private
*
*/
getKeys: function() {
var com = CUI.rte.Common;
if (this.cachedFormats == null) {
this.cachedFormats = this.config.formats || { };
com.removeJcrData(this.cachedFormats);
this.cachedFormats = com.toArray(this.cachedFormats, "tag", "description");
}
return this.cachedFormats;
},
initializeUI: function(tbGenerator) {
var plg = CUI.rte.plugins;
var ui = CUI.rte.ui;
if (this.isFeatureEnabled("placeHolder")) {
this.formatUI = tbGenerator.createParaFormatter("placeHolder", this,null,this.getKeys());
tbGenerator.addElement("placeHolder", plg.Plugin.SORT_PARAFORMAT, this.formatUI,
10);
}
},
notifyPluginConfig: function(pluginConfig) { //This function gets executed once on load of RTE for the first time
var url = pluginConfig.sourceURL;
keyvalueEnteries = CQ.HTTP.eval(url);
keyvalueEnteries = JSON.stringify(keyvalueEnteries);
if(keyvalueEnteries == undefined){
keyvalueEnteries = '[{"key":"","value":"None"}]';
}
pluginConfig = pluginConfig || { };
//creating JSON sttructure
var placeholderJSON = '{"formats":' + keyvalueEnteries + '}';
var placeHolderVals = eval('(' + placeholderJSON + ')');
var defaults = placeHolderVals;
if (pluginConfig.formats) {
delete defaults.formats;
}
CUI.rte.Utils.applyDefaults(pluginConfig, defaults);
this.config = pluginConfig;
},
execute: function(cmd) { // This function gets executed when there is change in the state of custom plugin/drop down
if (this.formatUI) {
var formatId = this.formatUI.getSelectedFormat();
if (formatId && range != undefined) {
var placeholderElement = "";
if(formatId == 'none' && range.collapsed == false){//checks if None is selected in placeholder and the text is selected
range.deleteContents();
}else if(formatId != 'none'){
placeholderElement = document.createTextNode(" ${" + formatId + "} ");
range.insertNode(placeholderElement); //INSERTS PLACEHOLDER AT CURRENT CARET LOCATION
range.setStartAfter(placeholderElement);//INSERTS CURSOR AT THE END OF CURRENT PLACEHOLDER IF PLACEHOLDER IS SELECTED AGAIN
}
}
}
},
updateState: function(selDef) { // This function gets executed on click on the editor space in RTE
doc = selDef.editContext.doc; //GET THE DOCUMENT INSIDE THE IFRAME
range = doc.getSelection().getRangeAt(0); //GETS CURRENT CARET POSITION
}
});
//register plugin
CUI.rte.plugins.PluginRegistry.register("placeholder",
CUI.rte.plugins.PlaceholderPlugin);
CUI.rte.ui.ext.ParaFormatterImpl = new Class({
toString: "ParaFormatterImpl",
extend: CUI.rte.ui.TbParaFormatter,
// Interface implementation ------------------------------------------------------------
addToToolbar: function(toolbar) {
var com = CUI.rte.Common;
this.toolbar = toolbar;
if (com.ua.isIE) {
// the regular way doesn't work for IE anymore with Ext 3.1.1, hence working
// around
var helperDom = document.createElement("span");
helperDom.innerHTML = "<select class=\"x-placeholder-select\">"
+ this.createFormatOptions() + "</select>";
this.formatSelector = CQ.Ext.get(helperDom.childNodes[0]);
} else {
this.formatSelector = CQ.Ext.get(CQ.Ext.DomHelper.createDom({
tag: "select",
cls: "x-placeholder-select",
html: this.createFormatOptions()
}));
}
this.initializeSelector();
toolbar.add(
CQ.I18n.getMessage("Placeholder"), // Type the name you want to appear in RTE for the custom plugin /drop down
" ",
this.formatSelector.dom);
},
/**
* Creates HTML code for rendering the options of the format selector.
* #return {String} HTML code containing the options of the format selector
* #private
*/
createFormatOptions: function() {
var htmlCode = "";
if (this.formats) {
var formatCnt = this.formats.length;
htmlCode += "<option value='none'>None</option>";
for (var f = 0; f < formatCnt; f++) {
var text = this.formats[f].text;
htmlCode += "<option value=\"" + this.formats[f].value + "\">" + text
+ "</option>";
}
}
return htmlCode;
},
createToolbarDef: function() {
return {
"xtype": "panel",
"itemId": this.id,
"html": "<select class=\"x-placeholder-select\">"
+ this.createFormatOptions() + "</select>",
"listeners": {
"afterrender": function() {
var item = this.toolbar.items.get(this.id);
if (item && item.body) {
this.formatSelector = CQ.Ext.get(item.body.dom.childNodes[0]);
this.initializeSelector();
}
},
"scope": this
}
};
},
initializeSelector: function() {
this.formatSelector.on('change', function() {
var format = this.formatSelector.dom.value;
if (format.length > 0) {
this.plugin.execute(this.id);
}
}, this);
this.formatSelector.on('focus', function() {
this.plugin.editorKernel.isTemporaryBlur = true;
}, this);
// fix for a Firefox problem that adjusts the combobox' height to the height
// of the largest entry
this.formatSelector.setHeight(20);
},
getSelectorDom: function() {
return this.formatSelector.dom;
},
getSelectedFormat: function() {
var format;
if (this.formatSelector) {
format = this.formatSelector.dom.value;
if (format.length > 0) {
return format;
}
} else {
var item = this.toolbar.items.get(this.id);
if (item.getValue()) {
return item;
}
}
return null;
},
selectFormat: function(formatToSelect, auxRoot, formatCnt, noFormatCnt) {
var indexToSelect = -1;
var selectorDom = this.formatSelector.dom;
if ((formatToSelect != null) && (formatCnt == 1) && (noFormatCnt == 0)) {
var options = selectorDom.options;
for (var optIndex = 0; optIndex < options.length; optIndex++) {
var optionToCheck = options[optIndex];
if (optionToCheck.value == formatToSelect) {
indexToSelect = optIndex;
break;
}
}
}
selectorDom.disabled = (noFormatCnt > 0) && (auxRoot == null);
selectorDom.selectedIndex = indexToSelect;
}
});

restricting input tags to maximum 5

I have got following jquery tags plugin.
I want to to restrict maxmimum 5 tags, so that user can not enter more than 5 words (separated by spaces).
Can someone please help me doing it?
Thanks.. Following is original plugin code:
(function($) {
var delimiter = new Array();
jQuery.fn.addTag = function(value,options) {
var options = jQuery.extend({focus:false},options);
this.each(function() {
id = $(this).attr('id');
var tagslist = $(this).val().split(delimiter[id]);
if (tagslist[0] == '') {
tagslist = new Array();
}
value = jQuery.trim(value);
if (value !='') {
$('<span class="tag">'+value + ' x</span>').insertBefore('#'+id+'_addTag');
tagslist.push(value);
$('#'+id+'_tag').val('');
if (options.focus) {
$('#'+id+'_tag').focus();
} else {
$('#'+id+'_tag').blur();
}
}
jQuery.fn.tagsInput.updateTagsField(this,tagslist);
});
return false;
};
jQuery.fn.removeTag = function(value) {
this.each(function() {
id = $(this).attr('id');
var old = $(this).val().split(delimiter[id]);
$('#'+id+'_tagsinput .tag').remove();
str = '';
for (i=0; i< old.length; i++) {
if (escape(old[i])!=value) {
str = str + delimiter[id] +old[i];
}
}
jQuery.fn.tagsInput.importTags(this,str);
});
return false;
};
jQuery.fn.tagsInput = function(options) {
var settings = jQuery.extend({defaultText:'add a tag',width:'300px',height:'100px','hide':true,'delimiter':',',autocomplete:{selectFirst:false}},options);
this.each(function() {
if (settings.hide) {
$(this).hide();
}
id = $(this).attr('id')
data = jQuery.extend({
pid:id,
real_input: '#'+id,
holder: '#'+id+'_tagsinput',
input_wrapper: '#'+id+'_addTag',
fake_input: '#'+id+'_tag',
},settings);
delimiter[id] = data.delimiter;
$('<div id="'+id+'_tagsinput" class="tagsinput"><div id="'+id+'_addTag"><input id="'+id+'_tag" value="" default="'+settings.defaultText+'" /></div><div class="tags_clear"></div></div>').insertAfter(this);
$(data.holder).css('width',settings.width);
$(data.holder).css('height',settings.height);
if ($(data.real_input).val()!='') {
jQuery.fn.tagsInput.importTags($(data.real_input),$(data.real_input).val());
} else {
$(data.fake_input).val($(data.fake_input).attr('default'));
$(data.fake_input).css('color','#666666');
}
$(data.holder).bind('click',data,function(event) {
$(event.data.fake_input).focus();
});
// if user types a comma, create a new tag
$(data.fake_input).bind('keypress',data,function(event) {
if (event.which==event.data.delimiter.charCodeAt(0) || event.which==13) {
$(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:true});
return false;
}
});
$(data.fake_input).bind('focus',data,function(event) {
if ($(event.data.fake_input).val()==$(event.data.fake_input).attr('default')) {
$(event.data.fake_input).val('');
}
$(event.data.fake_input).css('color','#000000');
});
if (settings.autocomplete_url != undefined) {
$(data.fake_input).autocomplete(settings.autocomplete_url,settings.autocomplete).bind('result',data,function(event,data,formatted) {
if (data) {
d = data + "";
$(event.data.real_input).addTag(d,{focus:true});
}
});;
$(data.fake_input).bind('blur',data,function(event) {
if ($(event.data.fake_input).val() != $(event.data.fake_input).attr('default')) {
$(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:false});
}
$(event.data.fake_input).val($(event.data.fake_input).attr('default'));
$(event.data.fake_input).css('color','#666666');
return false;
});
} else {
// if a user tabs out of the field, create a new tag
// this is only available if autocomplete is not used.
$(data.fake_input).bind('blur',data,function(event) {
var d = $(this).attr('default');
if ($(event.data.fake_input).val()!='' && $(event.data.fake_input).val()!=d) {
event.preventDefault();
$(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:true});
} else {
$(event.data.fake_input).val($(event.data.fake_input).attr('default'));
$(event.data.fake_input).css('color','#666666');
}
return false;
});
}
$(data.fake_input).blur();
});
return this;
};
jQuery.fn.tagsInput.updateTagsField = function(obj,tagslist) {
id = $(obj).attr('id');
$(obj).val(tagslist.join(delimiter[id]));
};
jQuery.fn.tagsInput.importTags = function(obj,val) {
$(obj).val('');
id = $(obj).attr('id');
var tags = val.split(delimiter[id]);
for (i=0; i<tags.length; i++) {
$(obj).addTag(tags[i],{focus:false});
}
};
})(jQuery);
best way is to count the number of "tag" classes already added, and then you can handle it differently, for example you can prevent showing the "add a tag" input once 5 tags inserted by defining maxTags and updating jQuery.fn.addTag and jQuery.fn.removeTag :
/*
jQuery Tags Input Plugin 1.0
Copyright (c) 2010 XOXCO, Inc
Documentation for this plugin lives here:
http://xoxco.com/clickable/jquery-tags-input
Licensed under the MIT license:
http://www.opensource.org/licenses/mit-license.php
ben#xoxco.com
*/
(function($) {
var delimiter = new Array();
var maxTags = 5;
jQuery.fn.addTag = function(value,options) {
var options = jQuery.extend({focus:false},options);
this.each(function() {
id = $(this).attr('id');
var tagslist = $(this).val().split(delimiter[id]);
if (tagslist[0] == '') {
tagslist = new Array();
}
value = jQuery.trim(value);
if (value !='') {
$('<span class="tag">'+value + ' x</span>').insertBefore('#'+id+'_addTag');
tagslist.push(value);
$('#'+id+'_tag').val('');
if (options.focus) {
$('#'+id+'_tag').focus();
} else {
$('#'+id+'_tag').blur();
}
}
jQuery.fn.tagsInput.updateTagsField(this,tagslist);
});
if($(".tag").length>maxTags-1){$('#'+id+'_addTag').hide()}
return false;
};
jQuery.fn.removeTag = function(value) {
this.each(function() {
id = $(this).attr('id');
var old = $(this).val().split(delimiter[id]);
$('#'+id+'_tagsinput .tag').remove();
str = '';
for (i=0; i< old.length; i++) {
if (escape(old[i])!=value) {
str = str + delimiter[id] +old[i];
}
}
jQuery.fn.tagsInput.importTags(this,str);
});
if($(".tag").length<maxTags){$('#'+id+'_addTag').show()}
return false;
};
jQuery.fn.tagsInput = function(options) {
var settings = jQuery.extend({defaultText:'add a tag',width:'300px',height:'100px','hide':true,'delimiter':',',autocomplete:{selectFirst:false}},options);
this.each(function() {
if (settings.hide) {
$(this).hide();
}
id = $(this).attr('id')
data = jQuery.extend({
pid:id,
real_input: '#'+id,
holder: '#'+id+'_tagsinput',
input_wrapper: '#'+id+'_addTag',
fake_input: '#'+id+'_tag',
},settings);
delimiter[id] = data.delimiter;
$('<div id="'+id+'_tagsinput" class="tagsinput"><div id="'+id+'_addTag"><input id="'+id+'_tag" value="" default="'+settings.defaultText+'" /></div><div class="tags_clear"></div></div>').insertAfter(this);
$(data.holder).css('width',settings.width);
$(data.holder).css('height',settings.height);
if ($(data.real_input).val()!='') {
jQuery.fn.tagsInput.importTags($(data.real_input),$(data.real_input).val());
} else {
$(data.fake_input).val($(data.fake_input).attr('default'));
$(data.fake_input).css('color','#666666');
}
$(data.holder).bind('click',data,function(event) {
$(event.data.fake_input).focus();
});
// if user types a comma, create a new tag
$(data.fake_input).bind('keypress',data,function(event) {
if (event.which==event.data.delimiter.charCodeAt(0) || event.which==13) {
$(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:true});
return false;
}
});
$(data.fake_input).bind('focus',data,function(event) {
if ($(event.data.fake_input).val()==$(event.data.fake_input).attr('default')) {
$(event.data.fake_input).val('');
}
$(event.data.fake_input).css('color','#000000');
});
if (settings.autocomplete_url != undefined) {
$(data.fake_input).autocomplete(settings.autocomplete_url,settings.autocomplete).bind('result',data,function(event,data,formatted) {
if (data) {
d = data + "";
$(event.data.real_input).addTag(d,{focus:true});
}
});;
$(data.fake_input).bind('blur',data,function(event) {
if ($(event.data.fake_input).val() != $(event.data.fake_input).attr('default')) {
$(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:false});
}
$(event.data.fake_input).val($(event.data.fake_input).attr('default'));
$(event.data.fake_input).css('color','#666666');
return false;
});
} else {
// if a user tabs out of the field, create a new tag
// this is only available if autocomplete is not used.
$(data.fake_input).bind('blur',data,function(event) {
var d = $(this).attr('default');
if ($(event.data.fake_input).val()!='' && $(event.data.fake_input).val()!=d) {
event.preventDefault();
$(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:true});
} else {
$(event.data.fake_input).val($(event.data.fake_input).attr('default'));
$(event.data.fake_input).css('color','#666666');
}
return false;
});
}
$(data.fake_input).blur();
});
return this;
};
jQuery.fn.tagsInput.updateTagsField = function(obj,tagslist) {
id = $(obj).attr('id');
$(obj).val(tagslist.join(delimiter[id]));
};
jQuery.fn.tagsInput.importTags = function(obj,val) {
$(obj).val('');
id = $(obj).attr('id');
var tags = val.split(delimiter[id]);
for (i=0; i<tags.length; i++) {
$(obj).addTag(tags[i],{focus:false});
}
};
})(jQuery);
How about adding something like this:
if($('.tag').length>=5){
$('#tags_tag').attr('disabled','true');
}
I put a little more flair into my demo.
Probably the easiest solution is to change line 89 of jquery.tagsinput.js from:
var skipTag = $(tagslist).tagExist(value);
to:
var skipTag = $(tagslist).length > 5 || $(tagslist).tagExist(value);

Categories