1/* 2 * vac - Vioma AutoCompleter 3 * 4 */ 5 6if ( typeof vac == 'undefined' ) { 7 var vac = { 8 version: '0.1.3', 9 ready: false, 10 insts: [], 11 inst: {}, 12 item: {}, 13 countries: null, 14 events: 0, 15 eventIgnored: '', 16 timer: false, 17 interval: false, 18 ios: navigator.userAgent.match(/like Mac OS X/i) == 'like Mac OS X', 19 log: false 20 }; 21 (function(jQuery) { 22 if ( 'console' in window ) { 23 var log = false; 24 25 if ( 'log' in window.console ) { 26 log = function( name, args ){ 27 console.log( name, args ) 28 }; 29 //log( 'Vioma AutoCompleter ' + vac.version ); 30 } 31 32 if ( vac.log ) { 33 vac.log = log; 34 } 35 } 36 37 // enter triggert teilweise ein click event, dieses hat jedoch (u.a.) keine screen(Y|X) angaben, woran wir es erkennen und ignorieren koennen 38 vac.isFakeClick = function( event ){ 39 return event.type == 'click' && !event.screenX && !event.screenY; 40 }; 41 42 // chrome's autocomplete triggers focus, keydown, keyup. we need to handle those a bit different to make autocomplete work for countries 43 vac.isFakeEvent = function( event ){ 44 if (event.type === 'keyup' || event.type === 'keydown') { 45 return event.view === undefined && event.keyCode === undefined; 46 } 47 48 return false; 49 }; 50 51 vac.init = function(){ 52 if ( this.log ) this.log( 'vac.init()', arguments ); 53 54 this.container = jQuery( document.createElement( 'div' ) ) 55 .attr( 'class', 'vac' ) 56 .bind( 'click', function( event ){ 57 if ( vac.isFakeClick( event ) ) { 58 return; 59 } 60 61 var element = jQuery( event.target ); 62 63 // vac.inst.element.is( element ) gibt manchmal false zurueck, obwohl es das selbe element ist 64 // von daher comparen wir einfach die im vac-attribute hinterlegte instanz-id.. 65 if ( vac.inst.element.attr( 'vac' ) == element.attr( 'vac' ) ) { 66 return; 67 } 68 69 // vac.container.find( element ) gibt teilweise false zurueck, von daher pruefen wir einfach die class der parent node 70 if ( !element.parent().hasClass( 'vac' ) ) { 71 vac.close(); 72 event.preventDefault() 73 } 74 }); 75 76 jQuery( 'body' ) 77 .bind( 'click', function () { 78 vac.close(); 79 }) 80 .append( this.container ) 81 82 jQuery( window ).bind( 'resize.vac', this.containerMove.bind( this ) ); 83 }; 84 85 vac.defaults = function(){ 86 return { 87 className: '', 88 fieldName: '', 89 fieldSelector: '', 90 callbacks: {}, 91 width: 250, 92 height: 300, 93 openInstant: true, 94 dataOrder: true, 95 dataValue: 'value', 96 dataSource: 'data', 97 resultsMax: 30, 98 resultsMore: true, 99 resultsMorePattern: 'Weitere $count Ãbereinstimmungen anzeigen.', 100 resultsOrder: true, 101 resultsEmptyMsg: true, 102 resultPosition: false, 103 resultPositionPattern: '$pos.', 104 searchMode: 'contains', 105 allowNonSelect: false, 106 allowNonMatch: false,
107 strings: { 108 searchFailed: 'Keine Ãbereinstimmungen gefunden' 109 } 110 } 111 }; 112 113 vac.instsPreInit = function( instId ){ 114 if ( this.log ) this.log( 'vac.instsPreInit()', arguments ); 115 116 for( var i in vac.insts ) { 117 if ( !vac.insts.hasOwnProperty( i ) ) { 118 continue; 119 } 120 121 var inst = vac.insts[i]; 122 123 if ( inst.preinited ) { 124 continue; 125 } 126 127 inst.preinited = true; 128 var options = inst.options; 129 130 if ( options.dataSource != 'data' ) { 131 vac.instReady( inst.id ); 132 continue; 133 } 134 135 if ( !options.url ) { 136 vac.instReady( inst.id ); 137 } 138 else { 139 if ( vac.countries != null && options.url.indexOf( 'json.php?get=countries' ) != -1 ) { 140 inst.options.data = vac.countries; 141 vac.instReady( inst.id ); 142 } 143 else { 144 jQuery.ajax({ 145 url: options.url, 146 dataType: 'jsonp', 147 success: function( result ){ 148 this.options.data = result; 149 150 if ( options.url.indexOf( 'json.php?get=countries' ) != -1 ) { 151 vac.countries = result; 152 } 153 154 vac.instReady( this.id ); 155 }.bind(inst), 156 error: function(){ 157 clearInterval( this.interval ) 158 }.bind( inst ) 159 }); 160 } 161 } 162 } 163 }; 164 165 vac.instInit = function(){ 166 if ( this.log ) this.log( 'vac.instInit()', arguments ); 167 168 var inst = this.inst, 169 options = inst.options, 170 form = inst.element.parents( 'form' ); 171 172 if ( !form.length ) { 173 form = inst.element.parent(); 174 } 175 176 inst.inited = true; 177 178 if (!inst.element.attr( 'autocomplete' )) { 179 inst.element.attr( 'autocomplete', 'off' ); 180 } 181 182 if ( this.ios ) { 183 inst.element 184 .attr( 'autocorrect', 'off' ) 185 .attr( 'autocapitalize', 'off' ); 186 } 187 188 // fieldSelector: das dort angegebene node wird ohne aenderungen als target genommen 189 if ( options.fieldSelector.length ) { 190 inst.target = jQuery( options.fieldSelector ); 191 } 192 // das node mit diesem namen, innerhalb der selben form, wird als target genommen 193 else if ( options.fieldName.length ) { 194 form.find('input,select,textarea').each(function(){ 195 var element = jQuery( this ); 196 197 if ( element.attr( 'name' ) == options.fieldName ) { 198 inst.target = element; 199 } 200 }); 201 if ( typeof inst.target == 'undefined' ) { 202 inst.target = jQuery( document.createElement( 'input' ) ) 203 .attr( 'type', 'hidden' ) 204 .attr( 'name', options.fieldName ) 205 .appendTo( form ); 206 } 207 } 208 // wird nichts angegeben, wird ein hidden-field erstellt und diesem der name des fields gegeben, welchem der vac gebinded ist 209 else { 210 inst.target = jQuery( document.createElement( 'input' ) ) 211 .attr( 'type', 'hidden' ) 212 .attr( 'name', inst.element.attr( 'name' ) ) 213 .appendTo( form ); 214 215 inst.element.removeAttr( 'name' ); 216 } 217 218 form.bind( 'submit', function(){ 219 if ( vac.container.is( ':visible' ) ) { 220 return false; 221 } 222 223 if ( !inst.itemSelected ) { 224 vac.itemSelect(); 225 } 226 }); 227 228 return true; 229 }; 230 231 vac.instReady = function( instId ) { 232 if ( this.log ) this.log( 'vac.instReady()', arguments ); 233 234 var inst = this.inst = this.insts[ instId ]; 235 236 this.order( 'priority' ); 237 inst.ready = true; 238 }; 239 240 vac.instCallback = function( name, args ){ 241 //if ( this.log ) this.log( 'vac.instCallback()', arguments ); 242 243 var options = this.inst.options; 244 245 if ( name in options.callbacks ) { 246 return options.callbacks[ name ].call( this, args ); 247 } 248 }; 249 250 vac.instChange = function( instId ){ 251 if ( this.log ) this.log( 'vac.instChange()', arguments ); 252 253 var inst = this.inst; 254 255 if ( inst && inst.id != instId ) { 256 this.close(); 257 this.eventIgnored = false; 258 this.inst = inst = this.insts[ instId ]; 259 } 260 261 if ( !inst.inited ) { 262 this.instInit(); 263 } 264 }; 265 266 vac.containerMove = function(){ 267 if ( this.log ) this.log( 'vac.containerMove()', arguments ); 268 269 var inst = this.inst, 270 options = inst.options, 271 element = inst.element, 272 offset = element.offset(), 273 scrollOffset = { 274 scrollX: window.scrollX || window.pageXOffset, 275 scrollY: window.scrollY || window.pageYOffset 276 }, 277 dimensions = { 278 height: element.outerHeight(), 279 width: element.outerWidth() 280 }, 281 position = { 282 left: offset.left + dimensions.width, 283 top: offset.top + dimensions.height - (document.body.getBoundingClientRect().top + scrollOffset.scrollY) 284 }, 285 cssProps = { 286 top: position['top'], 287 left: offset['left'], 288 width: options.width, 289 maxHeight: options.height 290 }; 291 292 this.container 293 .attr( 'class', 'vac ' + options.className ) 294 .css( cssProps ); 295 }; 296 297 vac.instReset = function(){ 298 if ( this.log ) this.log( 'vac.instReset()', arguments ); 299 300 var inst = this.inst; 301 inst.itemSelected = -1; 302 inst.element.data( 'result', '' ); 303 304 if ( 'target' in inst ) { 305 inst.target.val( '' ); 306 } 307 }; 308 309 vac.register = function( element, options ){ 310 if ( this.log ) this.log( 'vac.register()', arguments ); 311 312 var instId = this.insts.length; 313 314 element = jQuery( element ) 315 .attr( 'vac', vac.insts.length ) 316 .unbind( 'click focus keyup keydown' ) 317 .bind( 'click focus keyup keydown', function( event ){ 318 var instId = jQuery( this ).attr( 'vac' ); 319 320 if ( !vac.event( instId, event ) ) { 321 // event.stopPropagation(); 322 // return false;
323 } 324 }); 325 326 options = jQuery.extend( {}, this.defaults(), options ); 327 328 vac.insts.push({ 329 id: instId, 330 ready: false, 331 inited: false, 332 preinited: false, 333 itemIndex: -1, 334 itemIndexLast: -1, 335 string: '', 336 stringLast: '', 337 data: {}, 338 data_cached: [], 339 element: element, 340 options: options, 341 result: [] 342 }); 343 344 vac.timer = setTimeout(function(){ 345 vac.instsPreInit(); 346 347 // in case that the input field was focused before vac was listening we open ourself automatically 348 if (element.is(':focus')) { 349 element.trigger( 'click' ); 350 } 351 }, 200 ); 352 }; 353 354 vac.event = function( instId, event ){ 355 if ( this.log ) this.log( 'vac.event()', arguments ); 356 357 if ( !this.container ) { 358 this.init(); 359 } 360 361 if ( this.instChange( instId ) ) { 362 this.eventIgnore( this.eventIgnored ) 363 } 364 else if ( this.eventIgnore( event.type ) ) { 365 return false; 366 } 367 368 var inst = this.inst; 369 var eventAccept = true; 370 371 if ( !inst.inited ) { 372 this.instInit(); 373 } 374 375 // the user can only trigger specific events (KeyboardEvent, FocusEvent, ...), a pure "Event" is only triggert bei autocomplete or javascript (hopefully.) 376 if ( this.isFakeEvent( event) ) { 377 if ( inst.options.data && inst.options.data.length ) { 378 let elementValue = ( inst.element.val() || '' ).toLowerCase(); 379 380 for( var i in inst.options.data ) { 381 var dataItem = inst.options.data[i]; 382 383 if ( dataItem.alias.toLowerCase() == elementValue ) { 384 this.instCallback( 'onSelectBefore' ); 385 this.itemSelectByIndex(i); 386 387 setTimeout(function() { 388 this.instCallback( 'onSelectAfter' ); 389 }.bind(this), 500); // chrome does also autofill input fields if there getting replaced within ~400ms 390 break; 391 } 392 } 393 } 394 } 395 396 // ignored events 397 if ( 'keyCode' in event ) { 398 if ( event.keyCode == 38 || event.keyCode == 40 ) { 399 /* */ 400 } 401 else if ( ( event.ctrlKey || event.altkey || event.metaKey ) && ( event.keyCode == 0 || ( event.keyCode >= 65 && event.keyCode <= 90 ) || ( event.keyCode >= 48 && event.keyCode <= 57 ) ) ) { 402 eventAccept = false; 403 } 404 } 405 406 if ( eventAccept ) { 407 inst.event = event; 408 event.stopPropagation(); 409 return this.eventAccept(); 410 } 411 }; 412 413 vac.eventAccept = function(){ 414 if ( this.log ) this.log( 'vac.eventAccept()', arguments ); 415 416 if ( this.inst.ready ) { 417 return this.eventProcess(); 418 } 419 else { 420 var eventAcceptCounter = 0; 421 422 vac.timer = setTimeout(function(){ 423 if ( eventAcceptCounter++ == 50 ) { 424 clearTimeout( vac.timer ); 425 } 426 427 vac.eventAccept(); 428 }, 100 ); 429 } 430 }; 431 432 vac.eventProcess = function(){ 433 if ( this.log ) this.log( 'vac.eventProcess()', arguments ); 434 435 var inst = this.inst, 436 event = inst.event, 437 element = inst.element, 438 returnValue = true; 439 440 this.instCallback( 'onEventBefore' ); 441 442 // ignore this damn pseudo-events..are they fired from jquery? 443 if ( !( 'type' in event ) ) { 444 event.type = undefined; 445 } 446 447 switch( event.type ) { 448 case 'focus': 449 this.resultsReload(); 450 this.containerMove(); 451 452 if ( inst.itemSelected ) { 453 break; 454 } 455 456 case ( this.ios ? 'touchend' : 'click' ): 457 if ( inst.options.openInstant ) { 458 this.resultsShow(); 459 } 460 break; 461 462 case 'keydown': 463 switch( event.keyCode ) { 464 case 9:// tab 465 if ( !( event.ctrlKey || event.altkey || event.metaKey ) ) { 466 this.itemSelect(); 467 this.close(); 468 } 469 break; 470 471 case 13:// enter 472 returnValue = !vac.container.is( ':visible' ) || inst.options.allowNonMatch; 473 break; 474 475 case 38:// arrow up 476 this.itemPrev(); 477 break; 478 479 case 40:// arrow done 480 this.itemNext(); 481 break; 482 } 483 break; 484 485 case 'keyup': 486 switch( event.keyCode ) { 487 case 38:// arrow up 488 case 40:// arrow done 489 break; 490 491 case 13:// enter 492 returnValue = !vac.container.is( ':visible' ); 493 494 this.itemSelect(); 495 break; 496 497 default: 498 var value = element.val(), 499 search = element.data( 'search' ), 500 result = element.data( 'result' ); 501 502 if ( !value || !search || value != search && search != result ) { 503 element 504 .data( 'result', '' ) 505 .data( 'search', value ); 506 507 clearTimeout( this.searchTimer ); 508 this.searchTimer = setTimeout( 'vac.search()', 150 ); 509 } 510 else if ( event.keyCode == 8 || !( event.ctrlKey || event.altkey || event.metaKey ) && ( event.keyCode == 0 || ( event.keyCode >= 65 && event.keyCode <= 90 ) || ( event.keyCode >= 48 && event.keyCode <= 57 ) ) ) { 511 this.resultsShow(); 512 } 513 break; 514 } 515 break; 516
517 case 'submit': 518 returnValue = !vac.container.is( ':visible' ); 519 this.itemSelect(); 520 break; 521 } 522 523 this.instCallback( 'onEventAfter' ); 524 525 return returnValue; 526 }; 527 528 // da wir nach dem seletieren eines items den focus wieder in das input element setzen, 529 // triggern wir gleichzeitig ein focus event - dieses muessen wir ignorieren, da es keine user eingabe ist 530 vac.eventIgnore = function( eventType ){ 531 if ( this.log ) this.log( 'vac.eventIgnore()', arguments ); 532 533 if ( this.eventIgnored == eventType ) { 534 this.eventIgnored = ''; 535 return true; 536 } 537 538 return false; 539 }; 540 541 vac.eventIgnoreNext = function( eventType ){ 542 if ( this.log ) this.log( 'vac.eventIgnoreNext()', arguments ); 543 544 this.eventIgnored = eventType; 545 }; 546 547 vac.itemPrev = function(){ 548 if ( this.log ) this.log( 'vac.itemPrev()', arguments ); 549 550 var inst = this.inst, 551 container = this.container, 552 itemPrev = container.children( '.focused' ).prevAll( '[item]:first' ); 553 554 if ( itemPrev.size() ) { 555 this.itemScrollTo( itemPrev.attr( 'item' ) ); 556 } 557 else if ( inst.itemIndex > 0 ) { 558 this.itemScrollTo( parseInt( inst.itemIndex, 10 ) - 1 ); 559 } 560 }; 561 562 vac.itemNext = function(){ 563 if ( this.log ) this.log( 'vac.itemNext()', arguments ); 564 565 var inst = this.inst; 566 567 if ( inst.itemIndex < this.container.children( ':not(.hidden)' ).size() - 1 ) { 568 this.itemScrollTo( parseInt( inst.itemIndex, 10 ) + 1 ); 569 } 570 }; 571 572 vac.itemFocus = function( element ){ 573 if ( this.log ) this.log( 'vac.itemFocus()', arguments ); 574 575 element = jQuery( element ); 576 var inst = this.inst, 577 container = this.container; 578 579 this.instCallback( 'onItemFocusBefore' ); 580 581 if ( !this.ios ) { 582 container 583 .children( '.focused' ) 584 .removeClass( 'focused' ); 585 } 586 587 if ( element.is( '[item]' ) ) { 588 inst.itemIndexLast = inst.itemIndex; 589 inst.itemIndex = element.attr( 'item' ) * 1; 590 inst.containerScrollTop = container.scrollTop(); 591 } 592 593 if ( !this.ios ) { 594 element.addClass( 'focused' ); 595 } 596 597 this.instCallback( 'onItemFocusAfter' ); 598 }; 599 600 vac.itemSelect = function(){ 601 if ( this.log ) this.log( 'vac.itemSelect()', arguments ); 602 603 this.instCallback( 'onSelectBefore' ); 604 605 var inst = this.inst; 606 var container = this.container; 607 var item = container.children( '[item="' + inst.itemIndex + '"]' ); 608 609 if ( item.is( '.more' ) ) { 610 this.resultsShowAll(); 611 item = item.next(); 612 this.itemFocus( item ); 613 } 614 else if ( inst.itemIndex === -1 ) { 615 this.instReset(); 616 this.eventIgnoreNext( 'focus' ); 617 this.close(); 618 } 619 else if ( item.is( '[item]' ) ) { 620 this.itemSelectByIndex( item.attr( 'item' ) ); 621 } 622 623 this.instCallback( 'onSelectAfter' ); 624 }; 625 626 vac.itemSelectByIndex = function( itemIndex ){ 627 if ( this.log ) this.log( 'vac.itemSelectByIndex()', arguments ); 628 629 var inst = this.inst; 630 var data = inst.data_cached; 631 632 if ( !data.length ) { 633 data = inst.options.data; 634 } 635 636 inst.itemIndexLast = inst.itemIndex; 637 inst.itemIndex = itemIndex; 638 639 if ( !( itemIndex in data ) ) { 640 return; 641 } 642 643 var dataItem = data[ itemIndex ]; 644 645 inst.itemSelected = itemIndex; 646 inst.item = dataItem 647 648 if ( inst.target && inst.target.length ) { 649 inst.target.val( dataItem[ inst.options.dataValue ] ); 650 651 if (inst.container) { 652 inst.container.children( '.selected' ).removeClass( 'selected' ); 653 inst.container.children( '[item="' + inst.itemIndex + '"]' ).addClass( 'selected' ); 654 inst.containerScrollTop = inst.container.scrollTop(); 655 } 656 657 if (inst.element) { 658 inst.element 659 .data( 'result', dataItem.name ) 660 .val( dataItem.name ) 661 .get( 0 ) 662 .select(); 663 } 664 665 this.eventIgnoreNext( 'focus' ); 666 this.close(); 667 } 668 }; 669 670 vac.itemScrollTo = function( index ){ 671 if ( this.log ) this.log( 'vac.itemScrollTo()', arguments ); 672 673 var inst = this.inst, 674 container = this.container, 675 containerOffset = inst.containerOffset || container.offset(), 676 item = container.children( ':not(.hidden)' ).eq( index ), 677 itemOffset = item.offset(), 678 containerScrollTop = container.scrollTop(), 679 scrollTop = itemOffset.top - ( containerOffset.top - containerScrollTop ), 680 scrollTopItem = container.height() - item.outerHeight(); 681 682 inst.containerOffset = containerOffset; 683 684 this.itemFocus( item ); 685 if ( !inst.options.allowNonSelect ) { 686 } 687 688 // scroll down 689 if ( inst.itemIndexLast < inst.itemIndex && scrollTop > ( containerScrollTop + scrollTopItem ) ) { 690 container.scrollTop( scrollTop - scrollTopItem ); 691 } 692 // scroll up 693 else if ( scrollTop < container.scrollTop() ) { 694 container.scrollTop( scrollTop ); 695 } 696 697 container.show(); 698 }; 699 700 vac.close = function( blur = true ){ 701 if ( this.log ) this.log( 'vac.close()', arguments ); 702 703 this.instCallback( 'onCloseBefore' ); 704 this.inst.containerScrollTop = this.container.scrollTop(); 705 this.container.fadeOut('fast').hide(); // .fadeOut('fast') ie11 fix 706 707 if ( blur && this.inst.element.hasClass('vac_active') ) { 708 this.inst.element.trigger( 'blur' ); 709 } 710 this.instCallback( 'onCloseAfter' ); 711 }; 712 713 vac.search = function(){ 714 if ( this.log ) this.log( 'vac.search()', arguments ); 715 716 var inst = this.inst, 717 element = inst.element, 718 elementResult = ( inst.element.data( 'result' ) || '' ).toLowerCase(),
719 string = ( element.data( 'search' ) || '' ).toLowerCase(); 720 721 if ( elementResult && string == elementResult ) { 722 this.resultsShow(); 723 return; 724 } 725 726 if ( inst.options.dataSource == 'url' ) { 727 var self = this; 728 var url = inst.options.url; 729 var data = this.instCallback( 'onSearchParamsGet', string ); 730 731 jQuery.ajax({ 732 url: url.replace( /value/, string ), 733 dataType: 'json', 734 data: data, 735 success: function (data) { 736 var result = self.instCallback( 'onDataFetch', data ); 737 738 if ( result === false ) { 739 inst.options.data = []; 740 } 741 else { 742 inst.options.data = result || data; 743 } 744 745 inst.data_cached = []; 746 747 self.searchRun(); 748 } 749 }); 750 } 751 else { 752 this.searchRun(); 753 } 754 }; 755 756 vac.searchRun = function(){ 757 var inst = this.inst, 758 element = inst.element, 759 elementResult = ( inst.element.data( 'result' ) || '' ).toLowerCase(), 760 string = ( element.data( 'search' ) || '' ).toLowerCase(); 761 762 var options = inst.options, 763 data = options.data, 764 data_cached = [], 765 dataOrder = string.length; 766 767 if ( inst.data_cached.length && string.substr( 0, inst.string.length ) == inst.string ) { 768 data = inst.data_cached; 769 } 770 771 inst.string = string; 772 773 this.instReset(); 774 this.instCallback( 'onSearchBefore' ); 775 776 for( var i in data ) { 777 var entry = data[i], 778 multiplier = 1, 779 texts = [ entry.name, entry.alias ]; 780 781 texts.push.apply( texts, entry.alternatives ); 782 entry.score = -1; 783 784 if ( !string.length ) { 785 entry.score = entry.priority; 786 } 787 else { 788 for( var j in texts ) { 789 if ( !texts.hasOwnProperty( j ) || typeof texts[j] != 'string' ) { 790 continue; 791 } 792 793 var text = texts[j].toLowerCase(), 794 pos = text.indexOf( string ); 795 796 if ( inst.searchMode == 'beginsWith' && pos != 0 ) continue; 797 if ( pos == -1 ) continue; 798 if ( pos != 0 ) multiplier = 3; 799 if ( text == string ) multiplier = 5; 800 801 var score = ( ( pos + 1 ) * multiplier ) * ( j == 0 ? 1 : 2 ); 802 803 if ( entry.score == -1 || score < entry.score ) { 804 entry.score = score; 805 806 if ( j != 0 ) { 807 entry.match = texts[j]; 808 } 809 } 810 } 811 } 812 813 if ( entry.score != -1 ) { 814 data_cached.push( entry ); 815 } 816 } 817 818 inst.data_cached = data_cached; 819 inst.itemIndexLast = inst.itemIndex; 820 inst.itemIndex = inst.options.allowNonSelect ? -1 : 0; 821 822 this.instCallback( 'onSearchAfter' ); 823 824 if ( inst.data_cached.length ) { 825 if ( dataOrder && options.resultsOrder ) { 826 this.order( 'results' ); 827 } 828 829 this.resultsReload(); 830 } 831 else { 832 this.searchFailed(); 833 } 834 }; 835 836 vac.searchFailed = function(){ 837 if ( this.log ) this.log( 'vac.searchFailed()', arguments ); 838 839 var string = this.inst.options.strings['searchFailed']; 840 841 if ( this.inst.options.resultsEmptyMsg && string ) { 842 this.inst.result = this.container.html( '<div class="row noresult">' + string + '</div>' ); 843 } 844 else { 845 this.close(false); 846 } 847 }; 848 849 vac.resultsReload = function(){ 850 if ( this.log ) this.log( 'vac.resultsReload()', arguments ); 851 852 var rows = [], 853 row = '', 854 inst = this.inst, 855 options = inst.options, 856 data = inst.data_cached.length ? inst.data_cached : jQuery.makeArray( options.data ), 857 pattern = inst.string.length ? eval( '/(' + inst.string + ')/gi' ) : null, 858 length = data.length, 859 resultsMoreDone = false; 860 861 for( var i in data ) { 862 863 if( !data.hasOwnProperty( i ) ) { 864 continue; 865 } 866 867 var className = 'row item', 868 style = ''; 869 870 if ( i % 2 == 0 ) className += ' even'; 871 if ( i >= options.resultsMax ) className += ' hidden'; 872 if ( !inst.options.allowNonSelect && inst.itemSelected == -1 && i == 0 ) { 873 if ( !this.ios ) { 874 className += ' focused'; 875 } 876 } 877 else if ( i == inst.itemSelected ) className += ' selected'; 878 879 row = '<div class="' + className + '" onclick="vac.itemSelect()" onmouseover="vac.itemFocus( this )" item="' + i + '">'; 880 881 if ( options.resultPosition ) { 882 row += '<span class="position">' + options.resultPositionPattern.replace( '$pos', ( +i + 1 ) ) + '</span>'; 883 } 884 885 if ( pattern ) { 886 row += data[i].name.replace( pattern, '<span class="match">$1</span>' ); 887 888 if ( data[i].match && data[i].match.match( pattern ) ) { 889 row += '<span class="alternative">'; 890 row += data[i].match.replace( pattern, '<span class="match">$1</span>' ); 891 row += '</span>'; 892 } 893 } 894 else { 895 row += data[i].name; 896 } 897 898 row += '</div>'; 899 rows.push( row ); 900 901 if ( i == options.resultsMax - 1 ) { 902 if ( options.resultsMore ) { 903 className += ' hidden'; 904 905 if ( !resultsMoreDone ) { 906 resultsMoreDone = true; 907 908 var row = '<div class="row more" onclick="vac.resultsShowAll()" onmouseover="vac.itemFocus( this )" item="' + i + '">'; 909 row += options.resultsMorePattern.replace( '$count', ( length - 1 ) - i ); 910 row += '</div>'; 911 912 rows.push( row ); 913 } 914 } 915 else break; 916 } 917 } 918 919 inst.element.addClass( 'vac_active' ); 920 this.container.html( rows.join('') ).show(); 921 inst.containerScrollTop = 0; 922 this.container.scrollTop(0); 923 inst.result = this.container.children( '.item' ); 924 }; 925 926 vac.resultsShow = function(){ 927 if ( this.log ) this.log( 'vac.resultsShow()', arguments ); 928 929 var inst = this.inst; 930 931 this.instCallback( 'onShowBefore' ); 932 933 inst.element.addClass( 'vac_active' ); 934 935 if ( this.container.children( '[item]' ).length == 0 ) { 936 this.resultsReload(); 937 } 938 939 if ( !inst.options.data || !inst.options.data.length ) { 940 this.searchFailed(); 941 } 942 else if ( false && inst.itemIndex ) { 943 this.itemScrollTo( inst.itemIndex ) 944 } 945 else { 946 this.container.show().scrollTop( inst.containerScrollTop ); 947 } 948 949 this.instCallback( 'onShowAfter' ); 950 }; 951 952 vac.resultsShowAll = function(){ 953 if ( this.log ) this.log( 'vac.resultsShowAll()', arguments ); 954 955 // div.more darf nicht geloescht werden, da wir erst danach pruefen in wie fern wir das click event abfangen muessen 956 this.container 957 .children( '.more' ) 958 .addClass( 'hidden' ) 959 .nextAll() 960 .removeClass( 'hidden' ); 961 }; 962 963 vac.order = function( type, compare ){ 964 if ( this.log ) this.log( 'vac.order()', arguments ); 965 966 if ( type in vacOrder ) { 967 vacOrder[ type ].run( compare ); 968 } 969 }; 970 971 var vacOrder = function( type, data ){ 972 if ( type in vacOrder ) { 973 vacOrder[ type ].run( data ); 974 } 975 }; 976 977 vacOrder._new = function(){ 978 return { 979 data: {}, 980 run: function(){} 981 } 982 }; 983 984 vacOrder.results = new vacOrder._new; 985 vacOrder.results.run = function(){ 986 if ( vac.log ) vac.log( 'vacOrder.results()', arguments ); 987 988 if ( vac.instCallback( 'onOrderBefore' ) !== false ) { 989 vac.inst.data_cached.sort(function(a,b){ 990 var result = a.score - b.score; 991 992 if ( result == 0 ) return 0; 993 if( result > 0 ) return 1; 994 return -1; 995 }); 996 } 997 vac.instCallback( 'onOrderAfter' ); 998 }; 999 1000 vacOrder.priority = new vacOrder._new; 1001 vacOrder.priority.run = function(){ 1002 if ( vac.log ) vac.log( 'vacOrder.priority()', arguments ); 1003 1004 if ( 'data' in vac.inst.options ) { 1005 vac.inst.options.data.sort(function(a,b){ 1006 if ( a.priority == b.priority ) return 0; 1007 if ( a.priority > b.priority ) return -1; 1008 return 1; 1009 }); 1010 } 1011 }; 1012 1013 vacOrder.countries = new vacOrder._new; 1014 vacOrder.countries.run = function( data ){ 1015 if ( vac.log ) vac.log( 'vacOrder.countries()', arguments ); 1016 1017 if ( data ) { 1018 this.data = data; 1019 } 1020 1021 if ( 'country' in this.data && 'geo_country_code_iso_2' in this.data.country ) { 1022 var inst = vac.inst, 1023 options = inst.options, 1024 data = jQuery.extend( {}, options.data ), 1025 dataOrdered = [], 1026 firstItem; 1027
1028 for( var i in data ) { 1029 if ( data[i].alias == this.data.country.geo_country_code_iso_2 ) { 1030 firstItem = data[i]; 1031 } 1032 else { 1033 dataOrdered.push( data[i] ); 1034 } 1035 } 1036 1037 if ( firstItem ) { 1038 dataOrdered.unshift( firstItem ); 1039 } 1040 1041 options.data = dataOrdered; 1042 } 1043 } 1044 1045 jQuery.fn.vac = function( options ) { 1046 this.each(function() { 1047 vac.register( this, options ); 1048 }); 1049 1050 return this; 1051 }; 1052 1053 // damit wir den seitenaufbau nicht unnoetig mit irgendwelchen ajax requests verlangsamen 1054 // fangen wir erst nach dem onload event an die daten zu laden 1055 jQuery(function(){ 1056 vac.timer = setTimeout(function(){ 1057 vac.instsPreInit(); 1058 }, 200 ); 1059 }); 1060 })( typeof vQuery != 'undefined' ? vQuery : jQuery ); 1061} 1062else if ( typeof jQuery !== 'undefined' && typeof vQuery !== 'undefined' && 'vac' in jQuery.fn && !( 'vac' in vQuery.fn ) ){ 1063 vQuery.fn.vac = jQuery.fn.vac; 1064}
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.