├── LICENSE
├── README.md
├── ajaxcall.html
├── img
└── loading.gif
├── sampledata.json
├── tAutocomplete.html
├── tautocomplete.css
├── tautocomplete.jquery.json
└── tautocomplete.js
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 vyasrao
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Project not maintained anymore, happy to transfer the owenership!
2 | =============
3 |
4 | tAutocomplete
5 | =============
6 |
7 | Autocomplete Table is a jQuery plugin that provides suggestions from a multi-column table in a dropdown list while the visitors type into the input field.
8 | Complete details and demo is available at http://vyasrao.github.io/tAutocomplete/
9 |
10 | New releases
11 | 1. Delay search result
12 | 2. Highlight search word among result
13 | 3. Hide/ show columns
14 | 4. Display complete details on the selected row.
15 |
--------------------------------------------------------------------------------
/ajaxcall.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | ", { class: settings.theme }),
33 | ddTable: $("
", { style: "width:" + settings.width }),
34 | ddTableCaption: $("
" + settings.norecord + " "),
35 | ddTextbox: $("
")
36 | };
37 |
38 | var keys = {
39 | UP: 38,
40 | DOWN: 40,
41 | ENTER: 13,
42 | TAB: 9,
43 | BACKSPACE: 8
44 | };
45 |
46 | var errors = {
47 | columnNA: "Error: Columns Not Defined",
48 | dataNA: "Error: Data Not Available"
49 | };
50 |
51 | // plugin properties
52 | var tautocomplete = {
53 | id: function () {
54 | return el.ddTextbox.data("id");
55 | },
56 | text: function () {
57 | return el.ddTextbox.data("text");
58 | },
59 | searchdata: function () {
60 | return el.ddTextbox.val();
61 | },
62 | settext: function (text) {
63 | el.ddTextbox.val(text);
64 | },
65 | isNull: function () {
66 | if (el.ddTextbox.data("text") == "" || el.ddTextbox.data("text") == null)
67 | return true;
68 | else
69 | return false;
70 | },
71 | all: function(){
72 | return selectedData;
73 | }
74 | };
75 |
76 | // delay function which listens to the textbox entry
77 | var delay = (function () {
78 | var timer = 0;
79 | return function (callsback, ms) {
80 | clearTimeout(timer);
81 | timer = setTimeout(callsback, ms);
82 | };
83 | })();
84 |
85 | // key/value containing data of the selcted row
86 | var selectedData = {};
87 |
88 | var focused = false;
89 |
90 | // check if the textbox is focused.
91 | if (this.is(':focus')) {
92 | focused = true;
93 | }
94 |
95 | // get number of columns
96 | var cols = settings.columns.length;
97 |
98 | var orginalTextBox = this;
99 |
100 | // wrap the div for style
101 | this.wrap("
");
102 |
103 | // create a textbox for input
104 | this.after(el.ddTextbox);
105 | el.ddTextbox.attr("autocomplete", "off");
106 | el.ddTextbox.css("width", this.width + "px");
107 | el.ddTextbox.css("font-size", this.css("font-size"));
108 | el.ddTextbox.attr("placeholder", settings.placeholder);
109 |
110 | // check for mandatory parameters
111 | if (settings.columns == "" || settings.columns == null) {
112 | el.ddTextbox.attr("placeholder", errors.columnNA);
113 | }
114 | else if ((settings.data == "" || settings.data == null) && settings.ajax == null) {
115 | el.ddTextbox.attr("placeholder", errors.dataNA);
116 | }
117 |
118 | // append div after the textbox
119 | this.after(el.ddDiv);
120 |
121 | // hide the current text box (used for stroing the values)
122 | this.hide();
123 |
124 | // append table after the new textbox
125 | el.ddDiv.append(el.ddTable);
126 | el.ddTable.attr("cellspacing", "0");
127 |
128 | // append table caption
129 | el.ddTable.append(el.ddTableCaption);
130 |
131 | // create table columns
132 | var header = "
";
133 | for (var i = 0; i <= cols - 1; i++) {
134 | header = header + "" + settings.columns[i] + " "
135 | }
136 | header = header + " "
137 | el.ddTable.append(header);
138 |
139 | // assign data fields to the textbox, helpful in case of .net postbacks
140 | {
141 | var id = "", text = "";
142 |
143 | if (this.val() != "") {
144 | var val = this.val().split("#$#");
145 | id = val[0];
146 | text = val[1];
147 | }
148 |
149 | el.ddTextbox.attr("data-id", id);
150 | el.ddTextbox.attr("data-text", text);
151 | el.ddTextbox.val(text);
152 | }
153 |
154 | if (focused) {
155 | el.ddTextbox.focus();
156 | }
157 |
158 | // event handlers
159 |
160 | // autocomplete key press
161 | el.ddTextbox.keyup(function (e) {
162 | //return if up/down/return key
163 | if ((e.keyCode < 46 || e.keyCode > 105) && (e.keyCode != keys.BACKSPACE)) {
164 | e.preventDefault();
165 | return;
166 | }
167 | //delay for 1 second: wait for user to finish typing
168 | delay(function () {
169 | processInput();
170 | }, settings.delay);
171 | });
172 |
173 | // process input
174 | function processInput()
175 | {
176 | if (el.ddTextbox.val() == "") {
177 | hideDropDown();
178 | return;
179 | }
180 |
181 | // hide no record found message
182 | el.ddTableCaption.hide();
183 |
184 | el.ddTextbox.addClass("loading");
185 |
186 | if (settings.ajax != null)
187 | {
188 | var tempData = null;
189 | if ($.isFunction(settings.ajax.data)) {
190 | tempData = settings.ajax.data.call(this);
191 | }
192 | else{
193 | tempData = settings.ajax.data;
194 | }
195 | // get json data
196 | $.ajax({
197 | type: settings.ajax.type || 'GET',
198 | dataType: 'json',
199 | contentType: settings.ajax.contentType || 'application/json; charset=utf-8',
200 | headers: settings.ajax.headers || { 'Content-Type': 'application/x-www-form-urlencoded' },
201 | data: tempData || null,
202 | url: settings.ajax.url,
203 | success: ajaxData,
204 | error: function (xhr, ajaxOptions, thrownError) {
205 | el.ddTextbox.removeClass("loading");
206 | alert('Error: ' + xhr.status || ' - ' || thrownError);
207 | }
208 | });
209 | }
210 | else if ($.isFunction(settings.data)) {
211 | var data = settings.data.call(this);
212 | jsonParser(data);
213 | }
214 | else {
215 | // default function
216 | null;
217 | }
218 | }
219 |
220 | // call on Ajax success
221 | function ajaxData(jsonData)
222 | {
223 | if (settings.ajax.success == null || settings.ajax.success == "" || (typeof settings.ajax.success === "undefined"))
224 | {
225 | jsonParser(jsonData);
226 | }
227 | else {
228 | if ($.isFunction(settings.ajax.success)) {
229 | var data = settings.ajax.success.call(this, jsonData);
230 | jsonParser(data);
231 | }
232 | }
233 | }
234 |
235 | // do not allow special characters
236 | el.ddTextbox.keypress(function (event) {
237 | var regex = new RegExp(settings.regex);
238 | var key = String.fromCharCode(!event.charCode ? event.which : event.charCode);
239 |
240 | if (!regex.test(key)) {
241 | event.preventDefault();
242 | return false;
243 | }
244 | });
245 |
246 | // textbox keypress events (return key, up and down arrow)
247 | el.ddTextbox.keydown(function (e) {
248 |
249 | var tbody = el.ddTable.find("tbody");
250 | var selected = tbody.find(".selected");
251 |
252 | if (e.keyCode == keys.ENTER) {
253 | e.preventDefault();
254 | select();
255 | }
256 | if (e.keyCode == keys.UP) {
257 | el.ddTable.find(".selected").removeClass("selected");
258 | if (selected.prev().length == 0) {
259 | tbody.find("tr:last").addClass("selected");
260 | } else {
261 | selected.prev().addClass("selected");
262 | }
263 | }
264 | if (e.keyCode == keys.DOWN) {
265 | tbody.find(".selected").removeClass("selected");
266 | if (selected.next().length == 0) {
267 | tbody.find("tr:first").addClass("selected");
268 | } else {
269 | el.ddTable.find(".selected").removeClass("selected");
270 | selected.next().addClass("selected");
271 | }
272 | }
273 | });
274 |
275 | // row click event
276 | el.ddTable.delegate("tr", "mousedown", function () {
277 | el.ddTable.find(".selected").removeClass("selected");
278 | $(this).addClass("selected");
279 | select();
280 | });
281 |
282 | // textbox blur event
283 | el.ddTextbox.focusout(function () {
284 | hideDropDown();
285 | // clear if the text value is invalid
286 | if ($(this).val() != $(this).data("text")) {
287 |
288 | var change = true;
289 | if ($(this).data("text") == "") {
290 | change = false;
291 | }
292 |
293 | $(this).data("text", "");
294 | $(this).data("id", "");
295 | $(this).val("");
296 | orginalTextBox.val("");
297 |
298 | if (change) {
299 | onChange();
300 | }
301 | }
302 | });
303 |
304 | function select() {
305 |
306 | var selected = el.ddTable.find("tbody").find(".selected");
307 |
308 | el.ddTextbox.data("id", selected.find('td').eq(0).text());
309 | el.ddTextbox.data("text", selected.find('td').eq(1).text());
310 |
311 | for(var i=0; i < cols; i++)
312 | {
313 | selectedData[settings.columns[i]] = selected.find('td').eq(i).text();
314 | }
315 |
316 | el.ddTextbox.val(selected.find('td').eq(1).text());
317 | orginalTextBox.val(selected.find('td').eq(0).text() + '#$#' + selected.find('td').eq(1).text());
318 | hideDropDown();
319 | onChange();
320 | el.ddTextbox.focus();
321 | }
322 |
323 | function onChange()
324 | {
325 | // onchange callback function
326 | if ($.isFunction(settings.onchange)) {
327 | settings.onchange.call(this);
328 | }
329 | else {
330 | // default function for onchange
331 | }
332 | }
333 |
334 | function hideDropDown() {
335 | el.ddTable.hide();
336 | el.ddTextbox.removeClass("inputfocus");
337 | el.ddDiv.removeClass("highlight");
338 | el.ddTableCaption.hide();
339 | }
340 |
341 | function showDropDown() {
342 |
343 | var cssTop = (el.ddTextbox.height() + 20) + "px 1px 0px 1px";
344 | var cssBottom = "1px 1px " + (el.ddTextbox.height() + 20) + "px 1px";
345 |
346 | // reset div top, left and margin
347 | el.ddDiv.css("top", "0px");
348 | el.ddDiv.css("left", "0px");
349 | el.ddTable.css("margin", cssTop);
350 |
351 | el.ddTextbox.addClass("inputfocus");
352 | el.ddDiv.addClass("highlight");
353 | el.ddTable.show();
354 |
355 | // adjust div top according to the visibility
356 | if (!isDivHeightVisible(el.ddDiv)) {
357 | el.ddDiv.css("top", -1 * (el.ddTable.height()) + "px");
358 | el.ddTable.css("margin", cssBottom);
359 | if (!isDivHeightVisible(el.ddDiv)) {
360 | el.ddDiv.css("top", "0px");
361 | el.ddTable.css("margin", cssTop);
362 | $('html, body').animate({
363 | scrollTop: (el.ddDiv.offset().top - 60)
364 | }, 250);
365 | }
366 | }
367 | // adjust div left according to the visibility
368 | if (!isDivWidthVisible(el.ddDiv)) {
369 | el.ddDiv.css("left", "-" + (el.ddTable.width() - el.ddTextbox.width() - 20) + "px");
370 | }
371 | }
372 | function jsonParser(jsonData) {
373 | try{
374 | el.ddTextbox.removeClass("loading");
375 |
376 | // remove all rows from the table
377 | el.ddTable.find("tbody").find("tr").remove();
378 |
379 | // regular expression for word highlight
380 | var re = null;
381 | if(settings.highlight != null){
382 | var highlight = true;
383 | var re = new RegExp(el.ddTextbox.val(),"gi");
384 | }
385 |
386 | var i = 0, j = 0;
387 | var row = null, cell = null;
388 | if (jsonData != null) {
389 | for (i = 0; i < jsonData.length; i++) {
390 |
391 | // display only 15 rows of data
392 | if (i >= 15)
393 | continue;
394 |
395 | var obj = jsonData[i];
396 | row = "";
397 | j = 0;
398 |
399 | for (var key in obj) {
400 |
401 | // return on column count
402 | if (j <= cols) {
403 | cell = obj[key] + "";
404 |
405 | if(highlight){
406 | cell = cell.replace(re,"
$& ");
407 | }
408 | row = row + "
" + cell + " ";
409 | }
410 | else {
411 | continue;
412 | }
413 | j++;
414 | }
415 | // append row to the table
416 | el.ddTable.append("
" + row + " ");
417 | }
418 | }
419 | // show no records exists
420 | if (i == 0)
421 | el.ddTableCaption.show();
422 |
423 | // hide columns
424 | for(var i=0; (i< settings.hide.length) && (i< cols) ; i++)
425 | {
426 | if(!settings.hide[i])
427 | el.ddTable.find('td:nth-child('+ (i+1) +')').hide();
428 | }
429 |
430 | el.ddTable.find("tbody").find("tr:first").addClass('selected');
431 | showDropDown();
432 | }
433 | catch (e)
434 | {
435 | alert("Error: " + e);
436 | }
437 | }
438 | return tautocomplete;
439 | };
440 | }(jQuery));
441 |
442 | function isDivHeightVisible(elem) {
443 | var docViewTop = $(window).scrollTop();
444 | var docViewBottom = docViewTop + $(window).height();
445 |
446 | var elemTop = $(elem).offset().top;
447 | var elemBottom = elemTop + $(elem).height();
448 |
449 | return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom)
450 | && (elemBottom <= docViewBottom) && (elemTop >= docViewTop));
451 | }
452 |
453 | function isDivWidthVisible(elem) {
454 | var docViewLeft = $(window).scrollLeft();
455 | var docViewRight = docViewLeft + $(window).width();
456 |
457 | var elemLeft = $(elem).offset().left;
458 | var elemRight = elemLeft + $(elem).width();
459 |
460 | return ((elemRight >= docViewLeft) && (elemLeft <= docViewRight)
461 | && (elemRight <= docViewRight) && (elemLeft >= docViewLeft));
462 | }
--------------------------------------------------------------------------------