1(function (angular) { 2 var app = angular.module('angucomplete', ['ngSanitize']); 3 app.directive('scrolly', ['$timeout', function ($timeout) { 4 return { 5 restrict: 'A', 6 link: function (scope, element, attrs) { 7 element.bind('scroll', function () { 8 $timeout(function () { 9 scope.resLimit += parseInt(scope.displayNumber) ? parseInt(scope.displayNumber) : 10; 10 }, 1500); 11 }); 12 } 13 }; 14 }]); 15 16 app.directive('angucomplete', ['$q', '$parse', '$http', '$sce', '$timeout', '$templateCache', '$interpolate', function ($q, $parse, $http, $sce, $timeout, $templateCache, $interpolate) { 17 // keyboard events 18 var KEY_DW = 40; 19 var KEY_RT = 39; 20 var KEY_UP = 38; 21 var KEY_LF = 37; 22 var KEY_ES = 27; 23 var KEY_EN = 13; 24 var KEY_TAB = 9; 25 26 var MIN_LENGTH = 3; 27 var MAX_LENGTH = 524288; // the default max length per the html maxlength attribute 28 var PAUSE = 500; 29 var BLUR_TIMEOUT = 200; 30 31 // string constants 32 var REQUIRED_CLASS = 'autocomplete-required'; 33 var TEXT_SEARCHING = 'Searching...'; 34 var TEXT_NORESULTS = 'No results found'; 35 var TEMPLATE_URL = '/angucomplete-alt/index.html'; 36 37 // Set the default template for this directive 38 $templateCache.put(TEMPLATE_URL, 39 '<div class="angucomplete-holder" ng-class="{\'angucomplete-dropdown-visible\': showDropdown}" ng-cloak>' + 40 '<input id="{{id}}" name="{{id}}" data-val="{{dataVal}}" tabindex="{{fieldTabindex}}" ng-class="{\'angucomplete-input-not-empty\': notEmpty, \'required\': required}" ng-model="searchStr" ng-disabled="disableInput" type="{{inputType}}" placeholder="{{placeholder}}" maxlength="{{maxlength}}" ng-focus="onFocusHandler()" class="ignore-focusout-validation preventdefault {{inputClass}} autocomplete angucomplete-input" ng-focus="resetHideResults()" ng-blur="hideResults($event)" autocapitalize="off" autocorrect="off" autocomplete="off" ng-change="inputChangeHandler(searchStr)"/>' + 41 ' <div scrolly id="{{id}}_dropdown" class="angucomplete-dropdown panel panel-default" ng-show="showDropdown">' + 42 ' <div class="angucomplete-searching" ng-show="searching" ng-bind="textSearching"></div>' + 43 ' <div class="angucomplete-searching" ng-show="!searching && (!results || results.length == 0)" ng-bind-html="textNoResults"></div>' + 44 ' <div class="angucomplete-row" ng-repeat="result in results | limitTo: resLimit" ng-click="selectResult(result)" ng-mouseenter="hoverRow($index)" ng-class="{\'angucomplete-selected-row\': $index == currentIndex}">' + 45 // ' <div ng-if="imageField" class="angucomplete-image-holder">' + 46 // ' <img ng-if="result.image && result.image != \'\'" ng-src="{{result.image}}" class="angucomplete-image"/>' + 47 // ' <div ng-if="!result.image && result.image != \'\'" class="angucomplete-image-default"></div>' + 48 // ' </div>' + 49 ' <div class="angucomplete-title" ng-if="matchClass" ng-bind-html="result.originalObject.matchedtext || result.originalObject.text"></div>' + 50 // ' <div class="angucomplete-title" ng-if="!matchClass">{{ result.title }}</div>' + 51 // ' <div ng-if="matchClass && result.description && result.description != \'\'" class="angucomplete-description" ng-bind-html="result.description"></div>' + 52 // ' <div ng-if="!matchClass && result.description && result.description != \'\'" class="angucomplete-description">{{result.description}}</div>' + 53 ' </div>' + 54 ' </div>' + 55 '</div>' 56 ); 57 58 function link(scope, elem, attrs, ctrl) { 59 scope.resLimit = parseInt(scope.displayNumber) ? parseInt(scope.displayNumber) : 50; 60 scope.required = elem.hasClass('required'); 61 var inputField = elem.find('input'); 62 var minlength = MIN_LENGTH; 63 var searchTimer = null; 64 var hideTimer; 65 var requiredClassName = REQUIRED_CLASS; 66 var responseFormatter;
67 var validState = null; 68 var httpCanceller = null; 69 var httpCallInProgress = false; 70 var dd = elem[0].querySelector('.angucomplete-dropdown'); 71 var isScrollOn = false; 72 var mousedownOn = null; 73 var unbindInitialValue; 74 var displaySearching; 75 var displayNoResults; 76 if (scope.initValue) { 77 $.each(scope.localData, function (i, v) { 78 if (v.text == scope.initValue) { 79 scope.dataVal = v.path; 80 scope.searchStr = v.text; 81 scope.selectedObject = v; 82 } else { 83 if (v.value == scope.initValue) { 84 scope.dataVal = v.path; 85 scope.searchStr = v.text; 86 scope.selectedObject = v; 87 } 88 } 89 }) 90 } 91 elem.on('mousedown', function (event) { 92 if (event.target.id) { 93 mousedownOn = event.target.id; 94 if (mousedownOn === scope.id + '_dropdown') { 95 document.body.addEventListener('click', clickoutHandlerForDropdown); 96 } 97 } 98 else { 99 mousedownOn = event.target.className; 100 } 101 }); 102
vendor: 17,895 bytes, lines 103-538
103 scope.currentIndex = scope.focusFirst ? 0 : null; 104 scope.searching = false; 105 unbindInitialValue = scope.$watch('initialValue', function (newval) { 106 if (newval) { 107 // remove scope listener 108 unbindInitialValue(); 109 // change input 110 handleInputChange(newval, true); 111 } 112 }); 113 scope.$watch('fieldRequired', function (newval, oldval) { 114 if (newval !== oldval) { 115 if (!newval) { 116 ctrl[scope.inputName].$setValidity(requiredClassName, true); 117 } 118 else if (!validState || scope.currentIndex === -1) { 119 handleRequired(false); 120 } 121 else { 122 handleRequired(true); 123 } 124 } 125 }); 126 127 scope.$on('angucomplete-alt:clearInput', function (event, elementId) { 128 if (!elementId || elementId === scope.id) { 129 scope.searchStr = null; 130 callOrAssign(); 131 handleRequired(false); 132 clearResults(); 133 } 134 }); 135 136 scope.$on('angucomplete-alt:changeInput', function (event, elementId, newval) { 137 if (!!elementId && elementId === scope.id) { 138 handleInputChange(newval); 139 } 140 }); 141 142 function handleInputChange(newval, initial) { 143 if (newval) { 144 if (typeof newval === 'object') { 145 scope.searchStr = extractTitle(newval); 146 callOrAssign({ originalObject: newval }); 147 } else if (typeof newval === 'string' && newval.length > 0) { 148 scope.searchStr = newval; 149 } else { 150 if (console && console.error) { 151 console.error('Tried to set ' + (!!initial ? 'initial' : '') + ' value of angucomplete to', newval, 'which is an invalid value'); 152 } 153 } 154 155 handleRequired(true); 156 } 157 } 158 159 // #194 dropdown list not consistent in collapsing (bug). 160 function clickoutHandlerForDropdown(event) { 161 mousedownOn = null; 162 scope.hideResults(event); 163 document.body.removeEventListener('click', clickoutHandlerForDropdown); 164 } 165 166 // for IE8 quirkiness about event.which 167 function ie8EventNormalizer(event) { 168 return event.which ? event.which : event.keyCode; 169 } 170 171 function callOrAssign(value) { 172 if (typeof scope.selectedObject === 'function') { 173 scope.selectedObject(value, scope.selectedObjectData); 174 } 175 else { 176 scope.selectedObject = value; 177 } 178 179 if (value) { 180 handleRequired(true); 181 } 182 else { 183 handleRequired(false); 184 } 185 } 186 187 function callFunctionOrIdentity(fn) { 188 return function (data) { 189 return scope[fn] ? scope[fn](data) : data; 190 }; 191 } 192 193 function setInputString(str) { 194 callOrAssign({ originalObject: str }); 195 196 if (scope.clearSelected) { 197 scope.searchStr = null; 198 } 199 clearResults(); 200 } 201 202 function extractTitle(data) { 203 // split title fields and run extractValue for each and join with ' ' 204 return scope.titleField.split(',') 205 .map(function (field) { 206 return extractValue(data, field); 207 }) 208 .join(' '); 209 } 210 211 function extractValue(obj, key) { 212 var keys, result; 213 if (key) { 214 keys = key.split('.'); 215 result = obj; 216 for (var i = 0; i < keys.length; i++) { 217 result = result[keys[i]]; 218 } 219 } 220 else { 221 result = obj; 222 } 223 return result; 224 } 225 226 function findMatchString(target, str) { 227 var result, matches, re; 228 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions 229 // Escape user input to be treated as a literal string within a regular expression 230 re = new RegExp(str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i'); 231 if (!target) { return; } 232 if (!target.match || !target.replace) { target = target.toString(); } 233 matches = target.match(re); 234 if (matches) { 235 result = target.replace(re, 236 '<span class="' + scope.matchClass + '">' + matches[0] + '</span>'); 237 } 238 else { 239 result = target; 240 } 241 return $sce.trustAsHtml(result); 242 } 243 244 function handleRequired(valid) { 245 scope.notEmpty = valid; 246 validState = scope.searchStr; 247 if (scope.fieldRequired && ctrl && scope.inputName) { 248 ctrl[scope.inputName].$setValidity(requiredClassName, valid); 249 } 250 } 251 252 function keyupHandler(event) { 253 var which = ie8EventNormalizer(event); 254 if (which === KEY_LF || which === KEY_RT) { 255 // do nothing 256 return; 257 } 258 259 if (which === KEY_UP || which === KEY_EN) { 260 event.preventDefault(); 261 } 262 else if (which === KEY_DW) { 263 if (!scope.searchStr) { 264 scope.showDropdown = true; 265 showAll(); 266 } 267 268 if (!scope.showDropdown && scope.searchStr && scope.searchStr.length >= minlength) { 269 initResults(); 270 scope.searching = true; 271 searchTimerComplete(scope.searchStr); 272 } 273 } 274 else if (which === KEY_ES) { 275 clearResults(); 276 scope.$apply(function () { 277 inputField.val(scope.searchStr); 278 }); 279 } 280 else { 281 if (minlength === 0 && !scope.searchStr) { 282 return; 283 } 284 285 if (!scope.searchStr || scope.searchStr === '') { 286 scope.showDropdown = false; 287 } else if (scope.searchStr.length >= minlength) { 288 initResults(); 289 290 if (searchTimer) { 291 $timeout.cancel(searchTimer); 292 } 293 294 scope.searching = true; 295 296 searchTimer = $timeout(function () { 297 searchTimerComplete(scope.searchStr); 298 }, scope.pause); 299 } 300 301 if (validState && validState !== scope.searchStr && !scope.clearSelected) { 302 scope.$apply(function () { 303 callOrAssign(); 304 }); 305 } 306 } 307 } 308 309 function handleOverrideSuggestions(event) { 310 if (scope.overrideSuggestions && 311 !(scope.selectedObject && scope.selectedObject.originalObject === scope.searchStr)) { 312 if (event) { 313 event.preventDefault(); 314 event.stopPropagation(); 315 } 316 317 // cancel search timer 318 $timeout.cancel(searchTimer); 319 // cancel http request 320 cancelHttpRequest(); 321 //Check if we have a match on the first search result to keep it or clear 322 if ((scope.results[0] ? scope.results[0].title : '') != scope.searchStr) 323 scope.searchStr = ''; 324 setInputString(scope.searchStr); 325 } 326 } 327 328 function dropdownRowOffsetHeight(row) { 329 var css = getComputedStyle(row); 330 return row.offsetHeight + 331 parseInt(css.marginTop, 10) + parseInt(css.marginBottom, 10); 332 } 333 334 function dropdownHeight() { 335 return dd.getBoundingClientRect().top + 336 parseInt(getComputedStyle(dd).maxHeight, 10); 337 } 338 339 function dropdownRow() { 340 return elem[0].querySelectorAll('.angucomplete-row')[scope.currentIndex]; 341 } 342 343 function dropdownRowTop() { 344 return dropdownRow().getBoundingClientRect().top - 345 (dd.getBoundingClientRect().top + 346 parseInt(getComputedStyle(dd).paddingTop, 10)); 347 } 348 349 function dropdownScrollTopTo(offset) { 350 dd.scrollTop = dd.scrollTop + offset; 351 } 352 353 function updateInputField() { 354 var current = scope.results[scope.currentIndex]; 355 if (scope.matchClass) { 356 inputField.val(extractTitle(current.originalObject)); 357 } 358 else { 359 inputField.val(current.title); 360 } 361 } 362 363 function keydownHandler(event) { 364 event.stopPropagation(); 365 var which = ie8EventNormalizer(event); 366 var row = null; 367 var rowTop = null; 368 369 if (which === KEY_EN && scope.results) { 370 if (scope.currentIndex >= 0 && scope.currentIndex < scope.results.length) { 371 event.preventDefault(); 372 event.stopPropagation(); 373 scope.selectResult(scope.results[scope.currentIndex]); 374 } else { 375 handleOverrideSuggestions(event); 376 clearResults(); 377 } 378 scope.$apply(); 379 } else if (which === KEY_DW && scope.results) { 380 event.preventDefault(); 381 if ((scope.currentIndex + 1) < scope.results.length && scope.showDropdown) { 382 scope.$apply(function () { 383 scope.currentIndex++; 384 //Add more items if it is the last 385 if (scope.currentIndex == scope.resLimit - 1) 386 $timeout(function () { 387 scope.resLimit += parseInt(scope.displayNumber) ? parseInt(scope.displayNumber) : 10; 388 }, 1500); 389 updateInputField(); 390 }); 391 392 if (isScrollOn) { 393 row = dropdownRow(); 394 if (dropdownHeight() < row.getBoundingClientRect().bottom) { 395 dropdownScrollTopTo(dropdownRowOffsetHeight(row)); 396 } 397 } 398 } 399 } else if (which === KEY_UP && scope.results) { 400 event.preventDefault(); 401 if (scope.currentIndex >= 1) { 402 scope.$apply(function () { 403 scope.currentIndex--; 404 updateInputField(); 405 }); 406 407 if (isScrollOn) { 408 rowTop = dropdownRowTop(); 409 if (rowTop < 0) { 410 dropdownScrollTopTo(rowTop - 1); 411 } 412 } 413 } 414 else if (scope.currentIndex === 0) { 415 scope.$apply(function () { 416 scope.currentIndex = -1; 417 inputField.val(scope.searchStr); 418 }); 419 } 420 } else if (which === KEY_TAB) { 421 if (scope.results && scope.results.length > 0 && scope.showDropdown) { 422 if (scope.currentIndex === -1 && scope.overrideSuggestions) { 423 // intentionally not sending event so that it does not 424 // prevent default tab behavior 425 handleOverrideSuggestions(); 426 } 427 else { 428 if (scope.currentIndex === -1) { 429 scope.currentIndex = 0; 430 } 431 scope.selectResult(scope.results[scope.currentIndex]); 432 scope.$digest(); 433 } 434 } 435 else { 436 // no results 437 // intentionally not sending event so that it does not 438 // prevent default tab behavior 439 if (scope.searchStr && scope.searchStr.length > 0) { 440 handleOverrideSuggestions(); 441 } 442 } 443 } else if (which === KEY_ES) { 444 // This is very specific to IE10/11 #272 445 // without this, IE clears the input text 446 event.preventDefault(); 447 } 448 } 449 450 function httpSuccessCallbackGen(str) { 451 return function (responseData, status, headers, config) { 452 // normalize return obejct from promise 453 if (!status && !headers && !config && responseData.data) { 454 responseData = responseData.data; 455 } 456 scope.searching = false; 457 processResults( 458 extractValue(responseFormatter(responseData), scope.remoteUrlDataField), 459 str); 460 }; 461 } 462 463 function httpErrorCallback(errorRes, status, headers, config) { 464 scope.searching = httpCallInProgress; 465 466 // normalize return obejct from promise 467 if (!status && !headers && !config) { 468 status = errorRes.status; 469 } 470 471 // cancelled/aborted 472 if (status === 0 || status === -1) { return; } 473 if (scope.remoteUrlErrorCallback) { 474 scope.remoteUrlErrorCallback(errorRes, status, headers, config); 475 } 476 else { 477 if (console && console.error) { 478 console.error('http error'); 479 } 480 } 481 } 482 483 function cancelHttpRequest() { 484 if (httpCanceller) { 485 httpCanceller.resolve(); 486 } 487 } 488 489 function getRemoteResults(str) { 490 var params = {}, 491 url = scope.remoteUrl + encodeURIComponent(str); 492 if (scope.remoteUrlRequestFormatter) { 493 params = { params: scope.remoteUrlRequestFormatter(str) }; 494 url = scope.remoteUrl; 495 } 496 if (!!scope.remoteUrlRequestWithCredentials) { 497 params.withCredentials = true; 498 } 499 cancelHttpRequest(); 500 httpCanceller = $q.defer(); 501 params.timeout = httpCanceller.promise; 502 httpCallInProgress = true; 503 $http.get(url, params) 504 .success(httpSuccessCallbackGen(str)) 505 .error(httpErrorCallback) 506 .finally(function () { httpCallInProgress = false; }); 507 } 508 509 function getRemoteResultsWithCustomHandler(str) { 510 cancelHttpRequest(); 511 512 httpCanceller = $q.defer(); 513 514 scope.remoteApiHandler(str, httpCanceller.promise) 515 .then(httpSuccessCallbackGen(str)) 516 .catch(httpErrorCallback); 517 518 /* IE8 compatible 519 scope.remoteApiHandler(str, httpCanceller.promise) 520 ['then'](httpSuccessCallbackGen(str)) 521 ['catch'](httpErrorCallback); 522 */ 523 } 524 525 function clearResults() { 526 scope.showDropdown = false; 527 scope.results = []; 528 if (dd) { 529 dd.scrollTop = 0; 530 } 531 if (!scope.dataVal || !scope.selectedObject) { 532 scope.searchStr = ""; 533 scope.dataVal = ""; 534 } 535 } 536 537 function initResults() { 538 scope.showDropdown = displaySearching;
539 scope.currentIndex = scope.focusFirst ? 0 : -1; 540 scope.results = []; 541 } 542 543 function getLocalResults(str) { 544 var i, match, s, value, 545 searchFields = scope.searchFields.split(','), 546 matches = []; 547 if (typeof scope.parseInput() !== 'undefined') { 548 str = scope.parseInput()(str); 549 } 550 for (i = 0; i < scope.localData.length; i++) { 551 match = false; 552 553 for (s = 0; s < searchFields.length; s++) { 554 value = extractValue(scope.localData[i], searchFields[s]) || ''; 555 match = match || (value.toString().toLowerCase().indexOf(str.toString().toLowerCase()) >= 0); 556 } 557 558 if (match) { 559 matches[matches.length] = scope.localData[i]; 560 } 561 } 562 return matches; 563 } 564 565 function checkExactMatch(result, obj, str) { 566 if (!str) { return false; } 567 for (var key in obj) { 568 if (obj[key].toLowerCase() === str.toLowerCase()) { 569 scope.selectResult(result); 570 return true; 571 } 572 } 573 return false; 574 } 575 576 function searchTimerComplete(str) { 577 // Begin the search 578 if (!str || str.length < minlength) { 579 return; 580 } 581 if (scope.localData) { 582 scope.$apply(function () { 583 var matches; 584 if (typeof scope.localSearch() !== 'undefined') { 585 matches = scope.localSearch()(str, scope.localData); 586 } else { 587 matches = getLocalResults(str); 588 } 589 scope.searching = false; 590 processResults(matches, str); 591 }); 592 } 593 else if (scope.remoteApiHandler) { 594 getRemoteResultsWithCustomHandler(str); 595 } else { 596 getRemoteResults(str); 597 } 598 } 599 600 function processResults(responseData, str) { 601 var i, description, image, text, formattedText, formattedDesc; 602 603 if (responseData && responseData.length > 0) { 604 scope.results = []; 605 606 for (i = 0; i < responseData.length; i++) { 607 responseData[i].initialValue ? (responseData[i].value = responseData[i].initialValue) : (responseData[i].initialValue = responseData[i].value); 608 responseData[i].title ? (responseData[i].title = responseData[i].text) : (responseData[i].title = responseData[i].text); 609 if (scope.titleField && scope.titleField !== '') { 610 text = formattedText = extractTitle(responseData[i]); 611 } 612 613 description = ''; 614 if (scope.descriptionField) { 615 description = formattedDesc = extractValue(responseData[i], scope.descriptionField); 616 } 617 618 image = ''; 619 if (scope.imageField) { 620 image = extractValue(responseData[i], scope.imageField); 621 } 622 623 if (scope.matchClass) { 624 formattedText = findMatchString(text, str); 625 //formattedDesc = findMatchString(description, str); 626 } 627 responseData[i].matchedtext = formattedText; 628 scope.results[scope.results.length] = { 629 title: responseData[i].title, 630 description: formattedDesc, 631 originalObject: responseData[i], 632 value: responseData[i].initialValue 633 }; 634 } 635 636 } else { 637 scope.results = []; 638 } 639 640 if (scope.autoMatch && scope.results.length === 1 && 641 checkExactMatch(scope.results[0], 642 { title: text, desc: description || '' }, scope.searchStr)) { 643 scope.showDropdown = false; 644 } else if (scope.results.length === 0 && !displayNoResults) { 645 scope.showDropdown = false; 646 } else { 647 scope.showDropdown = true; 648 //In case of searching .... wait and don't crash 649 if (!scope.results) { 650 scope.results = { 'length': 0 }; 651 } 652 //Calculate height of dropdown 653 var getHeight = setInterval(function () { 654 if ((scope.results.length == 0 ? 1 : scope.results.length) < scope.displayNumber && $('#' + scope.id + "_dropdown").children().last(
654) != null) { 655 $('#' + scope.id + '_dropdown').css('height', ($('#' + scope.id + "_dropdown").children().last().outerHeight() * parseInt((scope.results.length == 0 ? 1 : scope.results.length))) + 'px'); 656 window.clearInterval(getHeight); 657 } else { 658 $('#' + scope.id + '_dropdown').css('height', ($('#' + scope.id + "_dropdown").children().last().outerHeight() * parseInt(scope.displayNumber) - 20) + 'px'); 659 window.clearInterval(getHeight); 660 } 661 }, 50); 662 663 } 664 } 665 666 function showAll() { 667 if (scope.localData) { 668 scope.searching = false; 669 processResults(scope.localData, ''); 670 } 671 else if (scope.remoteApiHandler) { 672 scope.searching = true; 673 getRemoteResultsWithCustomHandler(''); 674 } 675 else { 676 scope.searching = true; 677 getRemoteResults(''); 678 } 679 } 680 681 scope.onFocusHandler = function () { 682 scope.resLimit = parseInt(scope.displayNumber) ? parseInt(scope.displayNumber) : 50; 683 if (scope.focusIn) { 684 scope.focusIn(); 685 scope.showDropdown = false; 686 //Option to display dropdown only for when you start typing 687 if (scope.disableDropdownOnClick != 'True') 688 showAll(); 689 } 690 691 if (minlength === 0 && (!scope.searchStr || scope.searchStr.length === 0)) {
692 scope.currentIndex = scope.focusFirst ? 0 : scope.currentIndex; 693 scope.showDropdown = true; 694 showAll(); 695 } 696 }; 697 698 scope.hideResults = function () { 699 if (mousedownOn && 700 (mousedownOn === scope.matchClass || 701 mousedownOn === scope.id + '_dropdown' || 702 mousedownOn.indexOf('angucomplete') >= 0)) { 703 mousedownOn = null; 704 } 705 else { 706 clearResults(); 707 hideTimer = $timeout(function () { 708 scope.$apply(function () { 709 if (scope.searchStr && scope.searchStr.length > 0) { 710 inputField.val(scope.searchStr); 711 } 712 }); 713 }, BLUR_TIMEOUT); 714 cancelHttpRequest(); 715 716 if (scope.focusOut) { 717 scope.focusOut(); 718 } 719 720 scope.overrideSuggestions = true; 721 if (scope.overrideSuggestions) { 722 if (scope.searchStr && scope.searchStr.length > 0 && scope.currentIndex === -1) { 723 handleOverrideSuggestions(); 724 } 725 } 726 } 727 }; 728 729 scope.resetHideResults = function () { 730 if (hideTimer) { 731 $timeout.cancel(hideTimer); 732 } 733 }; 734 735 scope.hoverRow = function (index) { 736 scope.currentIndex = index; 737 }; 738 739 scope.selectResult = function (result) { 740 if (result.value == scope.ngModel && scope.searchStr == result.title) 741 return; 742 scope.updateField({ field: attrs.afField, val: result.value }); 743 // Restore original values 744 // if (scope.matchClass) { 745 // result.title = extractTitle(result.originalObject); 746 // result.description = extractValue(result.originalObject, scope.descriptionField); 747 // } 748 scope.showDropdown = false; 749 if (scope.clearSelected) { 750 scope.searchStr = null; 751 } 752 else { 753 scope.dataVal = result.originalObject.path; 754 scope.searchStr = result.title; 755 } 756 callOrAssign(result); 757 clearResults(); 758 }; 759 760 scope.inputChangeHandler = function (str) { 761 if (!str.length) { 762 scope.dataVal = ""; 763 scope.updateField({ field: attrs.afField, val: "" }); 764 } 765 if (str.length < minlength) { 766 cancelHttpRequest(); 767 clearResults(); 768 } 769 else if (str.length === 0 && minlength === 0) { 770 showAll(); 771 } 772 773 if (scope.inputChanged) { 774 str = scope.inputChanged(str); 775 } 776 return str; 777 }; 778 779 // check required 780 if (scope.fieldRequiredClass && scope.fieldRequiredClass !== '') { 781 requiredClassName = scope.fieldRequiredClass; 782 } 783 784 // check min length 785 if (scope.minlength && scope.minlength !== '') { 786 minlength = parseInt(scope.minlength, 10); 787 } 788 789 // check pause time 790 if (!scope.pause) { 791 scope.pause = PAUSE; 792 } 793 794 // check clearSelected 795 if (!scope.clearSelected) { 796 scope.clearSelected = false; 797 } 798 799 // check override suggestions 800 if (!scope.overrideSuggestions) { 801 scope.overrideSuggestions = false; 802 } 803 804 // check required field 805 if (scope.fieldRequired && ctrl) { 806 // check initial value, if given, set validitity to true 807 if (scope.initialValue) { 808 handleRequired(true); 809 } 810 else { 811 handleRequired(false); 812 } 813 } 814 815 scope.inputType = attrs.type ? attrs.type : 'text'; 816 817 // set strings for "Searching..." and "No results" 818 scope.textSearching = attrs.textSearching ? attrs.textSearching : TEXT_SEARCHING; 819 scope.textNoResults = attrs.textNoResults ? $sce.trustAsHtml(attrs.textNoResults) : TEXT_NORESULTS; 820 displaySearching = scope.textSearching === 'false' ? false : true; 821 displayNoResults = scope.textNoResults === 'false' ? false : true; 822 823 // set max length (default to maxlength deault from html 824 scope.maxlength = attrs.maxlength ? attrs.maxlength : MAX_LENGTH; 825 826 // register events 827 inputField.on('keydown', keydownHandler); 828 inputField.on('keyup', keyupHandler); 829 830 // set response formatter 831 responseFormatter = callFunctionOrIdentity('remoteUrlResponseFormatter'); 832 833 // set isScrollOn 834 $timeout(function () { 835 var css = getComputedStyle(dd); 836 isScrollOn = css.maxHeight && css.overflowY === 'auto'; 837 }); 838 839 scope.$watch('ngModel', function (newVal, oldVal) { 840 if (newVal != oldVal) { 841 var result = _.find(scope.localData, function (result, index) { return result.value === newVal }); 842 if (!result) { 843 return; 844 } 845 var selectableResult = { 846 originalObject: result, 847 value: result.value, 848 title: result.text 849 } 850 scope.selectResult(selectableResult); 851 } 852 }); 853 } 854 855 return { 856 restrict: 'EA', 857 require: '^?form', 858 scope: { 859 // selectedObject: '=', 860 selectedObjectData: '=', 861 disableInput: '=', 862 initialValue: '=', 863 initValue: '=', 864 disabled: '=', 865 displayNumber: '=?', 866 disableDropdownOnClick: '=?', 867 localData: '=', 868 localSearch: '&', 869 remoteUrlRequestFormatter: '=', 870 remoteUrlRequestWithCredentials: '@', 871 remoteUrlResponseFormatter: '=', 872 remoteUrlErrorCallback: '=', 873 remoteApiHandler: '=', 874 id: '@', 875 type: '@', 876 placeholder: '@', 877 textSearching: '@', 878 textNoResults: '@', 879 remoteUrl: '@', 880 remoteUrlDataField: '@', 881 titleField: '@', 882 descriptionField: '@', 883 imageField: '@', 884 inputClass: '@', 885 pause: '@', 886 searchFields: '@', 887 minlength: '@', 888 matchClass: '@', 889 clearSelected: '@', 890 overrideSuggestions: '@', 891 fieldRequired: '=', 892 fieldRequiredClass: '@', 893 inputChanged: '=', 894 autoMatch: '@', 895 focusOut: '&', 896 focusIn: '&', 897 fieldTabindex: '@', 898 inputName: '@', 899 focusFirst: '@', 900 parseInput: '&', 901 updateField: '&', 902 ngModel: '=' 903 }, 904 templateUrl: function (element, attrs) {
905 return attrs.templateUrl || TEMPLATE_URL; 906 }, 907 compile: function (tElement) { 908 var startSym = $interpolate.startSymbol(); 909 var endSym = $interpolate.endSymbol(); 910 if (!(startSym === '{{' && endSym === '}}')) { 911 var interpolatedHtml = tElement.html() 912 .replace(/\{\{/g, startSym) 913 .replace(/\}\}/g, endSym); 914 tElement.html(interpolatedHtml); 915 } 916 return link; 917 } 918 }; 919 }]); 920})(window.dnnsfAngular15 || window.angular);
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.