1/*! 2 3* jQuery FlexBox $Version: 0.9.6 $ 4 5* 6 7* Copyright (c) 2008-2010 Noah Heldman and Fairway Technologies (http://www.fairwaytech.com/flexbox) 8 9* Licensed under Ms-PL (http://www.codeplex.com/flexbox/license) 10 11* 12 13* $Date: 2010-11-24 01:02:00 PM $ 14 15* $Rev: 0.9.6.1 $ 16 17*/ 18 19(function($) { 20 21 $.flexbox = function(div, o) { 22 23 24 25 // TODO: in straight type-ahead mode (showResults: false), if noMatchingResults, dropdown appears after new match 26 27 // TODO: consider having options.mode (select, which replaces html select; combobox; suggest; others?) 28 29 // TODO: on resize (at least when wrapping within a table), the arrow is pushed down to the next line 30 31 // TODO: check for boundary/value problems (such as minChars of -1) and alert them 32 33 // TODO: add options for advanced paging template 34 35 // TODO: general cleanup and refactoring, commenting 36 37 // TODO: detailed Exception handling, logging 38 39 // TODO: FF2, up arrow from bottom has erratic scroll behavior (if multiple flexboxes on page) 40 41 // TODO: FF2 (and maybe IE7): if maxVisibleRows == number of returned rows, height is a bit off (maybe set to auto?) 42 43 // TODO: escape key only works from input box (this might be okay) 44 45 // TODO: make .getJSON parameters (object and callback function) configurable (e.g. when calling yahoo image search) 46 47 // TODO: escape key reverts to previous value (FF only?) (is this a good thing?) 48 49 50 51 // TEST: highlightMatches uses the case of whatever you typed in to replace the match string, which can look funny 52 53 // TEST: handle pageDown and pageUp keys when scrolling through results 54 55 // TEST: allow client-side paging (return all data initially, set paging:{pageSize:#}, and ensure maxCacheBytes is > 0) 56 57 // TEST: accept json object as first parameter to flexbox instead of page source, and have it work like a combobox 58 59 // TEST: implement no results template 60 61 // TEST: implement noResultsText and class 62 63 // TEST: watermark color should be configurable (and so should default input color) 64 65 // TEST: exception handling and alerts for common mistakes 66 67 // TEST: first example should use defaults ONLY 68 69 // TEST: add property initialValue, so you can set it when the flexbox loads 70 71 // TEST: handle hidden input value for form submissions 72 73 // TEST: how can we allow programmatically setting the field value (and therefore hidden value). add jquery function? 74 75 // TEST: use pageSize parameter as threshold to switch from no paging to paging based on results 76 77 // TEST: if you type in an input value that matches the html, it might display html code (try typing "class" in the input box) 78 79 // TEST: don't require all paging subprops (let default override) 80 81 // TEST: when tabbing from one ffb to another, the previous ffb results flash... 82 83 // TEST: IE7: when two non-paging ffbs right after each other, with only a clear-both div between them, the bottom ffb jumps down when selecting a value, then jumps back up on mouseover 84 85 // TEST: FF2, make sure we scroll to top before showing results (maxVisibleRows only) 86 87 // TEST: if maxVisibleRows is hiding the value the user types in to the input, scroll to that value (is this even possible?) 88 89 // TEST: make sure caching supports multiple ffbs uniquely 90 91 // TEST: when entering a number in the paging input box, the results are displayed twice 92 93 94 95 var timeout = false, // hold timeout ID for suggestion results to appear 96 97 cache = [], // simple array with cacheData key values, MRU is the first element 98 99 cacheData = [], // associative array holding actual cached data 100 101 cacheSize = 0, // size of cache in bytes (cache up to o.maxCacheBytes bytes) 102 103 delim = '\u25CA', // use an obscure unicode character (lozenge) as the cache key delimiter 104 105 scrolling = false, 106 107 pageSize = o.paging && o.paging.pageSize ? o.paging.pageSize : 0, 108 109 retrievingRemoteData = false, 110 111 arrowklik = false, 112 113 $div = $(div).css('position', 'relative').css('z-index', 0); 114 115 var last_flexbox_searchword = ""; 116 117 // The hiddenField MUST be appended to the div before the input, or IE7 does not shift the dropdown below the input field (it overlaps) 118 119 var $hdn = $('<input type="hidden"/>') 120 121 .attr('id', $div.attr('id') + '_hidden') 122 123 .attr('name', $div.attr('id')) 124 125 .val(o.initialValue) 126 127 .appendTo($div); 128 129 var $input = $('<input/>') 130 131 .attr('id', $div.attr('id') + '_input') 132 133 .attr('autocomplete', 'off') 134 135 .addClass(o.inputClass) 136 137 .css('width', o.width + 'px') 138 139 .appendTo($div) 140 141 .click(function(e) { 142 143 if (o.watermark !== '' && this.value === o.watermark) 144 145 this.value = ''; 146 147 //else 148 149 //this.select(); 150 151 }) 152 153 .focus(function(e) { 154 155 $(this).removeClass('watermark'); 156 157 }) 158 159 .blur(function(e) { 160 161 if (this.value === '') $hdn.val(''); 162 163 setTimeout(function() { if (!$input.data('active')) hideResults(); }, 200); 164 165 }) 166 167 .keydown(processKeyDown); 168 169 170 171 if (o.initialValue !== '') 172 173 $input.val(o.initialValue).removeClass('watermark'); 174 175 else 176 177 $input.val(o.watermark).addClass('watermark'); 178 179 180 181 var arrowWidth = 0; 182 183 if (o.showArrow && o.showResults) { 184 185 var arrowClick = function() { 186 187 if ($ctr.is(':visible')) { 188 189 hideResults(); 190 191 } 192 193 else { 194 195 $input.focus(); 196 197 if (o.watermark !== '' && $input.val() === o.watermark) 198 199 $input.val(''); 200 201 else { 202 203 // Recept hozzavalo kereses nagyito 204 // ennek hatasara kijeloli kereso szot: 205 // $input.select(); // kereso szo kijelol 206 207 } 208 209 if (timeout) 210 211 clearTimeout(timeout); 212 213 arrowklik = true; 214 timeout = setTimeout(function() { flexbox(1, true, o.arrowQuery); }, o.queryDelay); 215 216 } 217 218 }; 219 220 var $arrow = $('<span></span>') 221 222 .attr('id', $div.attr('id') + '_arrow') 223 224 .addClass(o.arrowClass) 225 226 .addClass('out') 227 228 .hover(function() { 229 230 $(this).removeClass('out').addClass('over'); 231 232 }, function() { 233 234 $(this).removeClass('over').addClass('out'); 235 236 }) 237 238 .mousedown(function() { 239 240 $(this).removeClass('over').addClass('active'); 241 242 }) 243 244 .mouseup(function() { 245 246 $(this).removeClass('active').addClass('over'); 247 248 }) 249 250 .click(arrowClick) 251 252 .appendTo($div); 253 254 arrowWidth = $arrow.width(); 255 256 $input.css('width', (o.width - arrowWidth) + 'px'); 257 258 } 259 260 if (!o.allowInput) { o.selectFirstMatch = false; $input.click(arrowClick); } // simulate <select> behavior 261 262 263 264 // Handle presence of CSS Universal Selector (*) that defines padding by verifying what the browser thinks the outerHeight is. 265 266 // In FF, the outerHeight() will not pick up the correct input field padding 267 268 var inputPad = $input.outerHeight() - $input.height() - 2; 269
270 var inputWidth = $input.outerWidth() - 2; 271 272 var top = $input.outerHeight(); 273 274 275 276 if (inputPad === 0) { 277 278 inputWidth += 4; 279 280 top += 4; 281 282 } 283 284 else if (inputPad !== 4) { 285 286 inputWidth += inputPad; 287 288 top += inputPad; 289 290 } 291 292 293 294 var $ctr = $('<div></div>') 295 296 .attr('id', $div.attr('id') + '_ctr') 297 298 .css('width', inputWidth + arrowWidth) 299 300 .css('top', top) 301 302 .css('left', 0) 303 304 .addClass(o.containerClass) 305 306 .appendTo($div) 307 308 .mousedown(function(e) { 309 310 $input.data('active', true); 311 312 }) 313 314 .hide(); 315 316 317 318 var $content = $('<div></div>') 319 320 .addClass(o.contentClass) 321 322 .appendTo($ctr) 323 324 .scroll(function() { 325 326 scrolling = true; 327 328 }); 329 330 331 332 var $paging = $('<div></div>').appendTo($ctr); 333 334 $div.css('height', $input.outerHeight()); 335 336 337 338 function processKeyDown(e) { 339 340 // handle modifiers 341 342 var mod = 0; 343 344 if (typeof (e.ctrlKey) !== 'undefined') { 345 346 if (e.ctrlKey) mod |= 1; 347 348 if (e.shiftKey) mod |= 2; 349 350 } else { 351 352 if (e.modifiers & Event.CONTROL_MASK) mod |= 1; 353 354 if (e.modifiers & Event.SHIFT_MASK) mod |= 2; 355 356 } 357 358 // if the keyCode is one of the modifiers, bail out (we'll catch it on the next keypress) 359 360 if (/16$|17$/.test(e.keyCode)) return; // 16 = Shift, 17 = Ctrl 361 362 363 364 var tab = e.keyCode === 9, esc = e.keyCode === 27; 365 366 var tabWithModifiers = e.keyCode === 9 && mod > 0; 367 368 var backspace = e.keyCode === 8; // we will end up extending the delay time for backspaces... 369 370 371 372 // tab is a special case, since we want to bubble events... 373 374 if (tab) if (getCurr()) selectCurr(); 375 376 377 378 // handling up/down/escape/right arrow/left arrow requires results to be visible 379 380 // handling enter requires that AND a result to be selected 381 382 if ((/27$|38$|33$|34$/.test(e.keyCode) && $ctr.is(':visible')) || 383 384 (/13$|40$/.test(e.keyCode)) || !o.allowInput) { 385 386 387 388 if (e.preventDefault) e.preventDefault(); 389 390 if (e.stopPropagation) e.stopPropagation(); 391 392 393 394 e.cancelBubble = true; 395 396 e.returnValue = false; 397 398 399 400 switch (e.keyCode) { 401 402 case 38: // up arrow 403 404 prevResult(); 405 break; 406 407 case 40: // down arrow 408 409 if ($ctr.is(':visible')) nextResult(); 410 411 else flexboxDelay(true); 412 break; 413 case 13: // enter 414 //console.log("enter" + e.keyCode); 415/* 416 if (getCurr()) selectCurr(); 417 //else flexboxDelay(true); 418 else { 419 clearTimeout(timeout) 420 flexbox(1, false, ''); 421 } 422 */ 423 424 var vis = $ctr.css("display"); 425 if (last_flexbox_searchword == this.value && 426 vis == "block") { 427 selectCurr(); 428 } 429 else { 430 clearTimeout(timeout) //ez itt volt 431 flexbox(1, false, ''); //ez itt volt 432 } 433 break; 434 435 case 27: // escape 436 437 hideResults(); 438 439 break; 440 441 case 34: // page down 442 443 if (!retrievingRemoteData) { 444 445 if (o.paging) $('#' + $div.attr('id') + 'n').click(); 446 447 else nextPage(); 448 449 } 450 451 break; 452 453 case 33: // page up 454 455 if (!retrievingRemoteData) { 456 457 if (o.paging) $('#' + $div.attr('id') + 'p').click(); 458 459 else prevPage(); 460 461 } 462 463 break; 464 465 default: 466 467 if (!o.allowInput) { return; } 468 469 } 470 471 } else if (!esc && !tab && !tabWithModifiers) { // skip esc and tab key and any modifiers 472 473 //console.log(e.keyCode); 474 //if (e.keyCode === 13} { 475 //flexbox(1, false, ''); 476 //} else { 477 arrowklik = false, 478 flexboxDelay(false, backspace); 479 //} 480 481 } 482 483 } 484 485 486 487 function flexboxDelay(simulateArrowClick, increaseDelay) { 488 489 if (timeout) clearTimeout(timeout); 490
491 //var delay = increaseDelay ? o.queryDelay * 5 : o.queryDelay; 492 var delay = o.queryDelay; 493 494 timeout = setTimeout(function() { flexbox(1, simulateArrowClick, ''); }, delay); 495 496 } 497 498 499 500 function flexbox(p, arrowOrPagingClicked, prevQuery) { 501 502 // arrowklikk = false; 503 // if (arrowOrPagingClicked && prevQuery == '') arrowklikk = true; 504 505 if (arrowOrPagingClicked) prevQuery = ''; 506 507 508 var q = prevQuery && prevQuery.length > 0 ? prevQuery : $.trim($input.val()); 509 510 511 512 if (q.length >= o.minChars || arrowklik) { 513 514 // If we are getting data from the server, set the height of the content box so it doesn't shrink when navigating between pages, due to the $content.html('') below... 515 516 if ($content.outerHeight() > 0) 517 518 $content.css('height', $content.outerHeight()); 519 520 $content.html('').attr('scrollTop', 0); 521 522 523 524 var cached = checkCache(q, p); 525 526 if (cached) { 527 528 $content.css('height', 'auto'); 529 530 displayItems(cached.data, q); 531 532 showPaging(p, cached.t); 533 534 } 535 536 else { 537 var ddate = $('#txtdate').val(); 538 var numRand = Math.floor(Math.random()*101); 539 var params = { fav: arrowklik, gui: numRand, date: ddate, q: q, p: p, s: pageSize, contentType: 'application/json; charset=utf-8' }; 540 541 var callback = function(data, overrideQuery) { 542 543 if (overrideQuery === true) q = overrideQuery; // must compare to boolean because by default, the string value "success" is passed when the jQuery $.getJSON method's callback is called 544 545 var totalResults = parseInt(data[o.totalProperty]); 546 547 548 549 // Handle client-side paging, if any paging configuration options were specified 550 551 if (isNaN(totalResults) && o.paging) { 552 553 if (o.maxCacheBytes <= 0) alert('The "maxCacheBytes" configuration option must be greater\nthan zero when implementing client-side paging.'); 554 555 totalResults = data[o.resultsProperty].length; 556 557 558 559 var pages = totalResults / pageSize; 560 561 if (totalResults % pageSize > 0) pages = parseInt(++pages); 562 563 564 565 for (var i = 1; i <= pages; i++) { 566 567 var pageData = {}; 568 569 pageData[o.totalProperty] = totalResults; 570 571 pageData[o.resultsProperty] = data[o.resultsProperty].splice(0, pageSize); 572 573 if (i === 1) totalSize = displayItems(pageData, q); 574 575 //updateCache(q, i, pageSize, totalResults, pageData, totalSize); 576 577 } 578 579 } 580 581 else { 582 583 var totalSize = displayItems(data, q); 584 585 //updateCache(q, p, pageSize, totalResults, data, totalSize); 586 587 } 588 589 showPaging(p, totalResults); 590 591 $content.css('height', 'auto'); 592 593 retrievingRemoteData = false; 594 595 last_flexbox_searchword = q; 596 if (o.onSearchAfter) { 597 598 o.onSearchAfter.apply(); 599 } 600 601 }; 602 603 if (o.onSearchBefore) { 604 605 o.onSearchBefore.apply(); 606 607 } 608 609 if (typeof (o.source) === 'object') { 610 611 if (o.allowInput) callback(filter(o.source, params)); 612 613 else callback(o.source); 614 615 } 616 617 else { 618 619 retrievingRemoteData = true; 620 621 if (o.method.toUpperCase() == 'POST') $.post(o.source, params, callback, 'json'); 622 623 else $.getJSON(o.source, params, callback); 624 625 } 626 627 628 629 630 } 631 632 } else 633 634 { 635 hideResults(); 636 637 if (o.onDelete) { 638 639 o.onDelete.apply(); 640 641 } 642 } 643 644 } 645 646 647 648 function filter(data, params) { 649 650 var filtered = {}; 651 652 filtered[o.resultsProperty] = []; 653 654 filtered[o.totalProperty] = 0; 655 656 var index = 0; 657 658 659 660 for (var i=0; i < data[o.resultsProperty].length; i++) { 661 662 var indexOfMatch = data[o.resultsProperty][i][o.displayValue].toLowerCase().indexOf(params.q.toLowerCase()); 663 664 if ((o.matchAny && indexOfMatch !== -1) || (!o.matchAny && indexOfMatch === 0)) { 665 666 filtered[o.resultsProperty][index++] = data[o.resultsProperty][i]; 667 668 filtered[o.totalProperty] += 1; 669 670 } 671 672 } 673 674 if (o.paging) { 675 676 var start = (params.p - 1) * params.s; 677 678 var howMany = (start + params.s) > filtered[o.totalProperty] ? filtered[o.totalProperty] - start : params.s; 679 680 filtered[o.resultsProperty] = filtered[o.resultsProperty].splice(start, howMany); 681 682 } 683 684 return filtered; 685 686 } 687 688 689 690 function showPaging(p, totalResults) { 691 692 $paging.html('').removeClass(o.paging.cssClass);
692 // clear out for threshold scenarios 693 694 if (o.showResults && o.paging && totalResults > pageSize) { 695 696 var pages = totalResults / pageSize; 697 698 if (totalResults % pageSize > 0) pages = parseInt(++pages); 699 700 outputPagingLinks(pages, p, totalResults); 701 702 } 703 704 } 705 706 707 708 function handleKeyPress(e, page, totalPages) { 709 710 if (/^13$|^39$|^37$/.test(e.keyCode)) { 711 712 if (e.preventDefault) 713 714 e.preventDefault(); 715 716 if (e.stopPropagation) 717 718 e.stopPropagation(); 719 720 721 722 e.cancelBubble = true; 723 724 e.returnValue = false; 725 726 727 728 switch (e.keyCode) { 729 730 case 13: // Enter 731 732 if (/^\d+$/.test(page) && page > 0 && page <= totalPages) 733 734 flexbox(page, true); 735 736 else 737 738 alert('Please enter a page number between 1 and ' + totalPages); 739 740 // TODO: make this alert a function call, and a customizable parameter 741 742 break; 743 744 case 39: // right arrow 745 746 $('#' + $div.attr('id') + 'n').click(); 747 748 break; 749 750 case 37: // left arrow 751 752 $('#' + $div.attr('id') + 'p').click(); 753 754 break; 755 756 } 757 758 } 759 760 } 761 762 763 764 function handlePagingClick(e) { 765 766 flexbox(parseInt($(this).attr('page')), true, $input.attr('pq')); // pq == previous query 767 768 return false; 769 770 } 771 772 773 774 function outputPagingLinks(totalPages, currentPage, totalResults) { 775 776 // TODO: make these configurable images 777 778 var first = '<<', 779 780 prev = '<', 781 782 next = '>', 783 784 last = '>>', 785 786 more = '...'; 787 788 789 790 $paging.addClass(o.paging.cssClass); 791 792 793 794 // set up our base page link element 795 796 var $link = $('<a/>') 797 798 .attr('href', '#') 799 800 .addClass('page') 801 802 .click(handlePagingClick), 803 804 $span = $('<span></span>').addClass('page'), 805 806 divId = $div.attr('id'); 807 808 809 810 // show first page 811 812 if (currentPage > 1) { 813 814 $link.clone(true).attr('id', divId + 'f').attr('page', 1).html(first).appendTo($paging); 815 816 $link.clone(true).attr('id', divId + 'p').attr('page', currentPage - 1).html(prev).appendTo($paging); 817 818 } 819 820 else { 821 822 $span.clone(true).html(first).appendTo($paging); 823 824 $span.clone(true).html(prev).appendTo($paging); 825 826 } 827 828 829 830 if (o.paging.style === 'links') { 831 832 var maxPageLinks = o.paging.maxPageLinks; 833 834 // show page numbers 835 836 if (totalPages <= maxPageLinks) { 837 838 for (var i = 1; i <= totalPages; i++) { 839 840 if (i === currentPage) { 841 842 $span.clone(true).html(currentPage).appendTo($paging); 843 844 } 845 846 else { 847 848 $link.clone(true).attr('page', i).html(i).appendTo($paging); 849 850 } 851 852 } 853 854 } 855 856 else { 857 858 if ((currentPage + parseInt(maxPageLinks / 2)) > totalPages) { 859 860 startPage = totalPages - maxPageLinks + 1; 861 862 } 863 864 else { 865 866 startPage = currentPage - parseInt(maxPageLinks / 2); 867 868 } 869 870 871 872 if (startPage > 1) { 873 874 $link.clone(true).attr('page', startPage - 1).html(more).appendTo($paging); 875 876 } 877 878 else { 879 880 startPage = 1; 881 882 } 883 884 885 886 for (var i = startPage; i < startPage + maxPageLinks; i++) { 887 888 if (i === currentPage) { 889 890 $span.clone(true).html(i).appendTo($paging); 891 892 } 893 894 else { 895 896 $link.clone(true).attr('page', i).html(i).appendTo($paging); 897 898 } 899 900 } 901 902 903 904 if (totalPages > (startPage + maxPageLinks)) { 905 906 $link.clone(true).attr('page', i).html(more).appendTo($paging); 907 908 } 909 910 } 911 912 } 913 914 else if (o.paging.style === 'input') { 915 916 var $pagingBox = $('<input/>') 917 918 .addClass('box') 919 920 .click(function(e) { 921 922 this.select(); 923 924 }) 925 926 .keypress(function(e) { 927 928 return handleKeyPress(e, this.value, totalPages); 929 930 }) 931 932 .val(currentPage) 933 934 .appendTo($paging); 935 936 } 937 938 939 940 if (currentPage < totalPages) { 941 942 $link.clone(true).attr('id', divId + 'n').attr('page', +currentPage + 1).html(next).appendTo($paging); 943 944 $link.clone(true).attr('id', divId + 'l').attr('page', totalPages).html(last).appendTo($paging); 945 946 } 947 948 else { 949 950 $span.clone(true).html(next).appendTo($paging); 951 952 $span.clone(true).html(last).appendTo($paging); 953 954 } 955 956 var startingResult = (currentPage - 1) * pageSize + 1; 957 958 var endingResult = (startingResult > (totalResults - pageSize)) ? totalResults : startingResult + pageSize - 1; 959 960 961 962 if (o.paging.showSummary) { 963 964 var summaryData = { 965 966 "start": startingResult, 967 968 "end": endingResult, 969 970 "total": totalResults, 971 972 "page": currentPage, 973 974 "pages": totalPages 975 976 }; 977 978 var html = o.paging.summaryTemplate.applyTemplate(summaryData); 979 980 $('<br/>').appendTo($paging); 981 982 $('<span></span>') 983 984 .addClass(o.paging.summaryClass) 985 986 .html(html) 987 988 .appendTo($paging); 989 990 } 991 992 } 993 994 995 996 function checkCache(q, p) { 997 998 var key = q + delim + p; // use null character as delimiter 999 1000 if (cacheData[key]) { 1001 1002 for (var i = 0; i < cache.length; i++) { // TODO: is it possible to not loop here? 1003 1004 if (cache[i] === key) { 1005 1006 // pull out the matching element (splice), and add it to the beginning of the array (unshift) 1007 1008 cache.unshift(cache.splice(i, 1)[0]); 1009 1010 return cacheData[key]; 1011 1012 } 1013 1014 } 1015 1016 } 1017 1018 return false; 1019 1020 } 1021 1022 1023 1024 function updateCache(q, p, s, t, data, size) { 1025 1026 if (o.maxCacheBytes > 0) { 1027 1028 while (cache.length && (cacheSize + size > o.maxCacheBytes)) { 1029
1030 var cached = cache.pop(); 1031 1032 cacheSize -= cached.size; 1033 1034 } 1035 1036 var key = q + delim + p; // use null character as delimiter 1037 1038 cacheData[key] = { 1039 1040 q: q, 1041 1042 p: p, 1043 1044 s: s, 1045 1046 t: t, 1047 1048 size: size, 1049 1050 data: data 1051 1052 }; // add the data to the cache at the hash key location 1053 1054 cache.push(key); // add the key to the MRU list 1055 1056 cacheSize += size; 1057 1058 } 1059 1060 } 1061 1062 1063 1064 function displayItems(d, q) { 1065 1066 var totalSize = 0, itemCount = 0; 1067 1068 1069 1070 if (!d) 1071 1072 return; 1073 1074 1075 1076 $hdn.val($input.val()); 1077 1078 if (parseInt(d[o.totalProperty]) === 0 && o.noResultsText && o.noResultsText.length > 0) { 1079 1080 $content.addClass(o.noResultsClass).html(o.noResultsText); 1081 1082 $('#noResultDialogButton').click(function() { 1083 $('#noResultDialog').dialog('open'); 1084 return false; 1085 });
1086 1087 $('#noResultSportDialogButton').click(function() { 1088 $('#noResultSportDialog').dialog('open'); 1089 return false; 1090 }); 1091 1092 $ctr.show(); 1093 1094 return; 1095 1096 } else $content.removeClass(o.noResultsClass); 1097 1098 1099 1100 for (var i = 0; i < d[o.resultsProperty].length; i++) { 1101 1102 var data = d[o.resultsProperty][i], 1103 1104 result = o.resultTemplate.applyTemplate(data), 1105 1106 exactMatch = q === result, 1107 1108 selectedMatch = false, 1109 1110 hasHtmlTags = false, 1111 1112 match = data[o.displayValue]; 1113 1114 1115 1116 if (!exactMatch && o.highlightMatches && q !== '') { 1117 1118 var pattern = q, 1119 1120 highlightStart = match.toLowerCase().indexOf(q.toLowerCase()), 1121 1122 replaceString = '<span class="' + o.matchClass + '">' + match.substr(highlightStart,q.length) + '</span>'; 1123/* 1124 if (result.match('<(.|\n)*?>')) { // see if the content contains html tags 1125 1126 hasHtmlTags = true; 1127 1128 pattern = '(>)([^<]*?)(' + q + ')((.|\n)*?)(<)'; // TODO: look for a better way 1129 1130 replaceString = '$1$2<span class="' + o.matchClass + '">$3</span>$4$6'; 1131 1132 } 1133 1134 result = result.replace(new RegExp(pattern, o.highlightMatchesRegExModifier), replaceString); 1135*/ 1136 } 1137 1138 1139 1140 // write the value of the first match to the input box, and select the remainder, 1141 1142 // but only if autoCompleteFirstMatch is set, and there are no html tags in the response 1143 1144 if (o.autoCompleteFirstMatch && !hasHtmlTags && i === 0) { 1145 1146 if (q.length > 0 && match.toLowerCase().indexOf(q.toLowerCase()) === 0) { 1147 1148 $input.attr('pq', q); // pq == previous query 1149 1150 $hdn.val(data[o.hiddenValue]); 1151 1152 $input.val(data[o.displayValue]); 1153 1154 selectedMatch = selectRange(q.length, $input.val().length); 1155 1156 } 1157 1158 } 1159 1160 1161 1162 if (!o.showResults) return; 1163 1164 1165 1166 $row = $('<div></div>') 1167 1168 .attr('id', data[o.hiddenValue]) 1169 1170 .attr('val', data[o.displayValue]) 1171 1172 .addClass('row') 1173 1174 .html(result) 1175 1176 .appendTo($content); 1177 1178 1179 1180 if (exactMatch || (++itemCount == 1 && o.selectFirstMatch) || selectedMatch) { 1181 1182 $row.addClass(o.selectClass); 1183 1184 } 1185 1186 totalSize += result.length; 1187 1188 } 1189 1190 1191 1192 if (totalSize === 0) { 1193 1194 hideResults(); 1195 1196 return; 1197 1198 } 1199 1200 1201 if (!isMobilebrowser) $(".tipTip").tipTip(); 1202 1203 1204 1205 $ctr.parent().css('z-index', 11000); 1206 1207 $ctr.show(); 1208 1209 1210 1211 $content 1212 1213 .children('div') 1214 1215 .mouseover(function() { 1216 1217 $content.children('div').removeClass(o.selectClass); 1218 1219 $(this).addClass(o.selectClass); 1220 1221 }) 1222 1223 .mouseup(function(e) { 1224 1225 if(e.target.id && 1226 (e.target.id == 'delownfood' 1227 || e.target.id == 'delfavfood' 1228 || e.target.id == 'delownsport' 1229 || e.target.id == 'delfavsport') ) 1230 { 1231 $curr = getCurr(); 1232 1233 if ($curr) { 1234 1235 $.ajax({
1236 url: $url + '/food.php', 1237 dataType: 'text', 1238 data: { 1239 show: e.target.id, 1240 id: $curr.attr('id') 1241 }, 1242 success: function(data) { 1243 flexbox(1, true, ''); 1244 } 1245 }); 1246 1247 } 1248 return false; 1249 } 1250 1251 if(e.target.id == 'delunvoted') 1252 { 1253 $curr = getCurr(); 1254 1255 if ($curr) { 1256 1257 if(confirm("Biztos törli a kiválasztott tételt?")) 1258 { 1259 $.ajax({ 1260 url: $url + '/food.php', 1261 dataType: 'text', 1262 data: { 1263 show: e.target.id, 1264 id: $curr.attr('id') 1265 }, 1266 success: function(data) { 1267 flexbox(1, true, ''); 1268 } 1269 }); 1270 } 1271 } 1272 return false; 1273 } 1274 1275 if(e.target.id == 'kuktalink') 1276 { 1277 $curr = getCurr(); 1278 1279 if ($curr) { 1280 kukta($curr.attr('id')); 1281 } 1282 return false; 1283 } 1284 1285 1286 if(e.target.id == 'notselected') 1287 { 1288 return false; 1289 } 1290 1291 1292 if(e.target.id == 'etellink') 1293 { 1294 $curr = getCurr(); 1295 1296 if ($curr) { 1297 gokaja2($curr.attr('id')); 1298 } 1299 return false; 1300 } 1301 1302 if(e.target.id == 'noResultDialogButton') 1303 { 1304 1305 //console.log("noRes"); 1306 1307 $curr = getCurr(); 1308 1309 if ($curr) { 1310 noResult($curr.attr('id')); 1311 } 1312 return false; 1313 } 1314 1315 e.preventDefault(); 1316 1317 e.stopPropagation(); 1318 1319 selectCurr(); 1320 1321 }); 1322 1323 1324 1325 if (o.maxVisibleRows > 0) { 1326 1327 var maxHeight = $row.outerHeight() * o.maxVisibleRows; 1328 1329 $content.css('max-height', maxHeight); 1330 1331 } 1332 1333 1334 1335 return totalSize; 1336 1337 } 1338 1339 1340 1341 function selectRange(s, l) { 1342 1343 var tb = $input[0]; 1344 1345 if (tb.createTextRange) { 1346 1347 var r = tb.createTextRange(); 1348 1349 r.moveStart('character', s); 1350 1351 r.moveEnd('character', l - tb.value.length); 1352 1353 r.select(); 1354 1355 } else if (tb.setSelectionRange) { 1356 1357 tb.setSelectionRange(s, l); 1358 1359 } 1360 1361 tb.focus(); 1362 1363 return true; 1364 1365 } 1366 1367 1368 1369 String.prototype.applyTemplate = function(d) { 1370 1371 try { 1372 1373 if (d === '') return this; 1374 1375 return this.replace(/{([^{}]*)}/g, 1376 1377 function(a, b) { 1378 1379 var r; 1380 1381 if (b.indexOf('.') !== -1) { // handle dot notation in {}, such as {Thumbnail.Url} 1382 1383 var ary = b.split('.'); 1384 1385 var obj = d; 1386 1387 for (var i = 0; i < ary.length; i++) 1388 1389 obj = obj[ary[i]]; 1390 1391 r = obj; 1392 1393 } 1394 1395 else 1396 1397 r = d[b]; 1398 1399 if (typeof r === 'string' || typeof r === 'number') return r; else throw (a); 1400 1401 } 1402 1403 ); 1404 1405 } catch (ex) { 1406 1407 alert('Invalid JSON property ' + ex + ' found when trying to apply resultTemplate or paging.summaryTemplate.\nPlease check your spelling and try again.'); 1408 1409 } 1410 1411 }; 1412 1413 1414 1415 function hideResults() { 1416 1417 $input.data('active', false); // for input blur 1418 1419 $div.css('z-index', 0); 1420 1421 $ctr.hide(); 1422 1423 } 1424 1425 1426 1427 function getCurr() { 1428 1429 if (!$ctr.is(':visible')) 1430 1431 return false; 1432 1433 1434 1435 var $curr = $content.children('div.' + o.selectClass); 1436 1437 1438 1439 if (!$curr.length) 1440 1441 $curr = false; 1442 1443 1444 1445 return $curr; 1446 1447 } 1448 1449 1450 1451 function selectCurr() { 1452 1453 $curr = getCurr(); 1454 1455 1456 1457 if ($curr) { 1458 1459 $hdn.val($curr.attr('id')); 1460 1461 var $tmpval = $curr.attr('val'); 1462 1463 $tmpval = $tmpval.replace("</b>", ""); 1464 $tmpval = $tmpval.replace("<b>", ""); 1465 1466 $input.val($tmpval).focus(); 1467 1468 hideResults(); 1469 1470 1471 1472 if (o.onSelect) { 1473 1474 o.onSelect.apply($input[0]); 1475 1476 } 1477 1478 } 1479 1480 } 1481 1482 1483 1484 function supportsGetBoxObjectFor() { 1485 1486 try { 1487 1488 document.getBoxObjectFor(document.body); 1489 1490 return true; 1491 1492 } 1493 1494 catch (e) { 1495 1496 return false; 1497 1498 } 1499 1500 } 1501 1502 1503 1504 function supportsGetBoundingClientRect() { 1505 1506 try { 1507 1508 document.body.getBoundingClientRect(); 1509 1510 return true; 1511 1512 } 1513 1514 catch (e) { 1515 1516 return false; 1517 1518 } 1519 1520 } 1521 1522 1523 1524 function nextPage() { 1525 1526 $curr = getCurr(); 1527 1528 1529 1530 if ($curr && $curr.next().length > 0) { 1531 1532 $curr.removeClass(o.selectClass); 1533 1534 1535 1536 for (var i = 0; i < o.maxVisibleRows; i++) { 1537 1538 if ($curr.next().length > 0) { 1539 1540 $curr = $curr.next(); 1541 1542 } 1543 1544 } 1545 1546 1547 1548 $curr.addClass(o.selectClass); 1549 1550 var scrollPos = $content.attr('scrollTop'); 1551 1552 $content.attr('scrollTop', scrollPos + $content.height()); 1553 1554 } 1555 1556 else if (!$curr) 1557 1558 $content.children('div:first-child').addClass(o.selectClass); 1559 1560 } 1561 1562 1563 1564 function prevPage() { 1565 1566 $curr = getCurr(); 1567 1568 1569 1570 if ($curr && $curr.prev().length > 0) { 1571 1572 $curr.removeClass(o.selectClass); 1573 1574 1575 1576 for (var i = 0; i < o.maxVisibleRows; i++) { 1577 1578 if ($curr.prev().length > 0) { 1579 1580 $curr = $curr.prev(); 1581 1582 } 1583 1584 } 1585 1586 1587 1588 $curr.addClass(o.selectClass); 1589 1590 var scrollPos = $content.attr('scrollTop'); 1591 1592 $content.attr('scrollTop', scrollPos - $content.height()); 1593 1594 } 1595 1596 else if (!$curr) 1597 1598 $content.children('div:last-child').addClass(o.selectClass); 1599 1600 } 1601 1602 1603 1604 function nextResult() { 1605 1606 $curr = getCurr(); 1607 1608 1609 1610 if ($curr && $curr.next().length > 0) { 1611 1612 $curr.removeClass(o.selectClass).next().addClass(o.selectClass); 1613 1614 var scrollPos = $content.attr('scrollTop'), 1615 1616 curr = $curr[0], parentBottom, bottom, height; 1617 1618 if (supportsGetBoxObjectFor()) { 1619 1620 parentBottom = document.getBoxObjectFor($content[0]).y + $content.attr('offsetHeight'); 1621 1622 bottom = document.getBoxObjectFor(curr).y + $curr.attr('offsetHeight'); 1623 1624 height = document.getBoxObjectFor(curr).height; 1625 1626 } 1627 1628 else if (supportsGetBoundingClientRect()) { 1629 1630 parentBottom = $content[0].getBoundingClientRect().bottom; 1631 1632 var rect = curr.getBoundingClientRect(); 1633 1634 bottom = rect.bottom; 1635 1636 height = bottom - rect.top; 1637 1638 } 1639 1640 if (bottom >= parentBottom) 1641 1642 $content.attr('scrollTop', scrollPos + height); 1643 1644 } 1645 1646 else if (!$curr) 1647 1648 $content.children('div:first-child').addClass(o.selectClass); 1649 1650 } 1651 1652 1653 1654 function prevResult() { 1655 1656 $curr = getCurr(); 1657 1658 1659 1660 if ($curr && $curr.prev().length > 0) { 1661 1662 $curr.removeClass(o.selectClass).prev().addClass(o.selectClass); 1663 1664 var scrollPos = $content.attr('scrollTop'), 1665 1666 curr = $curr[0], 1667 1668 parent = $curr.parent()[0], 1669 1670 parentTop, top, height; 1671 1672 if (supportsGetBoxObjectFor()) { 1673 1674 height = document.getBoxObjectFor(curr).height; 1675 1676 parentTop = document.getBoxObjectFor($content[0]).y - (height * 2); // TODO: this is not working when i add another control... 1677 1678 top = document.getBoxObjectFor(curr).y - document.getBoxObjectFor($content[0]).y; 1679 1680 } 1681 1682 else if (supportsGetBoundingClientRect()) { 1683 1684 parentTop = parent.getBoundingClientRect().top; 1685 1686 var rect = curr.getBoundingClientRect(); 1687 1688 top = rect.top; 1689 1690 height = rect.bottom - top; 1691 1692 } 1693 1694 if (top <= parentTop) 1695 1696 $content.attr('scrollTop', scrollPos - height); 1697 1698 } 1699 1700 else if (!$curr) 1701 1702 $content.children('div:last-child').addClass(o.selectClass); 1703 1704 } 1705 1706 }; 1707 1708 1709 1710 $.fn.flexbox = function(source, options) { 1711 1712 if (!source) 1713 1714 return; 1715 1716 1717 1718 try { 1719 1720 var defaults = $.fn.flexbox.defaults; 1721 1722 var o = $.extend({}, defaults, options); 1723 1724 1725 1726 for (var prop in o) { 1727 1728 if (defaults[prop] === undefined) throw ('Invalid option specified: ' + prop + '\nPlease check your spelling and try again.'); 1729 1730 } 1731 1732 o.source = source; 1733 1734 1735 1736 if (options) { 1737 1738 o.paging = (options.paging || options.paging == null) ? $.extend({}, defaults.paging, options.paging) : false; 1739 1740 1741 1742 for (var prop in o.paging) { 1743 1744 if (defaults.paging[prop] === undefined) throw ('Invalid option specified: ' + prop + '\nPlease check your spelling and try again.'); 1745 1746 } 1747 1748 1749 1750 if (options.displayValue && !options.hiddenValue) { 1751 1752 o.hiddenValue = options.displayValue; 1753 1754 } 1755 1756 } 1757 1758 1759 1760 this.each(function() { 1761 1762 new $.flexbox(this, o); 1763 1764 }); 1765 1766 1767 1768 return this; 1769 1770 } catch (ex) { 1771 1772 if (typeof ex === 'object') alert(ex.message); else alert(ex); 1773 1774 } 1775 1776 }; 1777 1778 1779 1780 // plugin defaults - added as a property on our plugin function so they can be set independently 1781 1782 $.fn.flexbox.defaults = { 1783 1784 method: 'GET', // One of 'GET' or 'POST' 1785 1786 queryDelay: 500, // num of milliseconds before query is run. 1787 1788 allowInput: true, // set to false to disallow the user from typing in queries 1789 1790 containerClass: 'ffb', 1791 1792 contentClass: 'content', 1793 1794 selectClass: 'ffb-sel', 1795 1796 inputClass: 'ffb-input', 1797 1798 arrowClass: 'ffb-arrow', 1799 1800 matchClass: 'ffb-match', 1801 1802 noResultsText: 'No matching results', // text to show when no results match the query 1803 1804 noResultsClass: 'ffb-no-results', // class to apply to noResultsText 1805 1806 showResults: true, // whether to show results at all, or just typeahead 1807 1808 selectFirstMatch: true, // whether to highlight the first matching value 1809 1810 autoCompleteFirstMatch: false, // whether to complete the first matching value in the input box 1811 1812 highlightMatches: true, // whether all matches within the string should be highlighted
1812with matchClass 1813 1814 highlightMatchesRegExModifier: 'i', // 'i' for case-insensitive, 'g' for global (all occurrences), or combine 1815 1816 matchAny: true, // for client-side filtering ONLY, match any occurrence of the search term in the result (e.g. "ar" would find "area" and "cart") 1817 1818 minChars: 1, // the minimum number of characters the user must enter before a search is executed 1819 1820 showArrow: true, // set to false to simulate google suggest 1821 1822 arrowQuery: '', // the query to run when the arrow is clicked 1823 1824 onSelect: false, // function to run when a result is selected 1825 1826 onDelete: false, 1827 1828 onSearchBefore: false, 1829 1830 onSearchAfter: false, 1831 1832 maxCacheBytes: 32768, // in bytes, 0 means caching is disabled 1833 1834 resultTemplate: '{name}', // html template for each row (put json properties in curly braces) 1835 1836 displayValue: 'name', // json element whose value is displayed on select 1837 1838 hiddenValue: 'id', // json element whose value is submitted when form is submitted 1839 1840 initialValue: '', // what should the value of the input field be when the form is loaded? 1841 1842 watermark: '', // text that appears when flexbox is loaded, if no initialValue is specified. style with css class '.ffb-input.watermark' 1843 1844 width: 200, // total width of flexbox. auto-adjusts based on showArrow value 1845 1846 resultsProperty: 'results', // json property in response that references array of results 1847 1848 totalProperty: 'total', // json property in response that references the total results (for paging) 1849 1850 maxVisibleRows: 0, // default is 0, which means it is ignored. use either this, or paging.pageSize 1851 1852 paging: { 1853 1854 style: 'input', // or 'links' 1855 1856 cssClass: 'paging', // prefix with containerClass (e.g. .ffb .paging) 1857 1858 pageSize: 10, // acts as a threshold. if <= pageSize results, paging doesn't appear 1859 1860 maxPageLinks: 5, // used only if style is 'links' 1861 1862 showSummary: true, // whether to show 'displaying 1-10 of 200 results' text 1863 1864 summaryClass: 'summary', // class for 'displaying 1-10 of 200 results', prefix with containerClass 1865 1866 summaryTemplate: 'Displaying {start}-{end} of {total} results' // can use {page} and {pages} as well 1867 1868 } 1869 1870 }; 1871 1872 1873 1874 $.fn.setValue = function(val) { 1875 1876 var id = '#' + this.attr('id'); 1877 1878 $(id + '_hidden,' + id + '_input').val(val).removeClass('watermark'); 1879 1880 }; 1881 1882})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.