PageSourceSearch

https://rd-agri.fr/app/dependencies/angucomplete-alt.js

js rd-agri.fr collected 2026-10-05 07:29:48 UTC 27,199 bytes, 847 lines download raw bytes

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

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.