PageSourceSearch

https://www.allianztravelinsurance.com/v_40240276061390/system/master/core_js/angucomplete-alt.js

js allianztravelinsurance.com collected 2026-10-02 02:31:54 UTC 35,228 bytes, 996 lines download raw bytes

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

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.